Java Design Pattern

Contents

Creational Pattern
Singleton Design Pattern.
Factory Method Design Pattern.
Abstract Factory Design Pattern.
Prototype Design Pattern.
Builder Design Pattern.
Object Pool Design Pattern.

Structural Pattern
Adapter Design Pattern.
Bridge Design Pattern.
Composite Design Pattern.
Decorator Design Pattern.


Behavioral Pattern
Observer Design Pattern

To download Sample Example Code in GitHub click here.

Creational Pattern

Singleton Design Pattern


Definition:
  1. There should be only one instance allowed for a class.
  2. Should allow global point of access to that single instance.
GOF says, “Ensure a class has only one instance, and provide a global point of access to it. [GoF, p127]“.
Example: java.lang.Runtime which provides getRuntime() method to get access of it. It is used for Garbage collection.

Problem may arise:
  1. For multi-threading environment, Multiple object may creation.
  2. If other class extends Singleton class, then more than one object may created of Singleton class.
  3. For Serialization and De-Serialization of the Singleton class object
  4. Using clone() method, user can clone the Singleton object.
  5. Using different ClassLoader, user can load the Singleton class.
Below code is an example of lazy instantiation for singleton design pattern. The single instance will be created at the time of first call of the getInstance() method. 
Code:
public final class Singleton {
      
       private static Singleton instance;

       private Singleton() {
       }
      
       @Override
       protected Object clone() throws CloneNotSupportedException {
              return new CloneNotSupportedException("Not allowed clone method in Singleton class");
       }

       public static Singleton getInstance() {
              if (instance == null) {
                     synchronized (Singleton.class) {                      
                           instance = new Singleton();                    
                     }
              }
              return instance;
       }
}


Factory Method Design Pattern

Pattern definition:
It is used to instantiate a specific required object from a set of classes based on some logic. Based on the arguments passed to factory method, it does logical operations and decides on which sub class to be instantiated.
Block Diagram:
Factory Pattern

Example: SAXParserFactory
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();

Code:
public interface Pet {
      public String speak();
}
class Dog implements Pet {
      @Override
      public String speak() {
            return "Bark bark...";
      }
}

class Duck implements Pet {
      @Override
      public String speak() {
            return "Quack quack...";
      }
}

public class Factory {
     
      public Pet getPet(String petType) {
            Pet pet = null;

            // Based on logic factory instantiates an object
            if ("bark".equals(petType))
                  pet = new Dog();
            else if ("quack".equals(petType))
                  pet = new Duck();
            return pet;
      }
}

public class MainFactory {

      /**
       * @param args
       */
      public static void main(String[] args) {
            Factory petFactory = new Factory();

            // factory instantiates an object
            Pet pet = petFactory.getPet("bark");

            // you don't know which object factory created
            System.out.println(pet.speak());
      }
}


Abstract Factory Design Pattern

Concept:
Set of ‘related’ factory method design pattern, put all those set of simple factories inside a factory pattern. So in turn no need to be aware of the final concrete class that will be instantiated. 
Provide a level of indirection that abstracts the creation of families of related or dependent objects without directly specifying their concrete classes. The “factory” object has the responsibility for providing creation services for the entire platform family. Clients never create platform objects directly, they ask the factory to do that for them.  The service provided by the factory object is so pervasive (Spreading widely throughout the program), it is routinely implemented as a Singleton.
Abstract Factory classes are often implemented with Factory Methods, but they can also be implemented using Prototype Design Pattern.
Abstract Factory can be used as an alternative to Facade to hide platform-specific classes.
Example:
javax.xml.validation.SchemaFactory

UML Diagram of Abstract Factory Pattern:
Abstract Factory Pattern - Example 1



Abstract Factory Pattern - Example 2
Code Snippet:
public class AbstractFactory {

       public SpeciesFactory getSpeciesFactory(String type) {
              if ("mammal".equals(type)) {
                     return new MammalFactory();
              } else {
                     return new ReptileFactory();
              }
       }

}

class MammalFactory extends SpeciesFactory {

       @Override
       public Animal getAnimal(String type) {
              if ("bat".equals(type)) {
                     return new Bat();
              } else {
                     return new Cat();
              }
       }

}

class ReptileFactory extends SpeciesFactory {

       @Override
       public Animal getAnimal(String type) {
              if ("snake".equals(type)) {
                     return new Snake();
              } else {
                     return new Tyrannosaurus();
              }
       }

}

class Cat extends Animal {

       @Override
       public String makeSound() {
              return "Meow";
       }

}

class Bat extends Animal {

       @Override
       public String makeSound() {
              return "Cheee";
       }

}

class Snake extends Animal {

       @Override
       public String makeSound() {
              return "Hiss";
       }

}

class Tyrannosaurus extends Animal {

       @Override
       public String makeSound() {
              return "Roar";
       }

}

public abstract class Animal {
       public abstract String makeSound();
}
public abstract class SpeciesFactory {
       public abstract Animal getAnimal(String type);
}

public class MainAbstractFactory {

       /**
        * @param args
        */
       public static void main(String[] args) {

              AbstractFactory abstractFactory = new AbstractFactory();

              SpeciesFactory speciesFactory1 = abstractFactory
                           .getSpeciesFactory("reptile");
              Animal a1 = speciesFactory1.getAnimal("tyrannosaurus");
              System.out.println("a1 sound: " + a1.makeSound());
              Animal a2 = speciesFactory1.getAnimal("snake");
              System.out.println("a2 sound: " + a2.makeSound());

              SpeciesFactory speciesFactory2 = abstractFactory
                           .getSpeciesFactory("mammal");
              Animal a3 = speciesFactory2.getAnimal("bat");
              System.out.println("a3 sound: " + a3.makeSound());
              Animal a4 = speciesFactory2.getAnimal("cat");
              System.out.println("a4 sound: " + a4.makeSound());

       }

}


Prototype Design Pattern

Concept:
When creating an object is time consuming and a costly affair and you already have a most similar object instance in hand, then you go for prototype pattern. Instead of going through a time consuming process to create a complex object, just copy the existing similar object and modify it according to your needs.
Clone the existing instance in hand and then make the required update to the cloned instance so that you will get the object you need. Other way is, tweak the cloning method itself to suit your new object creation need. Therefore whenever you clone that object you will directly get the new object of desire without modifying the created object explicitly.
While using clone to copy, whether you need a shallow copy or deep copy
Shallow copy - Generally clone method of an object, creates a new instance of the same class and copies all the fields to the new instance and returns it. This is nothing but shallow copy. 
Deep copy - When you need a deep copy then you need to implement it yourself. When the copied object contains some other object its references are copied recursively in deep copy. When you implement deep copy be careful as you might fall for cyclic dependencies. If you don’t want to implement deep copy yourselves then you can go for serialization. It does implements deep copy implicitly and gracefully handling cyclic dependencies.

Code Snippet:
package design_pattern.creational;

class Bike implements Cloneable {

      private int gears;
      private String bikeType;
      private String model;

      public Bike() {
            bikeType = "Standard";
            model = "Leopard";
            gears = 4;
      }

      @Override
      public Bike clone() {
            return new Bike();
      }

      public void makeAdvanced() {
            bikeType = "Advanced";
            model = "Jaguar";
            gears = 6;
      }

      public String getModel() {
            return model;
      }

      public int getGears() {
            return gears;
      }

      public String getBikeType() {
            return bikeType;
      }
}

public class Prototype {

      public Bike makeJaguar(Bike basicBike) {
            basicBike.makeAdvanced();
            return basicBike;
      }

      public static void main(String arg[]) {
            Bike bike = new Bike();
            Bike basicBike = bike.clone();
            Prototype protptypeBike = new Prototype();
            Bike advancedBike = protptypeBike.makeJaguar(basicBike);
            System.out.println("Prototype Design Pattern: "
                        + advancedBike.getModel());
      }
}




Builder Design Pattern

Concept:
Builder pattern is used to construct a complex object step by step and the final step will return the object. The process of constructing an object should be generic so that it can be used to create different representations of the same object.
GOF says,
“Separate the construction of a complex object from its representation so that the same construction process can create different representations” [GoF 94]
In builder pattern emphasis is on ‘step by step’. Builder pattern will have many number of small steps. Those every steps will have small units of logic enclosed in it. There will also be a sequence involved. It will start from step 1 and will go on upto step n and the final step is returning the object. In these steps, every step will add some value in construction of the object. That is you can imagine that the object grows stage by stage. Builder will return the object in last step.

Example :
DocumentBuilderFactory , StringBuffer, StringBuilder
UML Diagram (Example):
Builder Pattern
  • Product. The product class defines the type of the complex object that is to be generated by the builder pattern.
  • Builder. This abstract base class defines all of the steps that must be taken in order to correctly create a product. Each step is generally abstract as the actual functionality of the builder is carried out in the concrete subclasses. The GetProduct method is used to return the final product. The builder class is often replaced with a simple interface.
  • ConcreteBuilder. There may be any number of concrete builder classes inheriting from Builder. These classes contain the functionality to create a particular complex product.
  • Director. The director class controls the algorithm that generates the final product object. A director object is instantiated and its Construct method is called. The method includes a parameter to capture the specific concrete builder object that is to be used to generate the product. The director then calls methods of the concrete builder in the correct order to generate the product object. On completion of the process, the GetProduct method of the builder object can be used to return the product.

Code Snippet:

interface Builder {
      
       public void BuildBasement();

       public void BuildWall();

       public void BuildRoof();

       public Product GetProduct();
}

class Director {
      
       public void Construct(Builder builder) {
              builder.BuildBasement();
              builder.BuildWall();
              builder.BuildRoof();
       }
}

class ConcreteBuilder implements Builder {
      
       private Product _product = new Product();

       @Override
       public void BuildBasement() {
              _product.Part1 = "Basement Constructed";
       }

       @Override
       public void BuildWall() {
              _product.Part2 = "Wall Constructed";
       }

       @Override
       public void BuildRoof() {
              _product.Part3 = "Roof Constructed";
       }

       @Override
       public Product GetProduct() {
              return _product;
       }
}

class Product {
      
       public String Part1 = "";
       public String Part2 = "";
       public String Part3 = "";
}

public class BuilderPattern {
       public static void main(String[] args) {
              Director director = new Director();
              Builder builder = new ConcreteBuilder();
              director.Construct(builder);
             
              Product product = builder.GetProduct();
              System.out.println("Product Details : " + product.Part1 + " " + product.Part2 + " " + product.Part3);
       }
      
}



Object Pool Design Pattern

Object Pool pattern offer a mechanism to reuse and share objects that are expensive to create. We'll use an object pool pattern whenever there are several clients who needs the same stateless resource which is expensive to create.
UML Diagram:
Object Pool Design Pattern

Object Pool Design Pattern


 



Reusable - Wraps the limited resource, will be shared by several clients for a limited amount of time.
Client - uses an instance of type Reusable.
ReusablePool - manage the reusable objects for use by Clients, creating and managing a pool of objects.
When a client asks for a Reusable object, the pool performs the following actions:
-    Search for an available Reusable object and if it was found it will be returned to the client.
-    If no Reusable object was found then it tries to create a new one. If this actions succeeds the new Reusable object will be returned to the client.
-    If the pool was unable to create a new Reusable, the pool will wait until a reusable object will be released.
The Client is responsible to request the Reusable object as well to release it to the pool. If this action will not be performed the Reusable object will be lost, being considered unavailable by the Object Pool. The Reusable Pool is implemented as a singleton. The reusable pool should be accessible only to the Client object.
In this pattern main point is, Client should mark the Reusable object as available, after it finishes to use it. It's not about releasing the objects or destroying the object.
For example, if we work with databases, when a connection is closed it's not necessarily destroyed, it means that it can be reused by another client.

Object Pool Design Pattern
Connection represent the object which is instantiated by the client. From the client perspective this object is instantiated and it handles the database operations and it is the only object visible to the client. The client is not aware that it uses  some shared connections. Internally this class does not contain any code for connecting to the database and calls ConnectionPool.aquireImpl() to get a ConnectionImpl object and then delegates the request to ConnectionImpl.

ConnectionImpl is the object which implements the database operations which are exposed by Connection for the client.

ConnectionPool is the main actor to manage the connections to the database. It keeps a list of ConnectionImpl objects and instantiates new objects if this is required.

Problem and Solution in Object Pool Pattern:
1. Limited number of resources in the pool
The number of resources (Reusable Pool) can't exceed a specific limit. In this case the Resource/Object pool check the number of instantiated resources and of the limit is reach it will wait for a resource to be released,  it will throw an exception or it will return a null value.
2. Handling situations when creating a new resource fails

There are many reasons when the ResourcePool.acquireConnectionImpl() method fails to return a resource. It might happens because there are not available resources or some exception occurred. Either way the client should be notified about his.

3. Synchronization

In order to work in a multithreading environment the methods that are used by different threads should be synchronized. There are only three methods in the ResourcePool object that have to be synchronized:
-    getInstance should be synchronized or should contain a synchronized block. For details check the singleton multithreading implementation.
-    acquireConnectionImpl - this method returns a resource and should be synchronized not to return the same resource to two different clients running tin different threads.
-    releaseConnectionImpl - this method release a resource. Usually it doesn't have to be synchronized a resource is allocated only by one client. Internally some blocks might need to be synchronized(depending on the method implementation and the internal structures used to keep the pool.).


4. Expired resources(unused but still reserved)

The main problem for the Object Pool Pattern is that the objects should be released by the client when it finishes using them. There are plenty of examples when the client ”forget” to release the resources. Let's take the example the the database connections when connection are not closed/released after they are used. This seems a minor problem but there are many applications crushing for this reason.

In object pool can be implemented a mechanism to check when a specific resource was used last time and if the time expired, to return it to the available resource pool.


5. Hot Points
-    When the Object Pool pattern is used the objects should be marked as available(released) by the client after they are used, so the pool will be aware about this. This is the main drawback because the client should do this and it's a common situation when database connection are not released after they are used. To overcome this a mechanism can be implemented to release resources if they are not used for a period of time.
-    Creating the resources might fail and this case should be treated carefully. When there is no available resource(because the number is limited or creating a new one failed) the client should be notified about it.

Code Snippet:
public abstract class ObjectPool {
      
       private long expirationTime;

       private Hashtable locked, unlocked;

       public ObjectPool() {
              expirationTime = 30000; // 30 seconds
              locked = new Hashtable();
              unlocked = new Hashtable();
       }

       protected abstract T create();

       public abstract boolean validate(T o);

       public abstract void expire(T o);

       public synchronized T checkOut() {
              long now = System.currentTimeMillis();
              T t;
              if (unlocked.size() > 0) {
                     Enumeration e = unlocked.keys();
                     while (e.hasMoreElements()) {
                           t = e.nextElement();
                           if ((now - unlocked.get(t)) > expirationTime) {
                                  // object has expired
                                  unlocked.remove(t);
                                  expire(t);
                                  t = null;
                            } else {
                                  if (validate(t)) {
                                         unlocked.remove(t);
                                         locked.put(t, now);
                                         return (t);
                                  } else {
                                         // object failed validation
                                         unlocked.remove(t);
                                         expire(t);
                                         t = null;
                                  }
                           }
                     }
              }
              // no objects available, create a new one
              t = create();
              locked.put(t, now);
              return (t);
       }

       public synchronized void checkIn(T t) {
              locked.remove(t);
              unlocked.put(t, System.currentTimeMillis());
       }
}



public class JDBCConnectionPool extends ObjectPool {

       private String dsn, usr, pwd;

       public JDBCConnectionPool(String driver, String dsn, String usr, String pwd) {
              super();
              try {
                     Class.forName(driver).newInstance();
              } catch (Exception e) {
                     e.printStackTrace();
              }
              this.dsn = dsn;
              this.usr = usr;
              this.pwd = pwd;
       }

       @Override
       protected Connection create() {
              try {
                     return (DriverManager.getConnection(dsn, usr, pwd));
              } catch (SQLException e) {
                     e.printStackTrace();
                     return (null);
              }
       }

       @Override
       public void expire(Connection o) {
              try {
                     ((Connection) o).close();
              } catch (SQLException e) {
                     e.printStackTrace();
              }
       }

       @Override
       public boolean validate(Connection o) {
              try {
                     return (!((Connection) o).isClosed());
              } catch (SQLException e) {
                     e.printStackTrace();
                     return (false);
              }
       }
}

public class MainObjectPool {

       /**
        * @param args
        */
       public static void main(String[] args) {
             
              // Create the ConnectionPool:
           JDBCConnectionPool pool = new JDBCConnectionPool(
             "org.hsqldb.jdbcDriver", "jdbc:hsqldb://localhost/mydb",
             "sa", "secret");

           // Get a connection:
           Connection con = pool.checkOut();

           // Use the connection
          /*
           * Do some work
           */

           // Return the connection:
           pool.checkIn(con);
       }

}









Structural patterns


Adapter Design Pattern


Concept:
Adapter is about creating an intermediary abstraction that translates, or maps, the old component to the new system. Clients call methods on the Adapter object which redirects them into calls to the legacy component. This strategy can be implemented either with inheritance or with aggregation.
Adapter functions as a wrapper or modifier of an existing class. It provides a different or translated view of that class.
·  Wrap an existing class with a new interface.
·  Impedance match an old component to a new system
Real world example: Different Power Socket, Memory chip (Mobile or Camera) adapter.
How to Implement: Adapter design pattern can be implemented in two ways. One using the inheritance method and second using the composition method. Just the implementation methodology is different but the purpose and solution is same.

Example with Inheritance:

class CylindricalSocket {
       public String supply(String cylinStem1, String cylinStem2) {
              return("Power power power...");
       }
}

class RectangularAdapter extends CylindricalSocket {
       public String adapt(String rectaStem1, String rectaStem2) {
              // some conversion logic
              String cylinStem1 = rectaStem1;
              String cylinStem2 = rectaStem2;
              return supply(cylinStem1, cylinStem2);
       }
}

public class Adapter {
       private String rectaStem1;
       private String rectaStem2;

       public void getPower() {
              RectangularAdapter adapter = new RectangularAdapter();
              String power = adapter.adapt(rectaStem1, rectaStem2);
              System.out.println(power);
       }
}

Example with Composition:
class CylindricalSocket {
       public String supply(String cylinStem1, String cylinStem2) {
              return ("Power power power...");
       }
}

class RectangularAdapter {
       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 Adapter {
       private String rectaStem1;
       private String rectaStem2;

       public void getPower() {
              RectangularAdapter adapter = new RectangularAdapter();
              String power = adapter.adapt(rectaStem1, rectaStem2);
              System.out.println(power);
       }
}

Basic UML Diagram:
Adapter Design Pattern

  • Target
    • Defines domains-specific interface client uses.
  • Client
    • Collaborates with objects conforming to target interface.
  • Adaptee
    • Defines existing interface that needs adapting
  • Adapter 
    • Adapts the interface of adaptee to target interface. 

Example:
Adapter Design Pattern


Code Snippet:
//Adapter class between Intermediate interface and specific class
public class Adapter implements PrintableList {
      
       @Override
       public void printList(ArrayList list) {

              // Converting ArrayList to String so that we can pass String to
              // adaptee class
              String listString = "";

              for (String s : list) {
                     listString += s + "\t";
              }

              // instantiating adaptee/specific class
              PrintString printString = new PrintString();
              printString.print(listString);
       }

}

// Specific Class type
class PrintString {

       public void print(String s) {
              System.out.println(s);
       }
}
//Intermediate interface
public interface PrintableList {
       void printList(ArrayList list);
}

public class MainAdapter {

       public static void main(String[] args) {
             
              ArrayList list = new ArrayList();
              list.add("one");
              list.add("two");
              list.add("three");
              PrintableList pl = new Adapter();
              pl.printList(list);

       }

}



Bridge Design Pattern


Decouple an abstraction from its implementation so that the two can vary independently” is the intent for bridge design pattern as stated by GoF.
Decouple implantation from interface and hiding implementation details from client is the essence of bridge design pattern.
Generic UML Diagram for Bridge Design Pattern:

Bridge Design Pattern

Simple Example:
Before Applying bridge design pattern:

After applying bridge design pattern:

UML diagram for example:

Comparing to above generic description of bridge pattern.
In example includes following elements:
  • Shape(Abstraction)
  • Rectangle(RefinedAbstraction)
  • Circle(RefinedAbstraction)
  • Color(Implementor)
  • RedColor(ConcreteImplementor)
  • BlueColor(ConcreteImplementor)
Real Example:
Graphical User Interface (GUI) Frameworks use the bridge pattern to separate abstractions from platform specific implementation. For example GUI frameworks separate a Window abstraction from a Window implementation for Linux or Mac OS using the bridge pattern.
Use the Bridge pattern when:
  • You want run-time binding of the implementation,
  • You have a proliferation of classes resulting from a coupled interface and numerous implementations,
  • You want to share an implementation among multiple objects,
  • You need to map orthogonal class hierarchies.
Advantage of using Bridge Pattern:
  • Decoupling the object’s interface,
  • Improved extensibility (you can extend (i.e. subclass) the abstraction and implementation hierarchies independently),
  • Hiding details from clients.
Bridge Vs Adapter Design Pattern
The adapter design pattern helps it two incompatible classes to work together. But, bridge design pattern decouples the abstraction and implementation by creating two different hierarchies.

Code Snippet:
public class Bridge {

       public static void main(String arg[]) {

              Shape s1 = new Rectangle(new RedColor());
              s1.colorIt();
              Shape s2 = new Circle(new BlueColor());
              s2.colorIt();
       }
}

interface MyColor {

       public void fillColor();
}

abstract class Shape {

       MyColor color;

       Shape(MyColor color) {
              this.color = color;
       }

       abstract public void colorIt();
}

class Rectangle extends Shape {

       Rectangle(MyColor color) {
              super(color);
       }

       public void colorIt() {
              System.out.print("Rectangle filled with ");
              color.fillColor();
       }
}

class Circle extends Shape {

       Circle(MyColor color) {
              super(color);
       }

       public void colorIt() {
              System.out.print("Circle filled with ");
              color.fillColor();
       }
}

class RedColor implements MyColor {

       public void fillColor() {
              System.out.println("red color");
       }
}

class BlueColor implements MyColor {

       public void fillColor() {
              System.out.println("blue color");
       }
}



Composite Design Pattern


“Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.” is the intent by GoF.
[part-whole hierarchy: A system consists of subsystems or components. Components can further be divided into smaller components. Further smaller components can be divided into smaller elements. This is a part-whole hierarchy.]
In Composite pattern, it is represented as Tree structure. In tree structure, there are two types of node - either Composite node or Leaf node. Composite node means, it can have another composite and/or leaf node(i.e. object). Leaf Node means, it does not have any other node (no further object under this end node/ leaf node). Like below figure:

Composite Pattern

Composite Pattern is used due to this following reason:
  • It  represent part-whole hierarchies of objects.
  • Client to be able to ignore difference between compositions of objects and individual objects. Clients will treat all objects in the composite structure uniformly.
UML Diagram:

Elements of this UML Diagram:
  • Component
    • Declares interface for objects in composition.
    • Implements default behaviour for the interface common to all classes as appropriate.
    • Declares an interface for accessing and managing its child components.
    • Component is at the top of hierarchy. It is an abstraction for the composite.
  • Leaf
    • Represents leaf objects in the composition. A leaf has no children.
    • Defines behaviour for primitive/single objects in the composition.
  • Composite
    • Defines behaviour for components having children.
    • Stores child components.
    • Implements child related operations in the component interface.
  • Client
    • Manipulates objects in the composition through the component interface.
    • Clients access the whole hierarchy through the components and they are not aware about if they are dealing with leaf or composites.
Disadvantages:
  • Once tree structure is defined, composite design makes tree overly general.
  • Leaf class have to create some methods which has to empty.
Usage in JDK:
  • java.util.Map#putAll(Map)
  • java.util.List#addAll(Collection)
  • java.util.Set#addAll(Collection)
  • java.nio.ByteBuffer#put(ByteBuffer) (also on CharBuffer, ShortBuffer, IntBuffer, LongBuffer, FloatBuffer and DoubleBuffer)
  • java.awt.Container#add(Component) 
Example of Composite Pattern:
UML Diagram:

Legend of above UML diagram:
  • Manager(Composite)
  • Developer(Leaf)
  • Employee(Component)
Code Snippet:
public class Composite {
      
       public static void main(String arg[]) {
              Employee emp1 = new Developer("John", 10000);
              Employee emp2 = new Developer("David", 15000);
              Employee manager1 = new Manager("Daniel", 25000);
              manager1.add(emp1);
              manager1.add(emp2);
              Employee emp3 = new Developer("Michael", 20000);
              Manager generalManager = new Manager("Mark", 50000);
              generalManager.add(emp3);
              generalManager.add(manager1);
              generalManager.print();
       }
}

interface Employee {

       public void add(Employee employee);

       public void remove(Employee employee);

       public Employee getChild(int i);

       public String getName();

       public double getSalary();

       public void print();
}

class Manager implements Employee {

       private String name;
       private double salary;

       public Manager(String name, double salary) {
              this.name = name;
              this.salary = salary;
       }

       List employees = new ArrayList();

       public void add(Employee employee) {
              employees.add(employee);
       }

       public Employee getChild(int i) {
              return employees.get(i);
       }

       public String getName() {
              return name;
       }

       public double getSalary() {
              return salary;
       }

       public void print() {
              System.out.println("++++++++++++");
              System.out.println("Name =" + getName());
              System.out.println("Salary =" + getSalary());
              System.out.println("-------------");

              Iterator employeeIterator = employees.iterator();
              while (employeeIterator.hasNext()) {
                     Employee employee = employeeIterator.next();
                     employee.print();
              }
       }

       public void remove(Employee employee) {
              employees.remove(employee);
       }

}

class Developer implements Employee {

       private String name;
       private double salary;

       public Developer(String name, double salary) {
              this.name = name;
              this.salary = salary;
       }

       public void add(Employee employee) {
              // this is leaf node so this method is not applicable to this class.
       }

       public Employee getChild(int i) {
              // this is leaf node so this method is not applicable to this class.
              return null;
       }

       public String getName() {
              return name;
       }

       public double getSalary() {
              return salary;
       }

       public void print() {
              System.out.println("+++++++++++++");
              System.out.println("Name =" + getName());
              System.out.println("Salary =" + getSalary());
              System.out.println("-------------");
       }

       public void remove(Employee employee) {
              // this is leaf node so this method is not applicable to this class.
       }
}





Decorator Design Pattern

In this pattern is to add additional responsibilities dynamically to an object.
Extending an object's functionality can be done statically (at compile time) by using inheritance. However it might be necessary to extend an object's functionality dynamically (at runtime) as an object is used. It will extend functionality of object without affecting any other object. Decorators provide a alternative to sub classing for extending functionality.
Example: Adding a frame to the graphical window, would require extending the window class to create a FramedWindow class. To create a framed window it is necessary to create an object of the FramedWindow class.
Example in Java API: java.io.BufferedReader, java.io.FileReader, java.io.Reader
UML Diagram:

  • Component
    • defines interface for objects that can have additional responsibilities added to them.
  • ConcreteComponent
    • defines an object on which additional responsibilities  can be added
  • Decorator
    • maintains a reference to component object and defines an interface that conforms to component's interface.In simple words,It has a component object to be decorated.
  • ConcreteDecorator
    • add responsibilities to the component object.
Example:
UML Diagram:
Room(Component)
  • It is an interface which creates a blue print for the class which will have decorators
SimpleRoom(ConcreteComponent)
  • Object of SimpleRoom class will be decorated.Additional responsibilities will be attached to it dynamically.
RoomDecorator(Decorator)
  • It contains reference to Room class which will be decorated.
ColorDecorator(ConcreteDecorator)
  • ColorDecorator will add additional responsibility i.e.add color to room.
CurtainDecorator(ConcreteDecorator)
  • CurtainDecorator will add  additional responsibility i.e. add curtains to room.
Code Snippet:
public class Decorator {

       public static void main(String arg[]) {
              Room room = new CurtainDecorator(new ColorDecorator(new SimpleRoom()));
              System.out.println(room.showRoom());
       }
}

interface Room {
       public String showRoom();
}

class SimpleRoom implements Room {

       @Override
       public String showRoom() {
              return "Normal Room";
       }

}

abstract class RoomDecorator implements Room {

       protected Room specialRoom;

       public RoomDecorator(Room specialRoom) {
              this.specialRoom = specialRoom;
       }

       public String showRoom() {
              return specialRoom.showRoom();
       }
}

class ColorDecorator extends RoomDecorator {

       public ColorDecorator(Room specialRoom) {
              super(specialRoom);
       }

       public String showRoom() {
              return specialRoom.showRoom() + addColors();
       }

       private String addColors() {
              return " + Blue Color";
       }
}

class CurtainDecorator extends RoomDecorator {

       public CurtainDecorator(Room specialRoom) {
              super(specialRoom);
       }

       public String showRoom() {
              return specialRoom.showRoom() + addCurtains();
       }

       private String addCurtains() {
              return " + Red Curtains";
       }
}

Advantages of decorator design pattern: 
  • It is flexible than inheritance because inheritance adds responsibility at compile time but decorator pattern adds at run time.
  • We can have any number of decorators and also in any order.
  • It extends functionality of object without affecting any other object.
Disadvantage of decorator design pattern:
Main disadvantage of decorator design pattern is code maintainability because this pattern creates lots of similar decorator which are sometimes hard to maintain and distinguish.



Behavioral Pattern

Observer Design Pattern

Described by GoF:
"Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically".
Observer pattern is used for observing some objects. Observer watch for any change in state or property of subject.

Subject-Observer relationship: Object which is being observed is refereed as Subject and classes which observe subject are called Observer.
The Observers will not monitor every time whether there is any change in state of Subject or not, since they will be notified for every state change of Subject, until they stop observing Subject.

Principle of Observer Pattern:
  
  1. One object changes its state, then all other dependents object must automatically change their state to maintain consistency.
  2. Subject doesn't know about total number of observers it has.
  3. An object should be able to notify other objects without knowing who objects are.
Components:
Subject
  • Knows its observers
  • Has any number of observer
  • Provides an interface to attach and detaching observer object at run time
Observer
  • Provides an update interface to receive signal from subject
Concrete Subject
  • Stores state of interest to Concrete Observer objects.
  • Send notification to its observer
Concrete Observer
  • Maintains reference to a Concrete Subject object
  • Maintains observer state consistent with subjects.
  • Implements update operation
The java API provides one class and one interface for implementing observer pattern.
   1. java.util.Observable << class >>
   2. java.util.Observer << interface >>

Example in Java:

import java.util.Observable;
import java.util.Observer;


public class MainClass {

       public static void main(String[] args) {
              Message messageObserve = new Message();
             
              Student student = new Student();
              Teacher teacher = new Teacher();
             
              // Register with Observable super class
              messageObserve.addObserver(student);
              messageObserve.addObserver(teacher);
             
              messageObserve.setMessage("New Message Come !!!");
             
              // Unregister from Observable class
              messageObserve.deleteObserver(student);
             
              messageObserve.setMessage("New Message Come AGAIN !!!");
       }

}


class Student implements Observer {

       @Override
       public void update(Observable arg0, Object arg1) {
              // This method will call whenever the setChanged() and notifyObservers()
             // call from Observable class
              System.out.println("Message Updated in Student class : " + arg1.toString());
       }
}

class Teacher implements Observer {

       @Override
       public void update(Observable arg0, Object arg1) {
              // This method will call whenever the setChanged() and notifyObservers()
              // call from Observable class
              System.out.println("Message Updated in Teacher class : " + arg1.toString());
       }
}

class Message extends Observable {

       private String message;

       public String getMessage() {
              return message;
       }

       public void setMessage(String message) {
              this.message = message;
             // This method call means, after certain property has changed on the object.
              setChanged(); 
             // This will notify all the Observer class and call update() method.
              notifyObservers(message); 
       }
}

OUTPUT in Console:
Message Updated in Teacher class : New Message Come !!!
Message Updated in Student class : New Message Come !!!
Message Updated in Teacher class : New Message Come AGAIN !!!

No comments:

Post a Comment