Design Pattern
Design Pattern
Design Pattern
Many programmers with many years experience don't know design patterns, but as an
Object-Oriented programmer, you have to know them well, especially for new Java
programmers. Actually, when you solved a coding problem, you have used a design
pattern. You may not use a popular name to describe it or may not choose an effective
way to better intellectually control over what you built. Learning how the experienced
developers to solve the coding problems and trying to use them in your project are a best
way to earn your experience and certification.
Remember that learning the design patterns will really change how you design your code;
not only will you be smarter but will you sound a lot smarter, too.
Note that the design patterns are not idioms or algorithms or components.
What is the relationship among these patterns?
Generally, to build a system, you may need many patterns to fit together. Different
designer may use different patterns to solve the same problem. Usually:
References
Design Patterns -- Elements of Reusable Object-Oriented Software by GOF.
Abstract Factory
Definition
Provides one level of interface higher than the factory pattern. It is used to return one of
several factories.
Example
Suppose you need to write a program to show data in two different places. Let's say from
a local or a remote database. You need to make a connection to a database before
working on the data. In this case, you have two choices, local or remote. You may use
abstract factory design pattern to design the interface in the following way:
class DataInfo {}
interface Local {
DataInfo[] loadDB(String filename);
}
}
// work on data
Such design is often used in SCJD project assignment. If you have a multiple places to
load data, you just add more methods in the connection interface without altering other
structure, or add a location variable in.
Builder
Definition
Construct a complex object from simple objects step by step.
Example
To build a house, we will take several steps:
1. build foundation,
2. build frame,
3. build exterior,
4. build interior.
Let's use an abstract class HouseBuilder to define these 4 steps. Any subclass of
HouseBuilder will follow these 4 steps to build house (that is to say to implement these 4
methods in the subclass). Then we use a WorkShop class to force the order of these 4
steps (that is to say that we have to build interior after having finished first three steps).
The TestBuilder class is used to test the coordination of these classes and to check the
building process.
import java.util.*;
class WorkShop {
//force the order of building process
public void construct(HouseBuilder hb) {
hb.buildFoundation();
hb.buildFrame();
hb.buildExterior();
hb.buildInterior();
}
}
public House() {
class TestBuilder {
C:\>
To fine tune the above example, every do method can be designed as a class. Similar
functional class can be designed once and used by other classes. e.g. Window, Door,
Kitchen, etc.
Another example, such as writing a Pizza program. Every gradient can be designed as a
class. One pizza at least consists of several gradients. Different pizza has different
gradients. A builder pattern may be adopted.
Factory Method
Definition
Provides an abstraction or an interface and lets subclass or implementing classes decide
which class or method should be instantiated or called, based on the conditions or
parameters given.
Examples
To illustrate such concept, let's use a simple example. To paint a picture, you may need
several steps. A shape is an interface. Several implementing classes may be designed in
the following way.
interface Shape {
public void draw();
}
class Line implements Shape {
Point x, y;
Line(Point a, Point b) {
x = a;
y = b;
}
public void draw() {
//draw a line;
}
}
class Square implements Shape {
Point start;
int width, height;
Square(Point s, int w, int h) {
start = s;
width = w;
height = h;
}
public void draw() {
//draw a square;
}
}
class Circle implements Shape {
....
}
class Painting {
Point x, y;
int width, height, radius;
Painting(Point a, Point b, int w, int h, int r) {
x = a;
y = b;
width = w;
height = h;
radius = r;
}
Shape drawLine() {
return new Line(x,y);
}
Shape drawSquare() {
return new Square(x, width, height);
}
Shape drawCircle() {
return new Circle(x, radius);
}
....
}
...
Shape pic;
Painting pt;
//initializing pt
....
if (line)
pic = pt.drawLine();
if (square)
pic = pt.drawSquare();
if (circle)
pic = pt.drawCircle();
From the above example, you may see that the Shape pic's type depends on the condition
given. The variable pic may be a line or square or a circle.
You may use several constructors with different parameters to instantiate the object you
want. It is another way to design with Factory pattern. For example,
class Painting {
...
Painting(Point a, Point b) {
new Line(a, b); //draw a line
}
Painting(Point a, int w, int h) {
new Square(a, w, h); //draw a square
}
Painting(Point a, int r){
new Circle(a, r); //draw a circle
}
...
}
You may use several methods to finish the drawing jobs. It is so-called factory method
pattern. for example,
class Painting {
...
Painting(Point a, Point b) {
draw(a, b); //draw a line
}
Painting(Point a, int w, int h) {
draw(a, w, h); //draw a square
}
Painting(Point a, int r){
draw(a, r); //draw a circle
}
...
}
Here is a popular example of Factory design pattern. For example, you have several
database storages located in several places. The program working on the database is the
same. The user may choose local mode or remote mode. The condition is the choice by
the user. You may design your program with Factory pattern. When the local mode is set,
you may instantiate an object to work on the local database. If the remote mode is set,
you may instantiate an object which may have more job to do like remote connection,
downloading, etc.
interface DatabaseService {
public DataInfo getDataInfo() throws Exception;
public FieldInfo getFieldInfo() throws Exception;
public void write(FieldInfo fi) throws Exception;
public void modify(FieldInfo fi) throws Exception;
public void delete(FieldInfo fi) throws Exception;
//...
}
class Data implements DatabaseService {
To illustrate how to use factory design pattern with class level implementation, here is a
real world example. A company has a website to display testing result from a plain text
file. Recently, the company purchased a new machine which produces a binary data file,
another new machine on the way, it is possible that one will produce different data file.
How to write a system to deal with such change. The website just needs data to display.
Your job is to provide the specified data format for the website.
Here comes a solution. Use an interface type to converge the different data file format.
The following is a skeleton of implementation.
//load a file
public void load(String fileName);
C:\>java TestFactory 1
load from a txt file
txt file format changed
C:\>java TestFactory 2
load from an xml file
xml file format changed
C:\>java TestFactory 3
load from a db file
db file format changed
In the future, the company may add more data file with different format, a programmer
just adds a new class in accordingly. Such design saves a lot of code and is easy to
maintain.
Prototype
Definition
Cloning an object by reducing the cost of creation.
Where to use & benefits
• When there are many subclasses that differ only in the kind of objects,
• A system needs independent of how its objects are created, composed, and
represented.
• Dynamic binding or loading a method.
• Use one instance to finish job just by changing its state or parameters.
• Add and remove objects at runtime.
• Specify new objects by changing its structure.
• Configure an application with classes dynamically.
• Related patterns include
o Abstract Factory, which is often used together with prototype. An abstract
factory may store some prototypes for cloning and returning objects.
o Composite, which is often used with prototypes to make a part-whole
relationship.
o Decorator, which is used to add additional functionality to the prototype.
Example
Dynamic loading is a typical object-oriented feature and prototype example. For
example, overriding method is a kind of prototype pattern.
interface Shape {
public void draw();
}
class Line implements Shape {
public void draw() {
System.out.println("line");
}
}
class Square implements Shape {
public void draw() {
System.out.println("square");
}
}
class Circle implements Shape {
public void draw() {
System.out.println("circle");
}
}
class Painting {
public static void main(String[] args) {
Shape s1 = new Line();
Shape s2 = new Square();
Shape s3 = new Circle();
paint(s1);
paint(s2);
paint(s3);
}
static void paint(Shape s) {
s.draw();
}
}
----------------------------
If we want to make code more readable or do more stuff, we can code the
paint method in the following way:
The paint method takes a variable of Shape type at runtime. The draw method is called
based on the runtime type.
class Painting {
public void draw(Point p, Point p2) {
//draw a line
}
public void draw(Point p, int x, int y) {
//draw a square
}
public void draw(Point p, int x) {
//draw a circle
}
}
The draw method is called to draw the related shape based on the parameters it takes.
The prototype is typically used to clone an object, i.e. to make a copy of an object. When
an object is complicated or time consuming to be created , you may take prototype
pattern to make such object cloneable. Assume the Complex class is a complicated, you
need to implement Cloneable interface and override the clone method(protected Object
clone()).
Cloning is a shallow copy of the original object. If the cloned object is changed, the
original object will be changed accordingly. See the following alteration.
To avoid such side effect, you may use a deep copy instead of a shallow copy. The
following shows the alteration to the above example, note that the Complex class doesn't
implement Cloneable interface.
class Complex {
int[] nums = {1,2,3,4,5};
public Complex clone() {
return new Complex();
}
int[] getNums() {
return nums;
}
}
class Test2 {
Complex c1 = new Complex();
Complex makeCopy() {
return (Complex)c1.clone();
}
public static void main(String[] args) {
Test2 tp = new Test2();
Complex c2 = tp.makeCopy();
int[] mycopy = c2.getNums();
mycopy[0] = 5;
System.out.println();
System.out.print("local array: ");
for(int i = 0; i < mycopy.length; i++)
System.out.print(mycopy[i]);
System.out.println();
Singleton
Definition
One instance of a class or one value accessible globally in an application.
Example
One file system, one window manager, one printer spooler, one Test engine, one
Input/Output socket and etc.
To design a Singleton class, you may need to make the class final like java.Math, which
is not allowed to subclass, or make a variable or method public and/or static, or make all
constructors private to prevent the compiler from creating a default one.
For example, to make a unique remote connection,
usage:
RemoteConnection rconn = RemoteConnection.getRemoteConnection;
rconn.loadData();
...
class Connection {
public static boolean haveOne = false;
public Connection() throws Exception{
if (!haveOne) {
doSomething();
haveOne = true;
}else {
throw new Exception("You cannot have a second instance");
}
}
public static Connection getConnection() throws Exception{
return new Connection();
}
void doSomething() {}
//...
public static void main(String [] args) {
try {
Connection con = new Connection(); //ok
}catch(Exception e) {
System.out.println("first: " +e.getMessage());
}
try {
Connection con2 = Connection.getConnection(); //failed.
}catch(Exception e) {
System.out.println("second: " +e.getMessage());
}
}
}
C:\ Command Prompt
C:\> java Connection
second: You cannot have a second instance
class Employee {
public static final int companyID = 12345;
public String address;
//...
}
class HourlyEmployee extends Employee {
public double hourlyRate;
//....
}
class SalaryEmployee extends Employee {
public double salary;
//...
}
class Test {
public static void main(String[] args) {
Employee Evens = new Employee();
HourlyEmployee Hellen = new HourlyEmployee();
SalaryEmployee Sara = new SalaryEmployee();
System.out.println(Evens.companyID == Hellen.companyID); //true
System.out.println(Evens.companyID == Sara.companyID); //true
}
}
C:\ Command Prompt
C:\> java Test
true
true
loader. If you use the same class across multiple distinct enterprise
containers, you'll get one instance for each container.
Adapter
Definition
Convert the existing interfaces to a new interface to achieve compatibility and reusability
of the unrelated classes in one application. Also known as Wrapper pattern.
Example
The famous adapter classes in Java API are WindowAdapter,ComponentAdapter,
ContainerAdapter, FocusAdapter, KeyAdapter, MouseAdapter and
MouseMotionAdapter.
As you know, WindowListner interface has seven methods. Whenever your class
implements such interface, you have to implements all of the seven methods.
WindowAdapter class implements WindowListener interface and make seven empty
implementation. When you class subclass WindowAdapter class, you may choose the
method you want without restrictions. The following give such an example.
public interface Windowlistener {
public void windowClosed(WindowEvent e);
public void windowOpened(WindowEvent e);
public void windowIconified(WindowEvent e);
public void windowDeiconified(WindowEvent e);
public void windowActivated(WindowEvent e);
public void windowDeactivated(WindowEvent e);
public void windowClosing(WindowEvent e);
}
public class WindowAdapter implements WindowListner{
public void windowClosed(WindowEvent e){}
public void windowOpened(WindowEvent e){}
public void windowIconified(WindowEvent e){}
public void windowDeiconified(WindowEvent e){}
public void windowActivated(WindowEvent e){}
public void windowDeactivated(WindowEvent e){}
public void windowClosing(WindowEvent e){}
}
import javax.swing.*;
import java.awt.event.*;
class Test extends JFrame {
public Test () {
setSize(200,200);
setVisible(true);
addWindowListener(new Closer());
}
public static void main(String[] args) {
new Test();
}
class Closer extends WindowAdapter {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
}
}
To reuse classes and make new class compatible with existing ones. For example, A
clean system is already designed, you want to add more job in, the Extra interface uses
adapter pattern to plug in the existing system.
interface Clean {
public void makeClean();
}
class Office implements Clean{
public void makeClean() {
System.out.println("Clean Office");
}
}
class Workshop implements Clean{
public void makeClean() {
System.out.println("Clean Workshop");
}
}
class Test {
static void Jobs (Extra job) {
if (job instanceof Clean)
((Clean)job).makeClean();
if (job instanceof Extra)
((Extra)job).takeCare();
}
public static void main(String[] args) {
Extra e = new Facility();
Jobs(e);
Clean c1 = new Office();
Clean c2 = new Workshop();
c1.makeClean();
c2.makeClean();
e.makeClean();
}
}
C:\ Command Prompt
C:\> java Test
Clean Facility
Care has been taken
Clean Office
Clean Workshop
Clean Facility
By composition, we can achieve adapter pattern. It is also called wrapper. For example, a
Data class has already been designed and well tested. You want to adapt such class to
your system. You may declare it as a variable and wrapper or embed it into your class.
//well-tested class
class Data {
public void add(Info){}
public void delete(Info) {}
public void modify(Info){}
//...
}
//Use Data class in your own class
class AdaptData {
Data data;
public void add(Info i) {
data.add(i);
//more job
}
public void delete(Info i) {
data.delete(i);
//more job
}
public void modify(Info i) {
data.modify(i);
//more job
}
//more stuff here
//...
}
Bridge
Definition
Decouple an abstraction or interface from its implementation so that the two can vary
independently.
Examples
If you have a question database, you may want to develop a program to display it based
on the user selection. The following is a simple example to show how to use a Bridge
pattern to decouple the relationship among the objects.
import java.util.*;
//abstraction
interface Question {
//implementation
class QuestionManager {
//further implementation
class QuestionFormat extends QuestionManager {
//decoupled implementation
class JavaQuestions implements Question {
public JavaQuestions() {
//load from a database and fill in the container
questions.add("What is Java? ");
questions.add("What is an interface? ");
questions.add("What is cross-platform? ");
questions.add("What is UFT-8? ");
questions.add("What is abstract? ");
questions.add("What is Thread? ");
questions.add("What is multi-threading? ");
class TestBridge {
public static void main(String[] args) {
QuestionFormat questions = new QuestionFormat("Java Language");
questions.display();
questions.next();
questions.displayAll();
}
}
//need jdk1.5 to compile
C:\ Command Prompt
C:\> javac TestBridge.java
C:\> java TestBridge
What is Java?
~~~~~~~~~~~~~~~~~~~~~~~~
Question Catalog: Java Language
What is Java?
What is an interface?
What is cross-platform?
What is UFT-8?
What is abstract?
What is Thread?
What is multi-threading?
What is object?
What is reference type?
~~~~~~~~~~~~~~~~~~~~~~~~
C:\>
Note that the JavaQuestion class can be launched independently and work as its own
system. Here we just show you how to use Bridge pattern to decouple the interface from
its implementation.