Being a bit of a realist (;)), I find it much easier to find problems with my design ideas than I do finding designs I’m really happy with. I am always cognisant of this and make sure to balance the needs of doing a good job and getting the job done. One technique I find useful for this is thinking about the minimum amount of code required.
Whenever I find myself evaluating different design patterns or counting responsibilities to work out where the feature should go or which areas I should refactor, I stop and work out the minimum amount of code I’d need to write to solve the problem, ignoring design considerations. Once I’ve starting thinking about the problem in that light, working out where to put that code becomes a bit easier. If the code violates my sense of design aesthetics, it could just be because it is a messy problem, and no matter how hard I try to abstract or design my way out, the fundamental messiness still remains.
As an example, say we’re working with a library that misuses exceptions, throwing for genuine exceptional cases, and also as a way to notify callers of a change in mode. I might find myself getting grand ideas like anti-corruption layers, Command patterns with reused error handling, or trying various continuation ideas. If there’s a lot of existing code I might start getting bogged down in working out all the classes I’m going to have to change to fit in with this new error handling infrastructure. These kind of premature generalisations are a sure sign I need to stop and simplify.
If we think about the problem for the moment, at a minimum we’re going to need a
try .. catch somewhere, and chances are we’re going to have to do something disgusting like swallow the irrelevant exceptions. We’re now in a position to think about exactly where this responsibility should go based on our standard design rules. In this case, it may be best to deal with this immediately at the boundary between our code and the library, rather than creating a more formal abstraction for handling them (or not, the point is that we know the code and minimum mess required, so we are in a better position to work out where to put it).
TDD can be a useful way to do a similar thing; writing a test that uncovers the minimum increment of code required. Sometimes though it’s not clear from what class or in which direction you should drive the code, in which case I’ve found thinking in terms of the minimum amount of code required then spiking some options around that a helpful alternative.
So next time you’re pondering whether to toggle a UI element’s visibility using MVVM or an MVP-variant for UI separation, or whether to push the conditional in to a Strategy implementation, just remember that somewhere along the line an evil
if statement or similar is going to have to execute and switch the element’s visibility switched from “visible” to “hidden”.