Tuesday, April 01, 2014

Design Patterns - Structural

Structural

1.       Adapter - allows classes with incompatible interfaces to work together by wrapping its own interface around that of an already existing class. 2 types:
a.       Object Adapter Pattern - The adapter contains an instance of the class it wraps.

b.      Class Adapter Pattern - The adapter is created by implementing or inheriting both the interface that is expected and the interface that is pre-existing.

/**
 * Adapter design pattern can be implemented in two ways. One using the
 * inheritance method and second using the composition method.
 *
 * @author Watsh
 *
 */
class CylindricalSocket {
     public String supply(String cylinStem1, String cylinStem2) {
              String output = "Power power power...";
              return output;
     }
}

/**
 * Adapter using inheritance. Class Adapter Pattern.
 *
 * @author Watsh
 *
 */
class RectangularAdapter extends CylindricalSocket {
     public String adapt(String rectaStem1, String rectaStem2) {
              // some conversion logic
              String cylinStem1 = rectaStem1;
              String cylinStem2 = rectaStem2;
              return supply(cylinStem1, cylinStem2);
     }
}

/**
 * Adapter using composition. Object Adapter pattern.
 *
 */
class RectangularAdapter2 {
     private CylindricalSocket socket;

     public String adapt(String rectaStem1, String rectaStem2) {
              // some conversion logic
              socket = new CylindricalSocket();
              String cylinStem1 = rectaStem1;
              String cylinStem2 = rectaStem2;
              return socket.supply(cylinStem1, cylinStem2);
     }
}

public class AdapterExample {
     private static String rectaStem1 = "test";
     private static String rectaStem2 = "test";

     public static void main(String[] args) {
              RectangularAdapter adapter = new RectangularAdapter();
              String power = adapter.adapt(rectaStem1, rectaStem2);
              System.out.println(power);
     }
}

Adapter (recognizeable by creational methods taking an instance of different abstract/interface type and returning an implementation of own/another abstract/interface type which decorates/overrides the given instance)

·      java.io.InputStreamReader(InputStream) (returns a Reader)
·      java.io.OutputStreamWriter(OutputStream) (returns a Writer)

2.       Flyweight - reduces the cost of creating and manipulating a large number of similar objects.

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

// Instances of CoffeeFlavour will be the Flyweights
class CoffeeFlavour {
     private final String name;

     CoffeeFlavour(String newFlavor) {
               this.name = newFlavor;
     }

     @Override
     public String toString() {
              return name;
     }
}

// Menu acts as a factory and cache for CoffeeFlavour flyweight objects
class Menu {
     private Map flavours = new HashMap();

     CoffeeFlavour lookup(String flavorName) {
              if (!flavours.containsKey(flavorName))
                        flavours.put(flavorName, new CoffeeFlavour(flavorName));
              return flavours.get(flavorName);
     }

     int totalCoffeeFlavoursMade() {
              return flavours.size();
     }
}

class Order {
     private final int tableNumber;
     private final CoffeeFlavour flavour;

     Order(int tableNumber, CoffeeFlavour flavor) {
              this.tableNumber = tableNumber;
              this.flavour = flavor;
     }

     void serve() {
              System.out.println("Serving " + flavour + " to table " + tableNumber);
     }
}

public class FlyweightExample {
     private final List orders = new ArrayList();
     private final Menu menu = new Menu();

     void takeOrder(String flavourName, int table) {
              CoffeeFlavour flavour = menu.lookup(flavourName);
              Order order = new Order(table, flavour);
              orders.add(order);
     }

     void service() {
              for (Order order : orders)
                        order.serve();
     }

     String report() {
              return "\ntotal CoffeeFlavour objects made: "
                                  + menu.totalCoffeeFlavoursMade();
     }

     public static void main(String[] args) {
              FlyweightExample shop = new FlyweightExample();

              shop.takeOrder("Cappuccino", 2);
              shop.takeOrder("Frappe", 1);
              shop.takeOrder("Espresso", 1);
              shop.takeOrder("Frappe", 897);
              shop.takeOrder("Cappuccino", 97);
              shop.takeOrder("Frappe", 3);
              shop.takeOrder("Espresso", 3);
              shop.takeOrder("Cappuccino", 3);
              shop.takeOrder("Espresso", 96);
              shop.takeOrder("Frappe", 552);
              shop.takeOrder("Cappuccino", 121);
              shop.takeOrder("Espresso", 121);

              shop.service();
              System.out.println(shop.report());
     }
}

Flyweight (recognizeable by creational methods returning a cached instance, a bit the "multiton" idea)

·      java.lang.Integer#valueOf(int) (also on BooleanByteCharacterShort and Long)

3.       Bridge - decouples an abstraction from its implementation so that the two can vary independently.
 The bridge pattern is useful when both the class as well as what it does vary often. The class itself can be thought of as the implementation and what the class can do as the abstraction. The bridge pattern can also be thought of as two layers of abstraction.


/** "Implementor" */
interface Workshop {
          abstract public void work();
}

/** "ConcreteImplementor" 1/2 */
class Produce implements Workshop {

          @Override
          public void work() {
                   System.out.print("Produced");
          }

}

/** "ConcreteImplementor" 2/2 */
class Assemble implements Workshop {

          @Override
          public void work() {
                   System.out.println(" Assembled.");
          }

}

/**
 * abstraction in bridge pattern
 *
 */
abstract class Vehicle {
          protected Workshop workShop1;
          protected Workshop workShop2;

          protected Vehicle(Workshop workShop1, Workshop workShop2) {
                   this.workShop1 = workShop1;
                   this.workShop2 = workShop2;
          }

          abstract public void manufacture();
}

/** "Refined Abstraction" */
class Car extends Vehicle {

          public Car(Workshop workShop1, Workshop workShop2) {
                   super(workShop1, workShop2);
          }

          @Override
          public void manufacture() {
                   System.out.print("Car ");
                   workShop1.work();
                   workShop2.work();

          }
}

/** "Refined Abstraction" */
class Bike extends Vehicle {

          public Bike(Workshop workShop1, Workshop workShop2) {
                   super(workShop1, workShop2);
          }

          @Override
          public void manufacture() {
                   System.out.print("Bike ");
                   workShop1.work();
                   workShop2.work();
          }

}
/** "Client" */
public class BridgeExample {
          public static void main(String[] args) {

                   Vehicle vehicle1 = new Car(new Produce(), new Assemble());
                   vehicle1.manufacture();
                   Vehicle vehicle2 = new Bike(new Produce(), new Assemble());
                   vehicle2.manufacture();

          }
}

Bridge (recognizeable by creational methods taking an instance of different abstract/interface type and returning an implementation of own abstract/interface type which delegates/uses the given instance)

·      None comes to mind yet. A fictive example would be new LinkedHashMap(LinkedHashSet, List) which returns an unmodifiable linked map which doesn't clone the items, but uses them. The java.util.Collections#newSetFromMap() and singletonXXX() methods however comes close.

4.       Composite - composes zero-or-more similar objects so that they can be manipulated as one object.

File system can be modeled with composite pattern as shown below. Directory can be a composite of Files. Files could be either RegularFile (leafs) or Directories (composites).

import java.util.ArrayList;
import java.util.List;

/** Component */
interface Group {
       public void assemble();
}

/** Leaf */
class Block implements Group {

       public void assemble() {
              System.out.println("Block");
       }
}
/** Composite */
class Structure implements Group {
       // Collection of child groups.
       private List groups = new ArrayList();

       public void assemble() {
              for (Group group : groups) {
                     group.assemble();
              }
       }

       // Adds the group to the structure.
       public void add(Group group) {
              groups.add(group);
       }

       // Removes the group from the structure.
       public void remove(Group group) {
              groups.remove(group);
       }
}

public class CompositeExample {
       public static void main(String[] args) {
        //Initialize three blocks
        Block block1 = new Block();
        Block block2 = new Block();
        Block block3 = new Block();

        //Initialize three structure
        Structure structure = new Structure();
        Structure structure1 = new Structure();
        Structure structure2 = new Structure();

        //Composes the groups
        structure1.add(block1);
        structure1.add(block2);

        structure2.add(block3);

        structure.add(structure1);
        structure.add(structure2);

        structure.assemble();
    }
}

Composite (recognizeable by behavioral methods taking an instance of same abstract/interface type into a tree structure)

·      java.awt.Container#add(Component) (practically all over Swing thus)
·      javax.faces.component.UIComponent#getChildren() (practically all over JSF UI thus)

5.       Decorator - dynamically adds/overrides behavior in an existing method of an object.



interface Icecream {
       public String makeIcecream();
}

class SimpleIcecream implements Icecream {

       @Override
       public String makeIcecream() {
              return "Base Icecream";
       }

}

abstract class IcecreamDecorator implements Icecream {

       protected Icecream specialIcecream;

       public IcecreamDecorator(Icecream specialIcecream) {
              this.specialIcecream = specialIcecream;
       }

       public String makeIcecream() {
              return specialIcecream.makeIcecream();
       }
}

class NuttyDecorator extends IcecreamDecorator {

       public NuttyDecorator(Icecream specialIcecream) {
              super(specialIcecream);
       }

       public String makeIcecream() {
              return specialIcecream.makeIcecream() + addNuts();
       }

       private String addNuts() {
              return " + cruncy nuts";
       }
}

class HoneyDecorator extends IcecreamDecorator {
        
         public HoneyDecorator(Icecream specialIcecream) {
           super(specialIcecream);
         }
        
         public String makeIcecream() {
           return specialIcecream.makeIcecream() + addHoney();
         }
        
         private String addHoney() {
           return " + sweet honey";
         }
       }

public class DecoratorExample {
       public static void main(String args[]) {
           Icecream icecream = new HoneyDecorator(new NuttyDecorator(new SimpleIcecream()));
           System.out.println(icecream.makeIcecream());
         }
}

Decorator (recognizeable by creational methods taking an instance of same abstract/interface type which adds additional behaviour)

·      All subclasses of java.io.InputStreamOutputStreamReader and Writer have a constructor taking an instance of same type.

6.       Façade - provides a simplified interface to a large body of code.

Facade (recognizeable by behavioral methods which internally uses instances of different independent abstract/interface types)

·      javax.faces.context.FacesContext, it internally uses among others the abstract/interface types LifeCycleViewHandlerNavigationHandler and many more without that the enduser has to worry about it (which are however overrideable by injection).

7.       Proxy - provides a placeholder for another object to control access, reduce cost, and reduce complexity.

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface Animal {

       public void getSound();

}

class Lion implements Animal {

       public void getSound() {
              System.out.println("Roar");
       }

}

/**
 * InvocationHandler is the interface implemented by the invocation handler of a
 * proxy instance. Each proxy instance has an associated invocation handler.
 * When a method is invoked on a proxy instance, the method invocation is
 * encoded and dispatched to the invoke method of its invocation handler.
 */
class AnimalInvocationHandler implements InvocationHandler {
       public AnimalInvocationHandler(Object realSubject) {
              this.realSubject = realSubject;
       }

       public Object invoke(Object proxy, Method m, Object[] args) {
              Object result = null;
              try {
                     /*
                      * Execute the getSound() method of realSubject.
                      */
                     result = m.invoke(realSubject, args);
              } catch (Exception ex) {
                     ex.printStackTrace();
              }
              return result;
       }

       private Object realSubject = null;
}

/**
 * Proxy provides static methods for creating dynamic proxy classes and
 * instances, and it is also the superclass of all dynamic proxy classes created
 * by those methods.
 *
 * @author Watsh
 *
 */
public class ProxyExample {
       public static void main(String[] args) {
              Animal realSubject = new Lion();
              ClassLoader classLoader = realSubject.getClass().getClassLoader();
              /*
               * interfaces below will be Animal
               */
              Class[] interfaces = realSubject.getClass().getInterfaces();
              AnimalInvocationHandler invocationHandler = new AnimalInvocationHandler(
                           realSubject);
              /*
               * Creates a dynamic proxy for Lion's realSubject instance for the
               * interface methods that Lion implements.
               */
              Animal proxy = (Animal) Proxy.newProxyInstance(classLoader, interfaces,
                           invocationHandler);
              /*
               * Calling an interface method on proxy instance will call invoke() of
               * realSubject instance's invocation Handler.
               */
              proxy.getSound();
       }
}

Proxy (recognizeable by creational methods which returns an implementation of given abstract/interface type which in turndelegates/uses a different implementation of given abstract/interface type)


·      java.rmi.*, the whole API actually.

No comments:

Popular micro services patterns

Here are some popular Microservice design patterns that a programmer should know: Service Registry  pattern provides a  central location  fo...