Sie sind auf Seite 1von 54

Gang of Four Design Patterns

An Introduction to Pattern-Based Object-Oriented Design

Gang of Four
o Pattern-based design was introduced into architecture and engineering in the 1950's o Almost immediately, software engineers began using patterns for designing software o It wasn't until a group of four researchers combined forces that pattern-based design became well-known and commonplace
o This group was known as the gang of four (GoF)

Gang of Four
o The gang of four (GoF) is:
o Erich Gamma o Richard Helm o Ralph Johnson o John Vlissides

o They are the authors of the famous text "Design Patterns: Elements of Reusable Object-Oriented Software"

Pattern Quotes
o A pattern is an idea that has been useful in one practical context and will probably be useful in others
o Martin Fowler

o A pattern is a three-part rule, which expresses a relation between a certain context, a problem, and a solution
o Christopher Alexander

Design Pattern Advantages


o Using patterns offers a few key advantages:
o Leverage a proven solution o Provide a common vocabulary

Leverage a Proven Solution


o The solution for a pattern has been designed, implemented and tested
o Reusing these solutions allows most of each of these steps to be eliminated o If the implementation of a pattern is used, the design, implementation, and testing are minimal just to ensure the proper behaviour exists o If the design of a pattern is used, the solution specific to the problem must be implemented and tested, but need not be redesigned

Provide a Common Vocabulary


o Some patterns are very common
o Documenting and cataloguing patterns allows designers and architects to describe a solution using patterns as part of the language
o Typically, this can make descriptions of solutions shorter

o Architects and designers can more easily communicate their designs to their developers

Creational Design Patterns


o There are many design patterns that relate to the creation of objects o In this class, we will focus on three important patterns:
o Singleton o Abstract Factory o Prototype

The Singleton Pattern


o The Singleton creates a single instance of an object, but allows multiple users to access it o e.g. Consider a class that represents the connection to a database
o Creating only one connection to a database and re-using it in various parts of the program makes sense

The Singleton Pattern


o When to use the Singleton pattern:
o When there should be only one instance of a class, and it must be accessible to clients from a wellknown access point

Singleton Pattern: Example


o A class (Configuration) maintains the applications configuration
o The configuration is also stored in an XML file by this class o Many objects may wish to read and update the configuration
o Multiple simultaneous reads are not a problem o However, we do not want multiple simultaneous writes o The class can use the synchronized keyword to block concurrent access o A singleton class will ensure that all of the applications objects can get the same copy of this class

Singleton Pattern: Example


public class ConfigurationSingleton { public static Configuration instance = null; public static Configuration getInstance() { if (instance == null) instance = new Configuration(); return instance; } }

The Abstract Factory Pattern


o The Idea: To provide means to create families of related or dependent objects without specifying their concrete classes o e.g. Consider a factory class that creates buttons
o Users can use this factory to create various kinds of buttons for their application o The factory object might determine (through the user's preferences) what kind of button to make
o Perhaps the button will be the Windows-style or UNIX-style button o Perhaps the colours of the button will be customized as per the users colour preferences o The developer need not concern him/herself with these user-preference details, since they use the same factory in all cases

The Abstract Factory Pattern


o The Abstract Factory takes advantage of polymorphism to accomplish its task o The user of an abstract factory requests an object of a specific type o The factory is actually a concrete factory, which knows how to create that one specific object type o The factory returns the parent class, but the value is the correct descendant of this type

The Abstract Factory Pattern


o Use the Abstract Factory when:
o The system should be independent of how objects are created, composed, and represented o The system should be configured with one of a family of objects

o How it works:
o The Abstract Factory only determines which Concrete Factory it will use o The Concrete Factory actually creates the appropriate object o Normally, the appropriate Concrete Factory is determined and created at run-time (once)

Abstract Factory Pattern: Example


o Abstract Factory: The main factory, which delegates object creation to one of the Concrete Factories
o e.g. public abstract Button getInstance();

o Concrete Factory (one per object type): Knows how to create one specific type of object
o e.g. public class UNIXButtonFactory extends ButtonFactory { public Button getInstance() { Button button = new UNIXButton(); // UNIX-specific stuff for buttons here return button; } } ButtonFactory factory = ; Button button = factory.getInstance();

Abstract Factory Example


ButtonFactory
getInstance()

request

User

Button UNIXButtonFactory
getInstance()

WindowsButtonFactory
getInstance()

UNIXButton

WindowsButton

The Prototype Pattern


o The Idea: Create a copy of a prototype object, and then customize it o e.g. Consider a UML tool that lets you add new classes, objects, methods, attributes
o When a user creates a class, it has all default values (and no methods or attributes) o The user can then:
o o o o Rename the class Modify class properties (e.g. abstract) Add attributes Add methods

Prototype: Example
// initialize the prototype UMLClass classPrototype = new UMLClass(); classPrototype.setName(NewClass); classPrototype.setAttributes(new ArrayList<Attribute>()); classPrototype.setMethods(new ArrayList<Method>()); etc // create an instance, and customize it UMLClass newClass1 = (UMLClass)classPrototype.clone(); newClass1.setName(Customer); newClass1.setAbstract(true); Attribute attribute1 = new Attribute(); attribute1.setName(firstName); attribute1.setType(String); newClass1.getAttributes.add(attribute1); etc

Prototype: Example

User

BasePrototype
clone()

ClassPrototype
clone()

UseCasePrototype
clone()

The Prototype Pattern


o When to use the Prototype pattern:
o When it makes sense to create classes in an identical (or similar) form, then customize them

Structural Design Patterns


o There are many design patterns that relate to the structure of (or static relationships between) objects o In this class, we will focus on three important patterns:
o Adapter o Faade o Proxy

The Adapter Pattern


o The Idea: Allow one interface to operate like another interface
o Thus one type of object can be used like another type of object

o e.g. In Java, if you want to read bytes from a file, you use one of the 'XYZInputStream' classes
o If you want to read characters (including multibyte characters) from a file, you use one of the 'XYZReader' classes o Any 'XYZInputStream' class can be treated like an 'XYZReader' class by applying an adapter called 'InputStreamReader' which converts bytes to characters

The Adapter Pattern


o Use adapters when:
o You want to use an existing class (that meets your needs), but its interface doesn't match what you need o You want to create a reusable class that can cooperate with unrelated classes (some yet undeveloped)
o Thus you cannot know in advance what the required interfaces will be

The Adapter Pattern


o An adapter is one of the most useful patterns
o This is because it allows seemingly different objects (which could be pattern-based) to operate, even if their interfaces don't match o Adapters increase the reusability of all objects

o Another example of an Adapter would be a program that sends input to and receives output from a legacy application
o This is typically done using standard in/out o This is done frequently to update old (but operational and well-tested) software to include features such as:
o An updated GUI interface to a text-based application o A web interface

Adapter: Example
o As an example, assume our company switches database vendors
o Our software has isolated database operations to one module o That module makes calls to a well-known API o Our new database has its own API, which has similar functionality o However, the interfaces are not the same o What do we do?

Adapter: Example

User

OldDBAPIInterface
GetObject(String key)

DBAdapter
GetObject(String key)

NewDBAPI
FindObject(String hashedKey)

Adapter: Example
public class DBAdapter { public Object GetObject(String key) { String hashedKey = HashingUtils.hash(key); return FindObject(hashedKey); } }

OldDBAPIInterface dbComponent = ; Config config = dbComponent.GetObject(config);

The Faade Pattern


o The Idea: Provide a unified interface to a set of interfaces of a subsystem
o When we have a number of lower-level classes that perform the function of some high-level interface, Faade acts as the high level interface
o The Faade delegates each responsibility to the appropriate lower-level class

The Faade Pattern


o e.g. In your project, the Controller might be made of several different classes
o Each of these classes might perform different tasks

o A Faade might be used to combine the functionality of these classes into a unified interface

Faade: Example
AccountService
User createAccount() deposit() withdraw()

TransactionService Faade
createAccount() deposit() withdraw() addTransaction() applyForLoan() addTransaction()

LoanService
applyForLoan()

Faade: Example
public class BankFaade { private AccountService accountService; private TransactionService transactionService; private LoanService loanService; public addAccount(Account account) { accountService.addAccount(account); } public deposit(int accountId, float amount) { accountService.deposit(accountId, amount); } public withdraw(int accountId, float amount) { accountService.withdraw(accountId, amount); } public addTransaction(Transaction tx) { transactionService.addTransaction(tx); } public applyForLoan(Customer cust, LoanDetails loan) { loanService.apply(cust, loan); } }

The Faade Pattern


o Use the Faade pattern when:
o You want to provide a unified interface to a more complex subsystem (of multiple classes) o Faades decouple the client and subsystem, reducing the interdependency (connascence)

The Proxy Pattern


o A proxy object is a representative (delegate) for another object o There are several uses for proxies:
o For controlling access to resources
o i.e. implementing security through proxies

o For creating virtual versions of objects


o Where creating the real objects is (e.g. computationally) expensive

o For access to remote objects


o Where the real objects exist on other machines

o For performing additional maintenance


o e.g. automatic garbage collection, synchronized access, loading objects from persistent storage

The Proxy Pattern


o e.g. Enterprise JavaBeans and RMI provide an example of using proxies
o A client for RMI or EJBs requests a proxy version of an object o The proxy communicates (over the network) with the actual object o The proxy object makes remote communication appear (almost) identical to a local connection

Proxy: Example
AccountService
{abstract}

User

addAccount() deposit() withdraw()

AccountServiceProxy
addAccount() deposit() withdraw()

AccountServiceImpl
addAccount() deposit() withdraw()

Proxy: Example
public class AccountServiceProxy implements AccountService { public void addAccount(Account account) { InvokeMessage message = new InvokeMessage(); message.setMethodName(addAccount); message.setArgumentCount(0); byte[] serializedAccount = serialize(account); message.setArgument(0, serializedAccount); send(message); } private byte[] serialize(Account account) { } private void send(InvokeMessage message) { } }

The Proxy Pattern


o The proxy object assembles data into a network message and sends it to the remote object o A remote receiver receives this data, and turns it into a local message that is sent to the remote object

The Proxy Pattern


o Use the Proxy pattern when:
o You wish to wrap an object in another object that performs some additional tasks
o e.g. loading the data for an object from disk when it is first needed o e.g. recording the usage of an object's methods o etc.

Behavioural Design Patterns


o There are many design patterns that relate to the communication between (or dynamic relationships between) objects o In this class, we will focus on three important patterns:
o Iterator o Observer o Template Method

The Iterator Pattern


o The Idea: To provide means of accessing elements of an aggregate in a sequential manner
o An Iterator sequentially processes each element of an aggregate o e.g. An Iterator might be used to search a LinkedList for a specific element

Iterator: Example
{abstract}

Iterator

First() : Item Next() : Item MoreElements() : Bool

ArrayIterator
First() : Item Next() : Item MoreElements() : Bool

LinkedListIterator
First() : Item Next() : Item MoreElements() : Bool

Item

Array

LinkedList

Iterator: Example
public class ArrayIterator implements Iterator { private String[] data; private int index; public ArrayIterator(String[] data) { this.data = data; this.index = 0; } public String first() { index = 0; return data[0]; } public String next() { index++; return data[index]; } public boolean moreElements() { if (index >= data.length) return false; return true; }

Iterator: Example
public class LinkedListIterator implements Iterator { private LinkedListElement first; private LinkedListElement current; public LinkedListIterator (LinkedListElement first) { this.first = first; this.current = first; } public String first() { return first.value(); } public String next() { current = current.getNext(); return current.value(); } public boolean moreElements() { if (current.getNext() == null) return false; return true; }

The Iterator Pattern


o Use the Iterator pattern when:
o To access an aggregate's contents sequentially, in a manner that is independent of the implementation of the aggregate
o i.e. To provide polymorphic iteration

The Observer Pattern


o The Idea: To provide a means for objects (Observers) to notify other objects (Observables) that they wish to be notified of changes to their state o e.g. In the MVC architecture, the View could register itself as an observer of the Model
o Thus, changes to the model would trigger events to be sent to the View, telling it to update its state to reflect the changes

Observer: Example
ObservableBugList
{abstract} observers

BugObserver
{abstract} BugAdded() BugStatusChanged()

AddObserver() RemoveObserver()

ObservableBugListImpl
bugs: List<Bug> AddObserver() RemoveObserver()

MyBugObserver
BugAdded() BugStatusChanged()

Observer: Example
public abstract class ObservableBugList { private ArrayList<BugObserver> observers; public void addObserver(BugObserver observer) { observers.add(observer); } public void removeObserver(BugObserver observer) { observers.remove(observer); } public abstract notifyBugAdded(Bug bug); public abstract notifyBugStatusChanged(Bug bug);

public MyBugObserver implements BugObserver { public void bugAdded(BugAddedEvent event) { } public void bugStatusChanged(BugStatusChangedEvent e) { } }

The Observer Pattern


o Use the Observer pattern when:
o A change to an object should cause other objects to be notified of the change
o The Observers may require notification so they can record the changes, update their own state accordingly, etc.

o These objects (Observers) may change over time o The object being watched (observable) does not necessarily need to know about the objects observing it

Template Method Pattern


o The Idea: Provide means to customize behaviour through subclasses
o The Template class will describe the main algorithms, but will leave various methods abstract to handle some small portion of the behaviour o Each subclass will handle this small portion differently, depending upon the desired outcome

Template Method Pattern


o e.g. Consider a Parser that checks an HTML file
o The Parser could be developed using a Template class, which would handle the generic parsing o Subclasses could define some methods to handle events that occur during the parsing process:
o startBlockElement(), endBlockElement(), startCharacterElement(), endCharacterElement(), startDocument(), endDocument()

o A program that turns HTML files into a tree data structure might do one thing in these methods, whereas a browser (which draws the HTML elements as its reads the file) might do something different

Template Method: Example


XMLParserTemplate
{abstract}

ParseXMLFile(filename: String) processStartTag(tagName: String) processArgument(tagName: String, argumentName: String, argumentValue: String) processBody(tagName: String, bodyText: String processEndTag(tagName: String)

ConvertHTMLToText
processStartTag(tagName: String) processArgument(tagName: String, argumentName: String, argumentValue: String) processBody(tagName: String, bodyText: String processEndTag(tagName: String)

Template Method: Example


public class ConvertHTMLToText extends XMLParserTemplate { private FileWriter out; public void convert(String htmlFile, String textFile) { out = new FileWriter(textFile); parseXMLFile(htmlFile); out.close(); } public void processStartTag(String tagName) {} public void processArgument(String tagName, String argumentName, String argumentValue) {} public void processBody(String tagName, String bodyText) { out.write(bodyText); } public void processEndTag(String tagName) {}

The Template Pattern


o Use the Template pattern when:
o An base algorithm is identical between all types of an object o The difference between each type are small, such as how they handle the occurrence of various events o Implement the abstract methods to define the specific behaviour o The template class will invoke these methods at the correct time during execution of the algorithm

Das könnte Ihnen auch gefallen