Can the use of Extension methods break the Law of Demeter?
To make an easy description of Law of Demeter we can summarize it to the following sentence:
“In particular, an object should avoid invoking methods of a member object returned by another method.”
What does this has to do with Extension methods? It depends on how it’s used. For example we have several Extension Methods for the IEnumerable interface, like Where and OrderBy. By using those methods we can easy select data out from lists, for example (I’m so worthless when it comes to give methods good name, and the following method is so stupid, but it’s only an example.):
public IList<int> GetNumbersGreaterThanTwo(IList<int> numbers) { return numbers .Where(n=> n > 2) .OrderBy(n => n); }
If this code should not break the law of Demeter, it will look like this:
public IList<int> GetNumbersGreaterThanTwo(IList<int> numbers) { var result = numbers.Where(n=> n>2); return result.OrderBy(n => n); }
But that is not true, regarding to the book The Pragmatic Programmer, we don’t “own” result and it wasn’t passed to us, so we can’t use OrderBy. So we break the law. How about using LINQ instead?
public IList<int> GetNumbersGreaterThanTwo(IList<int> numbers) { return (from n in numbers where n > 2 select n).ToList(); }
Will it break it?
If we have the following Extension method:
public static class StringExtension { public static int ToInt(this string value) { if (string.IsNullOrEmpty(value)) return 0; return Int32.Parse(value); } }
And use it in like this:
int number = 45.ToString().ToInt();
Yes, I know, why make an Int a string and then back to an Int, well I’m so bad when it comes to write good examples too ;)
Will the code break the Law of Demeter?
If I have understood how Extension method works, it will not. Why? Well it’s because an Extension method is only a compiler trick, the compiler will change the above code into something like this (Please correct me if I’m totally wrong):
string temp = 45.ToString(); int number = StringExtension.ToInt(temp); public static class StringExtension { public static int ToInt(string value) { if (string.IsNullOrEmpty(value)) return 0; return Int32.Parse(value); } }
Should we even care about this Law at all? Well is some cases, and in some not. I don’t think we should slavery follow it. I try to avoid larger response sets, but I will still write my code as the first example in my post where I use Where and OrderBy, I will not split it down and create extra wrapper methods to avoid breaking the Law. The reason I will break the law in that case is because I think it’s more readable and easy to understand. By using wrapper methods etc for that example, only make the code smell
Here are some examples I try to avoid, only to give you at least some examples:
ds.Tables[0].Rows[10].Cols[5].ToString(); customerRepository.GetCustomerById(10).FirstName myPlaceHolder.FindControl("TextBox").Text = "text"; myPlaceHolder.FindControl("myPlacehodler2").FindControl("Button").Text = "My Button";
Regarding to the book The Pragmatic Programmer, there was studies which shown that classes in C++ with larger response sets are more prone to error than classes with smaller response sets.
“System with many unnecessary dependencies are very hard (and expensive) to maintain, and tend to be highly unstable. In order to keep the dependencies to a minimum, we’ll use the Law of Demeter to design our methods and functions.” – The Pragmatic Programmer page. 140