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
Decorator
Design Pattern
Behavioral Pattern
Behavioral Pattern
Observer Design Pattern
To download Sample Example Code in GitHub click here.
Creational Pattern
Singleton Design Pattern
Definition:
- There should be only one
instance allowed for a class.
- 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:
- For multi-threading
environment, Multiple object may creation.
- If other class extends
Singleton class, then more than one object may created of Singleton class.
- For Serialization and De-Serialization of the Singleton class object
- Using clone() method, user can clone the Singleton object.
- 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 {
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.
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.
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.
- 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.
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 failsThere 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:
- 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:
- 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.
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:
- One object changes its
state, then all other dependents object must automatically change their
state to maintain consistency.
- Subject doesn't know about
total number of observers it has.
- 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;
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