Seriously, aside from perhaps Builder, the dreaded Singleton, Model-View-Controller or its hipster cousin Model-View-ViewModel, when was the last time you saw one of the Gang Of Four's patterns used in a new project? Even the direct use of an Iterator is borderline bad-practice nowadays!
I'm thinking that in these days of maximal code-avoidance (and these are great days - less code is always better code in my opinion), the just amount of overhead required to implement most of these patterns is a big turn-off. It's not quite "boilerplate", that word that implies so much burden these days, but it is definitely Not Fun to churn out all those interfaces and abstract classes that do very little aside from give you that apparently-vital level of indirection which so often ends up being nothing more than a level of annoyance.
But I'm in no doubt that a new generation of post-Patterns design patterns have started to arrive, as more powerful, expressive languages enable formations of code that Gamma et al could only have dreamt of. Over the next little bit I'm going to explore a couple of nice ones that I've come across:
The Loan Pattern
... is actually the Strategy pattern but without the dreaded inheritance requirement - to refresh, here's a micro-Strategy example:abstract class StrategySuperclass<T> { public T doSomethingIntricateInThreePartsWherePartTwoVaries() { T part1Result = doFirstPart(); T part2Result = doSecondPart(part1Result); return doThirdPart(part2Result); } protected abstract T doSecondPart(T firstPartResult); ... } public class ConcreteStrategyClass<T> extends StrategySuperclass<T> { protected T doSecondPart(T firstPartResult) { // Do stuff here } }The principal idea is to shield concrete classes from the complexity or intricate orchestration of resources required to do some "large" task, by allowing them to just "slot in" the specialisation or detail that they need for their solution.
The Loan Pattern does not mandate any inheritance structure at all - the two parts of the solution could be within the same file, mixed in as traits, inherited, or composed together. It is particularly excellent at protecting limited/valuable/scarce resources that have some kind of lifecycle where they should be closed/returned/de-allocated after use. Here's an example that I gave as an answer to a Stack Overflow problem related to closing resources:
Here's the loan "provider" for want of a better term:
def withPrintWriter(dir:String, name:String)(f: (PrintWriter) => Any) { val file = new File(dir, name) val writer = new FileWriter(file) val printWriter = new PrintWriter(writer) try { f(printWriter) } finally { printWriter.close() } }Which you use like this, as a "consumer":
withPrintWriter("/tmp", "myFile") { printWriter => printWriter.write("all good") }Scala makes this kind of anonymous-function goodness really easy to both write and use. I've been using something similar in Specs2 tests recently for things like:
- Database connections. Borrow one, give it back at the end, no matter what happened
- Working directories. The provider makes sure the dir is empty, gives to the consumer, and then empties it out again at the end, just to be sure
- System properties This is a really nice pattern for this hard-to-unit-test situation. Set it, call the test function, then clear it out again. Just make sure your tests are both isolated and sequential to avoid unpleasant inter-test interference
No comments:
Post a Comment
Comments welcome - spam is not. Spam will be detected, deleted and the source IP blocked.