Breaking up Fat Models With Delegation
Hope everyone had a relaxing Christmas with loads of great food, booze and gossip. I know I did!
So not being a classically trained CS guy, I have no idea what the name for this pattern is, but I find myself using it more and more to break up fat models. It basically involves a lot of explicit delegation in the class, but no decorator classes. I’m still wondering whether this pattern is a “good thing” or not, so I’d appreciate comments.
Use case - your model has been collecting a lot of crufty methods that don’t have anything, really, to do with the logic proper of the model. Classic examples are methods like full_name
, which basically only deal with presentation or some orthagonal logic like currency conversion and so on. There have been several approaches to fixing the presentation issue, the most notable of which has been Draper, but I didn’t enjoy using Draper. For one, I don’t want to call UserDecorator.decorate(User.get(params[:id]))
. Why? I just don’t ok?! Just kidding - actually in my experience, decorating large arrays (think CSV dump for the last months data) takes f.o.r.e.v.e.r and damned if I didn’t get some subtle bugs with DataMapper associations on the decorated class. I didn’t dig too deep, being a shallow and easily influenced guy, and instead started looking for other solutions. I present mine below
So there are several funky things about this approach.
- First of all, it is totally explicit. There is absolutely zero magic going on here.
- Secondly, because of the injected dependency, we can choose the class we would like to use to present our object at runtime.
- Thirdly, it saves us from having to explicitly decorate our objects.
- Fourthly, the ViewDelegate object gets access to all the original objects methods using
method_missing
. This means that the implicitself
in the ViewDelegate class is the original object for all practical purposes. Thus, any object that responds to the required methods can use this Delegate, not just a User. - Fifthly, the delegate objects are not instantiated until one of the delegated methods are called. This might have performance implications. Of course, the ViewDelegate can be memoized as well.
- Lastly, this can be used for any type of delegation, not just for presentation. For example, one might choose to delegate a
height
method to aMetricUnitDelegate
or to anImperialUnitDelegate
, depending on the context.
I have no idea if this is a good or even an original approach. Would love to hear from you in the comments.