Simple examples of using structural design patterns in Java.
List of them from wikipedia:
List of them from wikipedia:
Examples of Structural Patterns include:
- Adapter pattern: 'adapts' one interface for a class into one that a client expects
- Bridge pattern: decouple an abstraction from its implementation so that the two can vary independently
- Tombstone: An intermediate "lookup" object contains the real location of an object.[4]
- Composite pattern: a tree structure of objects where every object has the same interface
- Decorator pattern: add additional functionality to a class at runtime where subclassing would result in an exponential rise of new classes
- Facade pattern: create a simplified interface of an existing interface to ease usage for common tasks
- Flyweight pattern: a high quantity of objects share a common properties object to save space
- Proxy pattern: a class functioning as an interface to another thing
1. Adapter pattern.
Actors:
1. "convenient" interface
2. adapter class which adapt "non-convenient" classes to match "convenient" interface
Goal: using methods of interface on class which doesn't implements that interface using adaptor class.
Main class:
public class Adaptor { public interface Movable { String move(); } public class Shape { private String name; public Shape(String name) { this.name=name; } public String relocate() { return "shape "+name+" is relocated"; } } public class ShapeToMovableAdaptor implements Movable { private Shape shape; public ShapeToMovableAdaptor(Shape shape) { this.shape=shape; } @Override public String move() { return shape.relocate(); } } }
Test class:
public class AdaptorTest { @Test public void test() { Adaptor adaptor=new Adaptor(); Adaptor.Shape shape=adaptor. new Shape("circle"); Adaptor.Movable shapeAdaptor=adaptor. new ShapeToMovableAdaptor(shape); String result=shapeAdaptor.move(); Assert.assertEquals("shape circle is relocated", result); } }
2. Bridge
Actors:
1. Implementation
2. Abstraction - which depends(aggregate) on Implementation (for instance, Implementation set in constructor)
3. Concrete classes which extends Abstraction
Goal: implementation separated from abstraction
Main class:
public class Bridge { interface Implementation { String move(); } class FastImplementation implements Implementation { @Override public String move() { return "fast"; } } class SlowImplementation implements Implementation { @Override public String move() { return "slow"; } } public abstract class Abstraction{ Implementation implementation; public Abstraction(Implementation implementation) { this.implementation=implementation; } public String rellocate() { return implementation.move() +" rellocation"; } } public class ConcreteSlowClass extends Abstraction { public ConcreteSlowClass() { super(new SlowImplementation()); } } public class ConcreteFastClass extends Abstraction { public ConcreteFastClass() { super(new FastImplementation()); } } }
Test class:
public class BridgeTest { @Test public void test() { Bridge bridge=new Bridge(); Bridge.ConcreteFastClass fast=bridge. new ConcreteFastClass(); assertEquals("fast rellocation", fast.rellocate()); Bridge.ConcreteSlowClass slow=bridge. new ConcreteSlowClass(); assertEquals("slow rellocation", slow.rellocate()); } }
3. Composite
Actors:
1. interface
2. simple object which implements interface
3. complex object(contain set of simple objects) with implements interface
Goal:
Do some logic with set(array) of objects like one single object
Main class:
public class Composite { public interface drawable { String draw(); } public class SimpleObject implements drawable { String name; @Override public String draw() { return "drawing " + name; } public SimpleObject(String name) { this.name = name; } } public class CompositeObject implements drawable { List<Composite.SimpleObject> objects=new ArrayList<Composite.SimpleObject>(); public void addObject(Composite.SimpleObject object) { objects.add(object); } @Override public String draw() { StringBuilder result = new StringBuilder(); for (Composite.SimpleObject object : objects) { result.append(object.draw() + "."); } return result.toString(); }
Test class: } }
public class CompositeTest { @Test public void test() { Composite composite=new Composite(); Composite.SimpleObject simple1=composite. new SimpleObject("first"); assertEquals("drawing first", simple1.draw()); Composite.SimpleObject simple2=composite. new SimpleObject("second"); Composite.CompositeObject complex=composite. new CompositeObject(); complex.addObject(simple1); complex.addObject(simple2); assertEquals("drawing first.drawing second.", complex.draw()); } }
4. Decorator
Actors:
1. original class
2. decorator class which wrap original class
Goal: extend functionality of original class
Main class:
public class Decorator { public static class Shape { public String draw() { return "shape"; } } public static class RedDecorator { private Shape shape; public RedDecorator(Shape shape) { this.shape=shape; } public String drawRed() { return shape.draw()+" is red"; } } public static class BigDecorator { private RedDecorator redDecorator; public BigDecorator(RedDecorator redDecorator) { this.redDecorator=redDecorator; } public String drawBig() { return redDecorator.drawRed()+" and big"; } } }
Test class:
public class DecoratorTest { @Test public void test() { String result=new Decorator.BigDecorator(new Decorator.RedDecorator(new Decorator.Shape())).drawBig(); assertEquals("shape is red and big", result); } }
5. Facade
Actors: 1. set of classes (library) 2. facade class for managing them Goal: interface for library: one class which manage other classes
Main class:
public class Facade { public interface Drawable { public String draw(); } public class Square implements Drawable { @Override public String draw() { return "square."; } } public class Circle implements Drawable { @Override public String draw() { return "circle."; } } public class ComplexDrawingFacade { public String drawComplexObject() { Square square1=new Square(); Square square2=new Square(); Circle circle=new Circle(); String result=square1.draw()+circle.draw()+square2.draw(); return result; } } }
Test class:
public class FacadeTest { @Test public void test() { Facade facade=new Facade(); Facade.ComplexDrawingFacade compleDrawingFacade=facade. new ComplexDrawingFacade(); assertEquals("square.circle.square.", compleDrawingFacade.drawComplexObject()); } }
6. FlyWeight
Actors:
1. set of objects
2. flyweight(cache) object: contains list of created objects
Goal: using object cache(for memory minimizing) and object sharing instead of new object creation
public class FlyWeight { public static abstract class AbstractShape { public abstract String draw(); } public static class Shape extends AbstractShape { @Override public String draw() { return "shape"; } } public static class Circle extends AbstractShape { @Override public String draw() { return "circle"; } } public class FlyWeightFactory { HashMap<String, AbstractShape> cache=new HashMap<String, AbstractShape>(); public AbstractShape lookUp(Class<?> cl) throws InstantiationException, IllegalAccessException { String className=cl.getCanonicalName(); if (!cache.containsKey(className)) { try { Object o=cl.newInstance(); } catch(Exception e) { e.printStackTrace(); } AbstractShape shape=(AbstractShape)cl.newInstance(); cache.put(className, shape); } return cache.get(className); } } }
Test class:
public class FlyWeightTest { @Test public void test() throws InstantiationException, IllegalAccessException { FlyWeight flyWeight=new FlyWeight(); FlyWeight.FlyWeightFactory factory=flyWeight. new FlyWeightFactory(); FlyWeight.AbstractShape circle=factory.lookUp(FlyWeight.Circle.class); FlyWeight.AbstractShape shape=factory.lookUp(FlyWeight.Shape.class); assertEquals("shape", shape.draw()); assertEquals("circle", circle.draw()); } }
7. Proxy
Actors:
1. remote object
2. local proxy class which has the same interface as remote object
3. local client, which works with proxy as like it is remote object
Goal : make client independent of remote object and connection.
Also proxy object can add some addition functionality, checking and other stuff.
Main class:
public class Proxy { interface Drawable { String draw(); } public class RealImplementation implements Drawable { @Override public String draw() { return "real draw"; } } public class ProxyImplementation extends RealImplementation { RealImplementation realImplementation=new RealImplementation(); @Override public String draw() { // do some logic return realImplementation.draw(); // do some logic } } }
Test class:
public class ProxyTest { @Test public void test() { Proxy proxy=new Proxy(); Proxy.RealImplementation remoteObject=proxy. new ProxyImplementation(); assertEquals("real draw", remoteObject.draw()); } }