Use "constraints" instead of argument validation for int and double etc
I will still continue with the argument validation track in this post also, I think it's an interesting topic.
If you haven't read my earlier posts about this topic, take a look at the following posts on my blog:
http://weblogs.asp.net/fredriknormen/archive/2008/05/07/lazy-developers.aspx
http://weblogs.asp.net/fredriknormen/archive/2008/05/08/how-to-validate-a-method-s-arguments.aspx
This time I will focus on argument of type int and double etc. Lets assume we have a method which should be used to set a price on a product:
public class Product { public void SetPrice(double price) { price.RequireArgument("price") .GreaterOrEqualTo(0); //.. } }
Note: I will use my friend Roger Alsing cool argument validation solution in this example you can read more about it on his blog.
I use a Set method instead of a property in this example.
I use a validation here to make sure price is not lesser then 0. Here is another method that takes three argument, year, month and day as integers:
public void SetDate(int year, int month, int day) { year.RequireArgument("year") .InRange(1900, 3000); month.RequireArgument("month") .InRange(1, 12); day.RequireArgument("day") .InRange(1, 31); //... }
Note: The Day argument here is special, as we know there is not always 31 days in each month, so another kind of validation is needed, but I make this code only simple for this post.
Now lets change this code and make sure the int and double is not an argument that can take a number larger and lesser than the validation. What we do is to create a Price, Year, Month and a Day class and use them as an argument instead of double and int.
public class Price { private double _price; public Price(double price) { price.RequireArgument("price") .GreaterOrEqualTo(0); this._price = price; } //... } public class Year { private int _year; public Year(int year) { year.RequireArgument("year") .InRange(1900, 3000); } //... } public class Month { private int _month; public Month(int month) { month.RequireArgument("month") .InRange(1, 12); } } public class Product { public void SetPrice(Price price) { //.. } } public void SetDate(Year year, Month month, Day day) { //... }
Note: I skip the Day struct because of not adding to much code, you still get the basic ideas from the other structs.
As you see now the method takes a Price, Year, Month and a Day object, not a double or int and it will directly add a constraint. The user of the method can't pass in a invalid value. I also moved the validation to the classes instead. The Price class could be replaced with the Money pattern Fowler mention in his book if we want it to handle Money.
What do you think, is this something you use today?