it, but I wanted to write a little bit about how I stabilize the code I'm writing, cause there are a few tricks that are convenient. The following represent refactoring techniques that can increase encapsulation, and decrease the number of times you have to change a given piece of code. Use wisely.
- Argument lists --> Class
This first refactoring involves taking a long argument list for a function and wrapping the list in a class. A long argument list implies (to me) that there is some entire concept stored in that list, and that it would be better represented by some class with an appropriate name. Furthermore, if this is a public function that may be consumed by a customer, it would be very wise to do this so that if they have overwritten the function, when you change the class representing the argument list, they will not likely have a terrible break in their implementations of that class or interface. - Generic lists --> Class
The second refactoring takes long strings of generics and turns them into a single generic. For example:
Func<Father, Mother, IEnumerable<Children>, IEnumerable<KeyValuePair<Food, Money>>, Cost> PurchaseFoodStuffs;
Could be rewritten as:
Func<Family, GroceryStore, Cost> PurchaseFoodStuffs;
This Func takes a family to the grocery store and returns the cost of the food purchased. From the previous iteration, you would have no idea that that's what it's trying to do, however. You can imagine if you had to buy food for special occasions and what not that the argument list would get longer and longer and longer, but with the second one, if you're (say) adding an aunt and uncle that needs to be accounted for, you just add that to your Family class and you're almost done. There may need to be some more tinkering, but at least you don't have to go change a long generics list to match it all over the place. This also has the advantage that if you publish the above Func to a customer, you can change the underlying classes more easily than the argument list, and expect fewer breaks in customer code, similar to the first refactoring. - Func/Action --> Class
Are you noticing a trend? I prefer not to use Func/Action in my code unless it's particularly useful or necessary. I've found over time that using a naked Func or Action does not seem to really indicate the intent of a given piece of code very well. This is just my experience with code in the real world, but may not represent others' experiences. I just find that whenever I have a Func or Action that is even moderately complicated, I would have preferred to pass much of the data into a constructor and acted upon that data however I wanted, and passed around a class that can have functionality be overwritten, rather than a naked Func or Action which will likely require that you add a new condition in an if-statement. A good trick is to encapsulate the functionality into an actual class, and then make a derivative of that class that accepts a Func/Action into its constructor. This will give you the ability to rely purely on the class interface, while still being able to write simple code within a Func/Action, and you will not be coding yourself into a corner if the class functionality needs to change in the future, but the Func/Action code does not (necessarily) have to.
A good example of this in the real world is simply the ICommand interface and DelegateCommand<T> class in the WPF Commanding stack: It does a little bit of work for you, but allows you to either completely write a new implementation by implementing ICommand, or pass a delegate into the constructor and let the DelegateCommand do the majority of the heavy lifting.
I hope that these are useful to you in some way, even if the above seem very simple to the vast majority of you.
No comments:
Post a Comment