Design Pattern
Design Patterns are a cornerstone of today's software development. Using design patterns, creation and maintainability of Java applications can be improved and their cost can be reduced. In 1994, four authors, Erich Gamma, Richard Helm, Ralph Johnson and John Vpissides, published a book titled "Design Patterns - Elements of Reusable Object-Oriented Software". This book introduced the concept of design patterns in software development.
These four authors are known as Gang of Four (GOF). According to them, design patterns are based on the following principles of Object Oriented Design(OOD):
- Program to an interface, not an implementation.
- Prefer object composition over inheritance
Some useful definitions of design patterns are:
- "Design Patterns are recurring solutions to design problems, you see over
and over"
- "Design Patterns constitute a set of rules describing how to accomplish
certain tasks in the realm of software development".
- •"Patterns identify and specify abstractions that are above the level of single classes and instances".
Types of Design Patterns
Authors divided design patterns into three types:
- Creational Patterns:Create objects rather than instantiating objects directly using new operator.
- Structural Patterns: Help to compose group of objects in the larger structures.
- Behavioral Patterns: Help to define communication between the objects and how flow is controlled.
The most commonly used patterns are subject of discussion in this book.
Factory Pattern
It is commonly used design pattern in Java. It comes under creational pattern. It provides a better way to create an object depending upon data provided to it. In this pattern, objects are created without disclosing the creational logic. It can return objects of different types using a common interface. The factory pattern is discussed below using the class diagram and program:
Class Diagram:
interface shape
{
void draw();
}
class circle implements shape
{
public void draw()
{
System.out.println("Drawing Circle");
}
}
class square implements shape
{
public void draw()
{
System.out.println("Drawing Square");
}
}
class rectangle implements shape
{
public void draw()
{
System.out.println("Drawing Rectangle");
}
}
class shapeFactory
{
shape getobject(String type)
{
if(type==null)
{
return (null);
}
else if(type.equals("circle"))
{
return(new circle());
}
else if(type.equals("square"))
{
return(new square());
}
else if(type.equals("rectangle"))
{
return(new rectangle());
}
else
{
return(null);
}
}
}
//Using the Factory Class
class FPUser
{
public static void main(String args[])
{
shapeFactory sf=new shapeFactory();
shape r=sf.getobject("rectangle");
r.draw();//Drawing Rectangle
sf.getobject("square").draw();//Drawing Square
circle c=(circle)sf.getobject("circle");
c.draw();//Drawing Circle
}
}
Output
Drawing Rectangle
Drawing Square
Drawing Circle
Explanation
In the above program, class ShapeFactory consists of a factory method getObject(), which accepts a string as an argument and returns object of the corresponding class. Its return type is Shape (an interface) that is implemented by Circle, Square and Rectangle classes, however this factory method can return reference of a class that implements interface according to its return type.
Abstract Factory Design Pattern
Abstract factory design pattern is a creational design pattern. It is an extension of factory pattern. It is considered as another layer of abstraction over factory pattern. It means that abstract factory returns the factory of classes, whereas factory method returns one of the various sub-classes. Consider the following class diagram for better understanding:
Class Diagram
According to the above class diagram, classes Cpp and Java implement Syllabus interface and classes CppAuthors and JavaAuthors, implement Authors interface. Factory classes, Syllabus Factory and Authors Factory, implement Syllabus Authors interface. Factory User class calls getFactory() method of the Factory Producer class to instantiate further factory classes.
Implementation
interface syllabus
{
void display();
}
class cpp implements syllabus
{
public void display()
{
System.out.println("Array,Pointer,Loops,Class");
}
}
class java implements syllabus
{
public void display()
{
System.out.println("Thread,Interface,Exception");
}
}
interface authors
{
void showlist();
}
class cppauthors implements authors
{
public void showlist()
{
System.out.println("Yashwant Kant, Ravichandran");
}
}
class javaauthors implements authors
{
public void showlist()
{
System.out.println("James Gosling , Rajesh Bansal");
}
}
interface syllabusauthors
{
syllabus getsyllabus(String subjects);
authors getauthors(String Book);
}
class syllabusfactory implements syllabusauthors
{
public syllabus getsyllabus(String Subject)
{
if(Subject.equals("cpp"))
{
return new cpp();
}
else if(subject.equals("java"))
{
return new java();
}
else
{
return null;
}
}
public authors getauthors(String Book)
{
return null;
}
}
class authorsfactory implements syllabusauthors
{
public authors getauthors(string book)
{
if(book.equals("cpp"))
{
return new cppauthors();
}
else
if(book.equals("java"))
{
return new javaauthors();
}
else
{
return null;
}
}
public syllabus getsyllabus(String Subject)
{
return null;
}
}
class factoryproducer
{
static syllabusauthors getfactory(String type)
{
if(type.equals("syllabus"))
return new syllabusfactory();
else if(type.equals("authors"))
return new authorsfactory();
else
return null;
}
}
public class factoryuser
{
syllabusauthors syl=factoryproducer.getfactory("syllabus");
syllabus cpp=syl.getsyllabus("cpp");
cpp.display();
syllabus java=syl.getsyllabus("java");
java.display();
syllabusauthors auth=factoryprocedure.getfactory("authors");
authors cpp_auth=auth.getauthors("cpp");
cpp_auth.showlist();
authors java_auth=auth.getauthors("cpp");
java_auth.showlist();
}
Output
Array, Pointers, Loops, Classes Threads, Interface, Exceptions Yashwant Kant., Ravichandran James Gosling, Rajesh Bansal
Adapter Design Pattern
The adapter design pattern is a structural pattern. It is used to make two unrelated interfaces work together. Some times a class library cannot be used because its interface is not compatible with the interface required by an application. In this case, you need to create an adapter class which makes them work together. An adapter class allows you to reuse the existing code without changing it. For example, your mobile phone charger plays the role of an adapter because it takes 220 volt supply and gives 9 volt supply to charge your mobile.
As the above class diagram shows, class Result acts as an adapter class which implements IResult interface. Now, class College can use Result class as an adapter to get percentage and grade. But class Result uses Total class to get total marks to find the percentage and grade of a student. Consider the following program to understand how adapter design pattern can be used to solve real life problems:
Singleton Design Pattern
Singleton design pattern is a creational design pattern. A singleton is simply a class which can be instantiated exactly once. There are two ways to implement singleton design pattern, both use private constructors and a public static field of class type that contains this field to provide access to the sole instance,
- In one approach, a final & static field of same class type is used as shown in the program below:
class userlibrary
{
public static final userlibrary lib=new library();
private userlibrary(){}
public double getsquare(double v)
{
return v*v;
}
public double getpi()
{
return 3.14;
}
public double getcylindervol(double r,double h)
{
return getpi()* getsquare(r)*h;
}
}
public class designptrn_singleton
{
public static void main(String[] args)
{
userlibrary lib=userlibrary.lib;
double sq=lib.getsquare(10);
double cylvol=lib.getcylindervol(10,20);
System.out.println("Square="+sq+"cyl . volume="+cylvol);
}
}
Output
Square=100.0 Cyl. Volume=6280.0
Explanation
In the above program, line number 14 generates error because UserLibrary class cannot be instantiated due to its private constructor. In line number 2, UserLibrary class is instantiated with static, final and public modifiers and its reference is obtained in main function() in line number 18 to call the methods getSquare() and getCylinder Vol().
2. In the second approach, a public & static factory method is used to return the reference of a UserLibrary class object which is declared as private, static and final, as shown in the program below:
class userlibrary
{
private static final userlibrary lib=new library();
private userlibrary(){}
public static userlibrary getinstance()
{
return lib;
}
public double getsquare(double v)
{
return v*v;
}
public double getpi()
{
return 3.14;
}
public double getcylindervol(double r,double h)
{
return getpi()* getsquare(r)*h;
}
}
public class designptrn_singleton
{
public static void main(String[] args)
{
userlibrary lib=userlibrary.getinstance;
double sq=lib.getsquare(10);
double cylvol=lib.getcylindervol(10,20);
System.out.println("Square="+sq+"cyl . volume="+cylvol);
}
}
Output
Square=100.0 Cyl. Volume=6280.0
Explanation
In the above program, in line number 3, lib is declared as a private field which refers to the instance of UserLibrary class. It is not accessible to the outside world, so a static factory method, getInstance(), is introduced to return its reference. Line number 21 calls getInstance() to obtain the reference for calling the methods that belong to such instance.