Difference between revisions of "DesignPatterns"
From Suhrid.net Wiki
Jump to navigationJump to search (→Intent) |
|||
Line 302: | Line 302: | ||
== Intent == | == Intent == | ||
− | * Rather using a constructor, create new objects by using a prototypical instance. | + | * Rather using a constructor, create new objects by using a prototypical instance.(via cloning). |
* For e.g. creating a new instance might be expensive or complicated. | * For e.g. creating a new instance might be expensive or complicated. | ||
Revision as of 06:44, 22 December 2011
Contents
Intro
- A design pattern systematically names, explains, and evaluates an important and recurring design problem and its solution.
- They capture the intent behind a design by identifying objects, collaborations, and distributions of responsibilities.
- They capture static and dynamic structures of successful solutions to problems.
- Patterns support reuse of software architecture and design.
- They give software engineers a vocabulary with which to describe their designs.
- Patterns solve design problems such as:
- Finding appropriate classes to solve a problem.
- Determining how abstract or how concrete a class should be.
- Specifying interfaces of classes, architectures, and binary components.
- Designing for change.
- Patterns are not code-reuse they are experience reuse !
Facade
Intent
- Provide a common interface to a set of interfaces within a subsystem.
- Defines a higher level interface to make the subsystem interfaces easier to use.
Motivation
- Provide a simplified interface.
Consequences
- Shield clients from myriad subsystem components - hence promote weak coupling between clients and subsystems.
- Reduces number of objects clients have to deal with.
- Using a facade promotes decoupling between the client and the subsystems.
- Note that clients can still access the subsystems directly.
- Clients make a choice between ease of use and fine grained control (through direct access of subsystems).
Examples
public class Compiler {
public Compiler();
private Node node_tree;
private Scanner scanner;
private Parser parser;
private Risc_CG generator;
public void compile() {
node_tree=parser.parse(scanner);
generator.emit(node_tree);
.....
}
}
- The Compiler class is a facade to various sub system interfaces (Node, Scanner, Parser, Generator) that comprise a compiler.
- The compile() method provides a high level interface to the whole process of compiling using various subsystem interfaces.
- Here's one more example of a HomeTheatreFacade which simplifies the operation of various subsystems of a home theatre.
public class HomeTheaterFacade {
private Amplifier amp;
private Tuner tuner;
private DVDPlayer dvdPlayer;
private Projector proj;
private Lights lights;
private Screen screen;
public void watchMovie() {
lights.dim();
screen.down();
projector.on();
amp.on();
amp.setVolume(5);
dvd.on();
dvd.play();
}
}
Adapter
Intent
- Convert the interface of one class into another interface that clients expect.
- An adapter would let objects work together, that otherwise wouldn't because of incompatible interfaces.
Motivation
- There is a class that provides required functionality, but does not support the interface the design needs.
Example
- The enumeration interface allows us to step through elements of a collection.
- However, there is now a new Iterator interface which has more functionality.
- What if our client's design expect enumeration interfaces, but our data structure e.g. ArrayList doesn't implement Enumerations but implements Iterator.
- We need an adapter to convert the Iterator interface to the Enumeration interface.
Decorator
Intent
- Attach additional responsibilities to an object at run time.
Motivation
- We would like to add extra behavior to an object, not a class. Modifying the class would mean making compile time changes.
- Inheritance is a static mechanism and cannot solve this problem.
Example
- The java io package makes heavy use of decorators.
- For e.g. we have an InputStream as an interface. There is a FileInputStream, StringBufferInputStream etc.
- What if we wanted to filter each input stream and there were different types of filters. e.g. Buffered, Pushback, LineNumber, LowerCaseInput etc
- Then there would be a class explosion FileInputBuffered, FileInputPushback, StringBufferLineNumber etc....
- So we'll decorate InputStream's with a FilterInputStream decorator.
- InputStream is the abstract component, FilterInputStream is a decorator, then the different types of filters Buffered,Pushback are concrete decorators.
- Here's a LowerCaseInputStream decorator
public class LowerCaseInputStream extends FilterInputStream {
protected LowerCaseInputStream(InputStream in) {
super(in);
}
public int read() throws IOException {
int c = super.read();
return ( c == -1 ? c : Character.toLowerCase((char)c));
}
public int read(byte[] b, int offset, int len) throws IOException {
int result = super.read(b, offset, len);
for(int i=offset; i < offset + result; i++) {
b[i] = (byte) Character.toLowerCase((char)b[i]);
}
return result;
}
}
- Here's the test code, notice how the InputStream in is being decorated with different behaviours at run-time using Composition.
public class InputTest {
public static void main(String[] args) {
int c;
try {
InputStream in = new LowerCaseInputStream(new BufferedInputStream(new StringBufferInputStream("Abc Def Xyz")));
while((c = in.read()) > 0) {
System.out.print((char) c);
}
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Composite
Intent
- Compose objects into tree structures to represent part whole hierarchies.
- Allows clients to treat individual objects and compositions of objects uniformly.
Motivations
- Drawing tools - Treat Elements and Groups of Elements in the same way. e.g. draw(), drag(), can be performed on elements as well as group of elements.
- File Systems - Files and Folders e.g. move(), copy() etc.
- Text Systems - Words, Sentences and Pragraphs. e.g. delete(), insert() etc.
Example
- Consider a restaurant Menu. A menu has a MenuItem e.g. a dish. A menu can also have sub-menus e.g. DessertMenu which can have Dessert Menu Items.
- If we want to print a Menu, we can treat Menu and MenuItems as a single composite structure.
public abstract class MenuComponent {
public void print() {
throws new UnsupportedOperationException();
}
}
public class MenuItem extends MenuComponent {
public void print() {
print(name);
print(price);
print(description);
}
}
public class Menu extends MenuComponent {
List<MenuComponent> menuComponents = new ArrayList<MenuComponent>();
public void print() {
for(MenuComponent mc : menuComponents) {
mc.print(); //This works recursively, if a MenuComponent is a menu.
}
}
}
Proxy
Intent
- To provide a surrogate or a placeholder for another object to control access to it (the another object).
Motivation
- A target object can exist in another address space or hard to access place. By providing a proxy for the object - it can stand in for the real object and make it transparent for the client.
- The target object might be expensive to create or may take a long time. The proxy can stand in for the target until it is ready and then defer all calls to the target object.
Example
- The classic example is Java RMI.
- The client calls a method on a local RMI Proxy object that forwards the call across the network to a remote object that does the actual work.
- So the remote proxy controls access to a remote object.
- Both the proxy and realsubject implement the subject interface, therefore clients can treat proxy just like RealSubject.
- The proxy keeps a reference to the RealSubject, so that it can forward requests to the Subject when necessary.
Singleton
Intent
- Ensure that the class has only one instance and provide a global access point for it.
- Why not just use statics ? Object semantics may be required, plus also singletons can be created whenever we want, whereas statics tend to get loaded whether we use them or not.
Motivation
- There are some things which you need only one instance of. e.g. ImmortalMemory in Real time Java, or maybe a system wide scheduler object.
Example
- See below example, the Singleton class has only one unique instance.
- It also provides a global access point to the instance through the getInstance() method.
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if(uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
Builder
Intent
- Separate construction of a complex object from its representation.
- One construction process can be used for several representations.
Motivation
- A reader for XML documents should be able to convert the read contents to some other representation.
- Need to encapsulate a way a complex object is constructed from the client, hides internal representation of the product (e.g. PostScript) from the client.
- Allows objects to be constructed in a multistep fashion.
Example
- In the below example, the client XMLReader has a reference to TextBuilder, an abstract builder
- This can have many implementations e.g. PostScriptBuilder, UnicodeBuilder etc.
- The client, XMLReader then calls build_char(),build_word() on the builder which then builds the output.
- The XMLReader then does a getOutput() on the builder to retrieve the output.
Prototype
Intent
- Rather using a constructor, create new objects by using a prototypical instance.(via cloning).
- For e.g. creating a new instance might be expensive or complicated.
Motivation
- Need to hide the complexity of creating an object from the client.