The Right Design Makes Your Job Easier, Not Harder

21 March 2015

In the last post, we examined the decorator, chain of responsibility, and composite patterns against a particular refactoring. As proof that you learn more from teaching than you ever do as a student, in teaching one my colleagues yesterday on this material, I had a bit of a realization. I’m sure others have had it before me, but it really struck me so I feel compelled to capture those thoughts.

After we ended up with implementations of the same interface, but in each of the three patterns, I concluded by saying the composite seemed an improper choice but picking between the chain and decorator was largely preferential. That turns out to be wrong. In the presented situation, the decorator is clearly the better choice than the chain and this post will explain why.

From the original problem, we need to retrieve a Registration from the database, update one field on it based on calling a CRM service, and then return the single, complete Registration. Therein lies the key: the bulk of the data is coming from the database. We are adding to it exactly 1 field. Which pattern seems to fit that more closely?

In our decorator implementation, our inner component returns its Registration from the database. Our decorator first calls the inner component, then simply updates the single field it cares about to the value it pulls from the CRM. In the chain implementation, we alread have to do things a little different than your typical chain. Instead of performing our processing and then calling the next component, we call next first, then add on top of it. This is necessary because our interface does not take a Registration as a parameter so the only way to access the other component’s instance is as a return value. This led us to a case where we created a third component to seed the chain by simply creating a new Registration to return. But let’s think about that for a minute.

If GetRegistrationFromDatabase receives a blank Registration from its linked component, we have two options. First, after we retrieve the Registration from the database, we can write a method to copy over the properties one by one (presumably using Reflection so it’s more dynamic and lowers chance a field is missed if it’s added later). But that’s a lot of code we didn’t have to write before. Thus option 2 is to ignore the value we’re given and just return the Registration pulled from the database. But if we do that, we no longer need the seed component at all. Once it’s removed, our GetRegistrationFromDatabase component is now essentially the terminal end of the chain: it starts the ball rolling since we want to use its Registration and build on top of it. Well that’s essentially a decorator under another name!

So that leads me to my realization and the point of this post. When we arrived at the correct design pattern, the work we had to do was very easy. In fact, the first implementation we started with where we simply put our CRM service call into the original method is essentially identical to our decorator, the logic is just split up across the two components. But when we picked the composite and the chain, we instantly had to grapple with a new issue (how do we get Registration into the chain) and code we didn’t have to write previously (copying/merging Registrations together). As soon as we find the problem getting harder instead of easier during a refactoring, we should presume our design or pattern selection may be faulty and we should reconsider.

Sometimes I don’t quite know the best way to take a design for a problem and even tests don’t help me dislodge the rut. I don’t know where I read this, but in those cases I will simply pick a pattern and try to solve the problem using it. In pretty short order I can see if things are getting better or worse and use that as feedback to inform my design. I posit that no one really solves hard problems anyway. Instead, we break it into smaller, easier problems and solve those instead. The ability to decompose such a problem requires a lot of skill and experience, but fundamentally that’s the nature of a developer’s job. Patterns are a great tool to have as it builds on the experience of those before us (most or all of whom were far smarter and more skilled than I), but only if you pick the right one. Use the problem itself in comparison to the pattern as a guide. If it’s not getting easier, either break it down more or consider whether you’ve picked the right one.