Upload
alex-miller
View
111
Download
1
Embed Size (px)
DESCRIPTION
Revisiting some popular design patterns in Java: singleton, template method, visitor.
Citation preview
Design Patterns Reconsidered
Alex Miller@puredanger
What is a Design Pattern?
“Each pattern describes a problem which occurs over and over again in our environment and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice.”
- Christopher Alexander
The Patterns Backlash
• Copy/paste
• Design by template
• Cookbook / recipe approach
“The Design Patterns solution is to turn the programmer into a fancy macro processor”
- M. J. Dominus, “Design Patterns” Aren't
The Patterns Backlash
• Design patterns aren’t patterns
• Just workaround for missing language features
“At code level, most design patterns are code smells.”
- Stuart Halloway
The Patterns Backlash
• Overuse
“Beginning developers never met a pattern or an object they didn’t like. Encouraging them to experiment with patterns is like throwing gasoline on a fire.”
- Jeff Atwood, Coding Horror
...Reconsidered
Singleton
TemplateMethod
Visitor
Proxy
Singleton
+ getInstance() : Singleton- Singleton()+ foo() : Object
- INSTANCESingleton
There can be only one...
Classic Singleton
public final class Singleton {
Classic Singleton
public final class Singleton { private static Singleton INSTANCE =
Classic Singleton
public final class Singleton { private static Singleton INSTANCE = new Singleton();
Classic Singleton
public final class Singleton { private static Singleton INSTANCE = new Singleton();
private Singleton() {}
Classic Singleton
public final class Singleton { private static Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton instance() {
Classic Singleton
public final class Singleton { private static Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton instance() { return INSTANCE;
Classic Singleton
public final class Singleton { private static Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton instance() { return INSTANCE; }
Classic Singleton
public final class Singleton { private static Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton instance() { return INSTANCE; }
public Object read() {
Classic Singleton
public final class Singleton { private static Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton instance() { return INSTANCE; }
public Object read() { // nasty database call
Classic Singleton
public final class Singleton { private static Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton instance() { return INSTANCE; }
public Object read() { // nasty database call }
Classic Singleton
public final class Singleton { private static Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton instance() { return INSTANCE; }
public Object read() { // nasty database call }}
Classic Singleton
public class Victim { public void something() { Object foo = Singleton.instance().read(); // etc... }}
Things go horribly wrong
public class Victim { public void something() { Object foo = Singleton.instance().read(); // etc... }}
public class TestVictim { public void testSomething() { // Holy crap, how do I // mock the db call in Singleton? }}
Things go horribly wrong
Singletons
Create
Coupling!
Hidden
public interface Singleton { Object read();}
public class SingletonImpl implements Singleton {
public Object read() { // nasty database call return null; }}
Interfaces to the rescue
public class Victim { private final Singleton singleton;
public Victim(Singleton singleton) { this.singleton = singleton; }
public void something() { Object foo = singleton.read(); // etc... }}
Dependency injection, you’re my hero!
public class TestVictim {
public void testSomething() { Singleton s = new MockSingleton(); Victim victim = new Victim(s);
victim.something();
// assertions }}
Now we can test
public class Component { private final Victim victim; public Component() { victim = new Victim( new SingletonImpl() ); }}
Push construction up
Bubble
Up
Singletons
Singleton
What have we learned?
• Interfaces and dependency injection
• Reduce hidden coupling
• Allow testability
• Allow subclassing
• Make construction and use flexible
• If need only one, control by configuration
• Guice
• Spring
...Reconsidered
Singleton
TemplateMethod
Visitor
Proxy
Template Method
Template Method
+ algorithm()# step1()# step2()# step3()
TemplateAlgorithmpublic void algorithm() { step1(); step2(); step3();}
# step1()# step2()# step3()
ConcreteAlgorithm1# step2()ConcreteAlgorithm2
Spring MVC Controllers
Controller (interface)AbstractController AbstractUrlViewController UrlFilenameViewController BaseCommandController AbstractCommandController AbstractFormController AbstractWizardFormController SimpleFormController CancellableFormController MultiActionController ParameterizableViewController
“What we’ve got here is a failure to communicate....”
public void algorithm() { step1Strategy.step1(); step2Strategy.step2(); step3Strategy.step3();}
+ step1()Step1Strategy
+ step2()Step2Strategy
+ step3()Step3Strategy
+ algorithm()
- Step1Strategy- Step2Strategy- Step3Strategy
TemplateAlgorithm
Refactoring to steps
public void algorithm() { Context context = new Context(); step1Strategy.step1(context); step2Strategy.step2(context); step3Strategy.step3(context);}
+ step1(Context ctx)Step1Strategy
+ step2(Context ctx)Step2Strategy
+ step3(Context ctx)Step3Strategy
+ algorithm()
- Step1Strategy- Step2Strategy- Step3Strategy
TemplateAlgorithm
Context
Sharing context
What have we learned?
• Prefer composition to inheritance
• Allows greater reuse
• Communicates intent better
• Easier to understand and maintain
• More robust as it evolves
• Inheritance is a very strong form of coupling
• Especially in a single-inheritance language
...Reconsidered
Singleton
TemplateMethod
Visitor
Proxy
operation1()operation2()
Node
operation1()operation2()
ConcreteNode1operation1()operation2()
CompositeNode
Composite hierarchy
accept(NodeVisitor v)Node
accept(NodeVisitor v)ConcreteNode1
accept(NodeVisitor v)CompositeNode
visit(ConcreteNode1 n)visit(ConcreteNode2 n)...
NodeVisitor
visit(ConcreteNode1 n)visit(ConcreteNode2 n)...
ConcreteVisitor1visit(ConcreteNode1 n)visit(ConcreteNode2 n)...
ConcreteVisitor2
Visitor Pattern
Navigation
public class CompositeNode implements Visitable {
private List<Node> nodes; public void accept(NodeVisitor v) { v.visit(this); for(Node n : nodes) { v.visit(n); } }}
Internal Navigation
public class CompositeNode { private List<Node> nodes; public void accept(NodeVisitor v) { v.visit(this);
List<Node> children = Navigation.getChildren(this);
for(Node n : children) { n.acceptVisitor(this); } }}
Navigation oracle
visit(ConcreteNode1 n)visit(ConcreteNode2 n)...
NodeVisitor
visit(ConcreteNode1 n)visit(ConcreteNode2 n)...
ConcreteVisitor1visit(ConcreteNode1 n)visit(ConcreteNode2 n)...
ConcreteVisitor2
NavigationVisitor(NodeVisitor v)visit(ConcreteNode1 n)visit(ConcreteNode2 n)...
NavigationVisitor
Navigation Visitor
Evolution
accept(NodeVisitor v)Node
accept(NodeVisitor v)ConcreteNode1
accept(NodeVisitor v)CompositeNode
visit(ConcreteNode1 n)visit(ConcreteNode2 n)visit(NewNode n)...
NodeVisitor
visit(ConcreteNode1 n)visit(ConcreteNode2 n)visit(NewNode n)...
ConcreteVisitor1visit(ConcreteNode1 n)visit(ConcreteNode2 n)visit(NewNode n)...
ConcreteVisitor2
accept(NodeVisitor v)NewNode
visit(ConcreteNode1 n)visit(ConcreteNode2 n)visit(NewNode n)...
NodeVisitor
visit(ConcreteNode1 n)visit(ConcreteNode2 n)visit(NewNode n)...
ConcreteVisitor1
visit(ConcreteNode1 n)visit(ConcreteNode2 n)visit(NewNode n)...
ConcreteVisitor2
visit(ConcreteNode1 n)visit(ConcreteNode2 n)visit(NewNode n)...
BaseVisitor
Visitor Types
• “Collector” visitor - accumulate state
• “Finder” visitor - search and return
• “Event” visitor - stateless, fire events
• “Transform” visitor - modify while walking
• “Validation” visitor - validate and report
public class FindVisitor implements ConcreteVisitor {
private final int seek; private Node match;
public FindVisitor(int seek) { this.seek = seek; } public Node getMatch() { return this.match; } public void visit(ConcreteNode1 n) { if( this.match == null && n.getValue() == seek) { this.match = n; } }}
Visitor abort
public class ComputeVisitor implements ConcreteVisitor {
public void visit(ConcreteNode1 n) { try { // blah blah } catch(BadException e) { // what now? } }}
Exceptions
What have we learned?
• Expression problem is tough
• Abstract base classes help plan for evolution
• Generics add precision and flexibility by revealing hidden coupling
Questions?
http://tech.puredanger.com/presentations/design-patterns-reconsidered