About Design Pattern What is the design pattern? If a problem occurs over and over again, a solution to that problem has been used effectively. That solution is described as a pattern. The design patterns are languageindependent strategies for solving common object-oriented design problems. When you make a design, you should know the names of some common solutions. Learning design patterns is good for people to communicate each other effectively. In fact, you may have been familiar with some design patterns, you may not use well-known names to describe them. SUN suggests GOF (Gang Of Four--four pioneer guys who wrote a book named "Design Patterns"- Elements of Reusable Object-Oriented Software), so we use that book as our guide to describe solutions. Please make you be familiar with these terms and learn how other people solve the code problems.
Do I have to use the design pattern? If you want to be a professional Java developer, you should know at least some popular solutions to coding problems. Such solutions have been proved efficient and effective by the experienced developers. These solutions are described as so-called design patterns. Learning design patterns speeds up your experience accumulation in OOA/OOD. Once you grasped them, you would be benefit from them for all your life and jump up yourselves to be a master of designing and developing. Furthermore, you will be able to use these terms to communicate with your fellows or assessors more effectively. 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.
How many design patterns? Many. A site says at least 250 existing patterns are used in OO world, including Spaghetti which refers to poor coding habits. The 23 design patterns by GOF are well known, and more are to be discovered on the way. 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: • • • • • •
Some patterns naturally fit together One pattern may lead to another Some patterns are similar and alternative Patterns are discoverable and documentable Patterns are not methods or framework Patterns give you hint to solve a problem effectively
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.
Where to use & benefits • • • • • • • •
Creates families of related or dependent objects like Kit. Provides a class library of products, exposing interface not implementation. Needs to isolate concrete classes from their super classes. A system needs independent of how its products are created, composed, and represented. Try to enforce a constraint. An alternative to Facade to hide platform-specific classes Easily extensible to a system or a family Related patterns include o Factory method, which is often implemented with an abstract factory. o Singleton, which is often implemented with an abstract factory. o Prototype, which is often implemented with an abstract factory. o Facade, which is often used with an abstract factory by providing an interface for creating implementing class.
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); } interface Remote extends Local{ void connect2WWW(String url); } class LocalMode implements Local { public DataInfo[] loadDB(String name) { System.out.print("Load from a local database "); return null; } } class RemoteMode implements Remote { public void connect2WWW(String url) { System.out.println("Connect to a remote site "); } public DataInfo[] loadDB(String name) { System.out.println("Load from a remote database "); return null; } } // The Abstract Factory interface ConnectionFactory { Local getLocalConnection(); Remote getRemoteConnection(); } class DataManager implements ConnectionFactory { boolean local = false; DataInfo[] data; //... public Local getLocalConnection() { return new LocalMode(); } public Remote getRemoteConnection() { return new RemoteMode(); }
public void loadData() { if(local){ Local conn = getLocalConnection(); data = conn.loadDB("db.db"); }else { Remote conn = getRemoteConnection(); conn.connect2WWW("www.some.where.com"); data = conn.loadDB("db.db"); } }
// work on data
public void setConnection(boolean b) { local = b; } } //Use the following Test class to test the above classes class Test { public static void main(String[] args) { DataManager dm = new DataManager(); DataInfo[] di = null; String dbFileName = "db.db"; if (args.length == 1) { //assume local is set to true dm.setConnection(true); LocalMode lm = (LocalMode)dm.getLocalConnection(); di = lm.loadDB(dbFileName); } else { //Note: dm.local = false is default setting RemoteMode rm = (RemoteMode)dm.getRemoteConnection(); rm.connect2WWW("www.javacamp.org/db/"); di = rm.loadDB(dbFileName); } //use one set of methods to deal with loaded data. //You don't need to worry about connection from this point. //Like di.find(), di.search() etc. } }
C:\ Command Prompt C:\> java Test Connect to a remote site Load from a remote database C:\ Command Prompt C:\> java Test local Load from a local database 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.
Where to use & benefits • • • • •
Make a complex object by specifying only its type and content. The built object is shielded from the details of its construction. Want to decouple the process of building a complex object from the parts that make up the object. Isolate code for construction and representation. Give you finer control over the construction process. Related patterns include o Abstract Factory, which focuses on the layer over the factory pattern (may be simple or complex), whereas a builder pattern focuses on building a complex object from other simple objects. o Composite, which is often used to build a complex object.
Example To build a house, we will take several steps: 1. 2. 3. 4.
build foundation, build frame, build exterior, 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();
} //set steps for building a house abstract class HouseBuilder { protected House house = new House(); protected String showProgress() { return house.toString(); } abstract abstract abstract abstract
public public public public
void void void void
buildFoundation(); buildFrame(); buildExterior(); buildInterior();
} class OneStoryHouse extends HouseBuilder { public OneStoryHouse(String features) { house.setType(this.getClass() + " " + features); } public void buildFoundation() { //doEngineering() //doExcavating() //doPlumbingHeatingElectricity() //doSewerWaterHookUp() //doFoundationInspection() house.setProgress("foundation is done"); } public void buildFrame() { //doHeatingPlumbingRoof() //doElectricityRoute() //doDoorsWindows() //doFrameInspection() house.setProgress("frame is done"); } public void buildExterior() { //doOverheadDoors() //doBrickWorks() //doSidingsoffitsGutters() //doDrivewayGarageFloor() //doDeckRail() //doLandScaping() house.setProgress("Exterior is done"); } public void buildInterior() { //doAlarmPrewiring() //doBuiltinVacuum() //doInsulation() //doDryWall() //doPainting() //doLinoleum()
//doCabinet() //doTileWork() //doLightFixtureBlinds() //doCleaning() //doInteriorInspection() house.setProgress("Interior is under going"); }
}
class TwoStoryHouse extends HouseBuilder { public TwoStoryHouse(String features) { house.setType(this.getClass() + " " + features); } public void buildFoundation() { //doEngineering() //doExcavating() //doPlumbingHeatingElectricity() //doSewerWaterHookUp() //doFoundationInspection() house.setProgress("foundation is done"); } public void buildFrame() { //doHeatingPlumbingRoof() //doElectricityRoute() //doDoorsWindows() //doFrameInspection() house.setProgress("frame is under construction"); } public void buildExterior() { //doOverheadDoors() //doBrickWorks() //doSidingsoffitsGutters() //doDrivewayGarageFloor() //doDeckRail() //doLandScaping() house.setProgress("Exterior is waiting to start"); }
}
public void buildInterior() { //doAlarmPrewiring() //doBuiltinVacuum() //doInsulation() //doDryWall() //doPainting() //doLinoleum() //doCabinet() //doTileWork() //doLightFixtureBlinds() //doCleaning() //doInteriorInspection() house.setProgress("Interior is not started yet"); }
class House { private String type = null; private List features = new ArrayList(); public House() { } public House(String type) { this.type = type; } public void setType(String type) { this.type = type; } public String getType() { return type; } public void setProgress(String s) { features.add(s); }
}
public String toString() { StringBuffer ff = new StringBuffer(); String t = type.substring(6); ff.append(t + "\n "); for (int i = 0; i < features.size(); i ++) { ff.append(features.get(i) + "\n "); } return ff.toString(); }
class TestBuilder
{
public static void main(String[] args) { HouseBuilder one = new OneStoryHouse("2 bedrooms, 2.5 baths, 2car garage, 1500 sqft"); HouseBuilder two = new TwoStoryHouse("4 bedrooms, 4 baths, 3-car garage, 5000 sqft"); WorkShop shop = new WorkShop(); shop.construct(one); shop.construct(two); System.out.println("Check house building progress: \n"); System.out.println(one.showProgress()); System.out.println(two.showProgress()); } } //need jdk1.5 above to compile
C:\ Command Prompt C:\> javac TestBuilder.java C:\> java TestBuilder Check house building progress: OneStoryHouse 2 bedrooms, 2.5 baths, 2-car garage, 1500 sqft foundation is done frame is done Exterior is done Interior is under going TwoStoryHouse 4 bedrooms, 4 baths, 3-car garage, 5000 sqft foundation is done frame is under construction Exterior is waiting to start Interior is not started yet 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.
Where to use & benefits • • • • • •
Connect parallel class hierarchies. A class wants its subclasses to specify the object. A class cannot anticipate its subclasses, which must be created. A family of objects needs to be separated by using shared interface. The code needs to deal with interface, not implemented classes. Hide concrete classes from the client.
• • • • •
Factory methods can be parameterized. The returned object may be either abstract or concrete object. Providing hooks for subclasses is more flexible than creating objects directly. Follow naming conventions to help other developers to recognize the code structure. Related patterns include o Abstract Factory , which is a layer higher than a factory method. o Template method, which defines a skeleton of an algorithm to defer some steps to subclasses or avoid subclasses o Prototype, which creates a new object by copying an instance, so it reduces subclasses. o Singleton, which makes a returned factory method unique.
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 } ... }
The above draw() methods are overloaded.
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 { public public public public public public public //....
Data(String fileName) {...}; Data(URL url, String fileName) {....}; DataInfo getDataInfo() throws Exception {...}; FieldInfo getFieldInfo() throws Exception {...}; void write(FieldInfo fi) throws Exception {...}; void modify(FieldInfo fi) throws Exception {...}; void delete(FieldInfo fi) throws Exception {...};
} class DataManager{ Data data = null; ... if (local) { data = new Data(localFile); ... } if (remote){ data = new Data(connectRemote, databaseFile); ...
} data.write(someInfo); data.modify(someInfo); .... }
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. //Let's say the interface is Display interface Display { //load a file public void load(String fileName); //parse the file and make a consistent data type public void formatConsistency(); } //deal with plain text file class CSVFile implements Display{ public void load(String textfile) { System.out.println("load from a txt file"); } public void formatConsistency() { System.out.println("txt file format changed"); } } //deal with XML format file class XMLFile implements Display {
}
public void load(String xmlfile) { System.out.println("load from an xml file"); } public void formatConsistency() { System.out.println("xml file format changed"); }
//deal with binary format file class DBFile implements Display { public void load(String dbfile) { System.out.println("load from a db file");
} public void formatConsistency() { System.out.println("db file format changed"); } } //Test the functionality class TestFactory { public static void main(String[] args) { Display display = null; //use a command line data as a trigger if (args[0].equals("1")) display = new CSVFile(); else if (args[0].equals("2")) display = new XMLFile(); else if (args[0].equals("3")) display = new DBFile(); else System.exit(1); //converging code follows display.load(""); display.formatConsistency();
} } //after compilation and run it 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: static void paint(Shape s){ if ( s instanceof Line) s.draw(); //more job here if (s instanceof Square) s.draw(); //more job here if (s instanceof Circle) s.draw(); //more job here }
C:\ Command Prompt C:\> java Painting line square circle The paint method takes a variable of Shape type at runtime. The draw method is called based on the runtime type. Overloading method is a kind of prototype too. 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()). class Complex implements Cloneable { int[] nums = {1,2,3,4,5}; public Object clone() {
try { return super.clone(); }catch(CloneNotSupportedException cnse) { System.out.println(cnse.getMessage()); return null; } } int[] getNums() { return nums; } } class Test { static Complex c1 = new Complex(); static Complex makeCopy() { return (Complex)c1.clone(); } public static void main(String[] args) { Complex c1 = makeCopy(); int[] mycopy = c1.getNums(); for(int i = 0; i < mycopy.length; i++) System.out.print(mycopy[i]); } }
C:\
Command Prompt C:\> java Test 12345
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. class Complex implements Cloneable { int[] nums = {1,2,3,4,5}; public Object clone() { try { return super.clone(); }catch(CloneNotSupportedException cnse) { System.out.println(cnse.getMessage()); return null; } } int[] getNums() { return nums; } } class Test { Complex c1 = new Complex(); Complex makeCopy() { return (Complex)c1.clone(); } public static void main(String[] args) { Test tp = new Test(); 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(); System.out.print("cloned object: "); for(int ii = 0; ii < c2.nums.length; ii++) System.out.print(c2.nums[ii]); System.out.println(); System.out.print("original object: "); for(int iii = 0; iii < tp.c1.nums.length; iii++) System.out.print(tp.c1.nums[iii]); }
C:\ Command Prompt C:\> java Test local array: 52345 cloned object: 52345 original object: 52345
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(); System.out.print("cloned object: "); for(int ii = 0; ii < c2.nums.length; ii++) System.out.print(c2.nums[ii]);
System.out.println();
}
System.out.print("original object: "); for(int iii = 0; iii < tp.c1.nums.length; iii++) System.out.print(tp.c1.nums[iii]);
}
C:\ Command Prompt C:\> java Test2 local array: 52345 cloned object: 52345 original object: 12345
Singleton Definition One instance of a class or one value accessible globally in an application.
Where to use & benefits • • • • • • •
Ensure unique instance by defining class final to prevent cloning. May be extensible by the subclass by defining subclass final. Make a method or a variable public or/and static. Access to the instance by the way you provided. Well control the instantiation of a class. Define one value shared by all instances by making it static. Related patterns include o Abstract factory, which is often used to return unique objects. o Builder, which is used to construct a complex object, whereas a singleton is used to create a globally accessible object. o Prototype, which is used to copy an object, or create an object from its prototype, whereas a singleton is used to ensure that only one prototype is guaranteed.
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, final class RemoteConnection { private Connect con; private static RemoteConnection rc = new RemoteConnection(connection); private RemoteConnection(Connect c) { con = c; .... } public static RemoteConnection getRemoteConnection() { return rc; } public void setConnection(Connect c) { this(c); } } usage: RemoteConnection rconn = RemoteConnection.getRemoteConnection; rconn.loadData(); ... The following statement may fail because of the private constructor RemoteConnection con = new RemoteConnection(connection); //failed //failed because you cannot subclass it (final class) class Connection extends RemoteConnection {}
For example, to use a static variable to control the instance; 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 For example to use a public static variable to ensure a unique. 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
The companyID is a unique and cannot be altered by all subclasses.
Note that Singletons are only guaranteed to be unique within a given class loader. If you use the same class across multiple distinct enterprise
containers, you'll get one instance for each container. Whether you need to use synchronized keyword to manage the method access, it depends on your project situation and thread controlling.
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.
Where to use & benefits • • • • • • • • •
Try to match an interface(WindowAdapter, etc.) Make unrelated classes work together. Multiple compatibility. Increase transparency of classes. Make a pluggable kit. Delegate objects. Highly class reusable. Achieve the goal by inheritance or by composition Related patterns include o Proxy, which provides the same interface as its subject, whereas an adapter provides a different interface to the object it adapts. o Decorator, which focuses on adding new functions to an object, whereas an adapter coordinates two different objects. o Bridge, which tries to separate an interface from its implementation and make an object vary independently, whereas an adapter tries to change and cooperate the interface of an object.
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){} }
Here is a test program 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");
}
}
interface Extra extends Clean{ public void takeCare(); } class Facility implements Extra{ public void makeClean() { System.out.println("Clean Facility"); } public void takeCare() { System.out.println("Care has been taken"); } } In order to reuse Workshop and Office classes, we create an adapter interface Extra and add new job takeCare in the system. 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.
Where to use & benefits • • • • •
Want to separate abstraction and implementation permanently Share an implementation among multiple objects Want to improve extensibility Hide implementation details from clients Related patterns include o Abstract Factory, which can be used to create and configure a particular bridge. o Adapter, which makes unrelated classes work together, whereas a bridge makes a clear-cut between abstraction and implementation.
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 {
}
public public public public public public
void void void void void void
nextQuestion(); priorQuestion(); newQuestion(String q); deleteQuestion(String q); displayQuestion(); displayAllQuestions();
//implementation class QuestionManager { protected Question questDB; //instantiate it later public String catalog; public QuestionManager(String catalog) { this.catalog = catalog; } public void next() { questDB.nextQuestion(); } public void prior() { questDB.priorQuestion(); } public void newOne(String quest) { questDB.newQuestion(quest); } public void delete(String quest) { questDB.deleteQuestion(quest); } public void display() { questDB.displayQuestion(); }
}
public void displayAll() { System.out.println("Question Catalog: " + catalog); questDB.displayAllQuestions(); }
//further implementation class QuestionFormat extends QuestionManager { public QuestionFormat(String catalog){ super(catalog); } public void displayAll() {
}
System.out.println("\n~~~~~~~~~~~~~~~~~~~~~~~~"); super.displayAll(); System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~");
} //decoupled implementation class JavaQuestions implements Question { private List<String> questions = new ArrayList<String>(); private int current = 0; public JavaQuestions() { //load from a database questions.add("What is questions.add("What is questions.add("What is questions.add("What is questions.add("What is questions.add("What is questions.add("What is
and fill in the container Java? "); an interface? "); cross-platform? "); UFT-8? "); abstract? "); Thread? "); multi-threading? ");
} public void nextQuestion() { if( current <= questions.size() - 1 ) current++; } public void priorQuestion() { if( current > 0 ) current--; } public void newQuestion(String quest) { questions.add(quest); } public void deleteQuestion(String quest) { questions.remove(quest); } public void displayQuestion() { System.out.println( questions.get(current) ); }
}
public void displayAllQuestions() { for (String quest : questions) { System.out.println(quest); } }
class TestBridge { public static void main(String[] args) {
QuestionFormat questions = new QuestionFormat("Java Language"); questions.questDB = new JavaQuestions();//can be hooked up with other question class //questions.questDB = new CsharpQuestions(); //questions.questDB = new CplusplusQuestions(); questions.display(); questions.next(); questions.newOne("What is object? "); questions.newOne("What is reference type?"); 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.
See more sample code at wikipedia.org