Design Patterns Reconsidered

Preview:

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

Recommended