55
Design Patterns Reconsidered Alex Miller @puredanger

Design Patterns Reconsidered

Embed Size (px)

DESCRIPTION

Revisiting some popular design patterns in Java: singleton, template method, visitor.

Citation preview

Page 1: Design Patterns Reconsidered

Design Patterns Reconsidered

Alex Miller@puredanger

Page 2: Design Patterns Reconsidered

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

Page 3: Design Patterns Reconsidered
Page 4: Design Patterns Reconsidered

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

Page 5: Design Patterns Reconsidered

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

Page 6: Design Patterns Reconsidered

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

Page 7: Design Patterns Reconsidered

...Reconsidered

Singleton

TemplateMethod

Visitor

Proxy

Page 8: Design Patterns Reconsidered

Singleton

Page 9: Design Patterns Reconsidered

+ getInstance() : Singleton- Singleton()+ foo() : Object

- INSTANCESingleton

There can be only one...

Page 10: Design Patterns Reconsidered

Classic Singleton

Page 11: Design Patterns Reconsidered

public final class Singleton {

Classic Singleton

Page 12: Design Patterns Reconsidered

public final class Singleton { private static Singleton INSTANCE =

Classic Singleton

Page 13: Design Patterns Reconsidered

public final class Singleton { private static Singleton INSTANCE = new Singleton();

Classic Singleton

Page 14: Design Patterns Reconsidered

public final class Singleton { private static Singleton INSTANCE = new Singleton();

private Singleton() {}

Classic Singleton

Page 15: Design Patterns Reconsidered

public final class Singleton { private static Singleton INSTANCE = new Singleton();

private Singleton() {}

public static Singleton instance() {

Classic Singleton

Page 16: Design Patterns Reconsidered

public final class Singleton { private static Singleton INSTANCE = new Singleton();

private Singleton() {}

public static Singleton instance() { return INSTANCE;

Classic Singleton

Page 17: Design Patterns Reconsidered

public final class Singleton { private static Singleton INSTANCE = new Singleton();

private Singleton() {}

public static Singleton instance() { return INSTANCE; }

Classic Singleton

Page 18: Design Patterns Reconsidered

public final class Singleton { private static Singleton INSTANCE = new Singleton();

private Singleton() {}

public static Singleton instance() { return INSTANCE; }

public Object read() {

Classic Singleton

Page 19: Design Patterns Reconsidered

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

Page 20: Design Patterns Reconsidered

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

Page 21: Design Patterns Reconsidered

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

Page 22: Design Patterns Reconsidered

public class Victim { public void something() { Object foo = Singleton.instance().read(); // etc... }}

Things go horribly wrong

Page 23: Design Patterns Reconsidered

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

Page 24: Design Patterns Reconsidered

Singletons

Create

Coupling!

Hidden

Page 25: Design Patterns Reconsidered

public interface Singleton { Object read();}

public class SingletonImpl implements Singleton {

public Object read() { // nasty database call return null; }}

Interfaces to the rescue

Page 26: Design Patterns Reconsidered

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!

Page 27: Design Patterns Reconsidered

public class TestVictim {

public void testSomething() { Singleton s = new MockSingleton(); Victim victim = new Victim(s);

victim.something();

// assertions }}

Now we can test

Page 28: Design Patterns Reconsidered

public class Component { private final Victim victim; public Component() { victim = new Victim( new SingletonImpl() ); }}

Push construction up

Page 29: Design Patterns Reconsidered

Bubble

Up

Singletons

Page 30: Design Patterns Reconsidered

Singleton

Page 31: Design Patterns Reconsidered

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

Page 32: Design Patterns Reconsidered

...Reconsidered

Singleton

TemplateMethod

Visitor

Proxy

Page 33: Design Patterns Reconsidered

Template Method

Page 34: Design Patterns Reconsidered

Template Method

+ algorithm()# step1()# step2()# step3()

TemplateAlgorithmpublic void algorithm() { step1(); step2(); step3();}

# step1()# step2()# step3()

ConcreteAlgorithm1# step2()ConcreteAlgorithm2

Page 35: Design Patterns Reconsidered

Spring MVC Controllers

Controller (interface)AbstractController AbstractUrlViewController UrlFilenameViewController BaseCommandController AbstractCommandController AbstractFormController AbstractWizardFormController SimpleFormController CancellableFormController MultiActionController ParameterizableViewController

Page 36: Design Patterns Reconsidered
Page 37: Design Patterns Reconsidered

“What we’ve got here is a failure to communicate....”

Page 38: Design Patterns Reconsidered

public void algorithm() { step1Strategy.step1(); step2Strategy.step2(); step3Strategy.step3();}

+ step1()Step1Strategy

+ step2()Step2Strategy

+ step3()Step3Strategy

+ algorithm()

- Step1Strategy- Step2Strategy- Step3Strategy

TemplateAlgorithm

Refactoring to steps

Page 39: Design Patterns Reconsidered

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

Page 40: Design Patterns Reconsidered

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

Page 41: Design Patterns Reconsidered

...Reconsidered

Singleton

TemplateMethod

Visitor

Proxy

Page 42: Design Patterns Reconsidered

operation1()operation2()

Node

operation1()operation2()

ConcreteNode1operation1()operation2()

CompositeNode

Composite hierarchy

Page 43: Design Patterns Reconsidered

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

Page 44: Design Patterns Reconsidered

Navigation

Page 45: Design Patterns Reconsidered

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

Page 46: Design Patterns Reconsidered

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

Page 47: Design Patterns Reconsidered

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

Page 48: Design Patterns Reconsidered

Evolution

Page 49: Design Patterns Reconsidered

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

Page 50: Design Patterns Reconsidered

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

Page 51: Design Patterns Reconsidered

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

Page 52: Design Patterns Reconsidered

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

Page 53: Design Patterns Reconsidered

public class ComputeVisitor implements ConcreteVisitor {

public void visit(ConcreteNode1 n) { try { // blah blah } catch(BadException e) { // what now? } }}

Exceptions

Page 54: Design Patterns Reconsidered

What have we learned?

• Expression problem is tough

• Abstract base classes help plan for evolution

• Generics add precision and flexibility by revealing hidden coupling