Sunday, August 31, 2014

Structural design patterns in Java

Simple examples of using structural design patterns in Java.
List of them from wikipedia:
Examples of Structural Patterns include:
  • Adapter pattern: 'adapts' one interface for a class into one that a client expects
    • Adapter pipeline: Use multiple adapters for debugging purposes.[1]
    • Retrofit Interface Pattern:[2][3] An adapter used as a new interface for multiple classes at the same time.
  • 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());
        
    }

}



No comments:

Post a Comment