330 likes | 587 Views
Design Patterns. SWE 619 Software Construction Fall 2008. Design Patterns. Why should we study patterns? Standard solutions to common problems Provide a vocabulary for programmers Bag of tricks! Essential for every programmer. What is a design pattern?.
E N D
Design Patterns SWE 619 Software Construction Fall 2008
Design Patterns • Why should we study patterns? • Standard solutions to common problems • Provide a vocabulary for programmers • Bag of tricks! • Essential for every programmer
What is a design pattern? • It is a design technique geared to a particular, common problem • Example: suppose you need to limit objects to only single, logicially distinct instances • What does it provide? • Optimization (faster execution, lesser memory storage) • Flexibility (remove dependency) • Easier for clients to use your classes
Downside? • Increased complexity • Patterns are not for free • Contrast better performance and flexibility of use with increased difficulty in implementing/maintaining your code • Judgment call, when to use patterns
Use of substitution principal • Many patterns use subtypes while relying on substitution principal • Assume that subtypes will conform to super type specs • Clients need not know which subtype is being used • Types can be changed during execution without requiring client code to change
Types of patterns • Originally proposed by Erich Gamma et al. in GOF book • Three main types of patterns: • Creational patterns: Factory pattern (PolyGen), Singleton, Prototype etc. • Structural patterns: Flyweight, Bridge, Adaptor, Proxy, Façade etc. • Behavioral patterns: Iterator, Command, Strategy, Template etc. • We’ll look at patterns covered in Liskov 15
Factory Pattern • Hides details of object creation. • Why? • Do not want clients burdened with deciding which object to use. • Client code is based on supertype specs • Easier to replace types • Use a factory method to create objects
Factory method • Is it a constructor? NO • Factory may call a constructor Earlier client code: Poly p = new DensePoly(…); Poly q = new SparsePoly(…); New client code: Poly p = PolyProcs.makePoly(); Poly q = PolyProcs.makePoly(); P Q Client P Factory Q Client
Factory Method Benefits • Eliminates the needs to bind application classes into your code. • Client deals with interfaces instead of specific objects • Client may use any class that implements that interface • Enables subclasses to provide extended versions of an object • creating an object inside a factory is more flexible than creating an object directly in the client.
When to Use • When a class cannot anticipate the class of objects it must create. • When a class wants its subclasses to specify the objects it creates. • Classes delegate responsibility to one of several helper subclasses, and you want to localize the knowledge of which helper subclasses is the delegate.
Creation of compatible types • Example: • There are 4 types of objects- SparsePoly, DensePoly, SparseMatrix and DenseMatrix • SparsePoly can only be used with SparseMatrix • DensePoly can only be used with DenseMatrix • How can we restrict client code to only use compatible objects? • Solution: Use related factories (f1, f2) • f1 only creates SPoly and SMatrix and f2 only creates DPoly and DMatrix [Figure 15.3]
Compatible types [fig 15.3] M P Poly Matrix Factory F1 F2 SPoly DPoly SMatrix DMatrix
Other Factory Patterns • Builder • Constructs a collection of objects • Prototype • Provides method to obtain new object of same type as prototype • Differs from clone(), in that objects are typically in an initial state • Iteration • Every Iterator is a factory method • Produces a generator object satisfying Iterator interface
Flyweight Pattern • When • Lots of occurrences, need to be shared • Storage costs are high because of the quantity of objects • The application doesn't depend on object identity. • Naïve approach: create new instance each time • Smarter approach: Create only the first time • Benefit • (Possibly) Less memory usage • (Possibly) Better performance
Liskov example 15.4 public class Word { // Overview: … private static Map map; // maps strings to words public static Word makeWord (String s) // factory //E: returns the word corresponding to s private Word (String s) // private to exclude client //E: Makes this to be the word corr. to s public String mapWord (Context c) //E: Returns the string corr. to this // in the form suitable for context c
2 variations • Key table is hidden from the user • Client need not know if objects are being shared • Example on previous slide • Key table is visible to clients • When there are other uses for the table • E.g. clients need to iterate over all instances. • Example on next slide
Figure 15.5 public class Ident { // O: … Ident (String s) // package private constructor } public class IdentTable { //O: … public IdentTable() // Note: public constructor //E: makes this empty IdentTable public Ident makeReserved(String s) //Nonstatic Ident factory public Ident makeNonReserved (String s) public Iterator idents() // publicly accessible iterator }
State Pattern • When to use • Change the rep periodically for your data abstraction • E.g.: Liskov’s sets are small initially, so use Vector for rep, but if grow to large sizes, use HashSet for rep • How to handle this composite representation?
LiskovSet example • Rep • Before: Vector els; • Now: Vector els; HashSet h; • One (undesirable) approach: • Insert elements into both these objects – gag! • Another (undesirable) approach: • Declare els as: • Object els; (not Vector els or HashTable els). • Depending upon instance, call the right method.
What’s wrong with this approach? • Ugly! • Programmer is doing the job of runtime system • Need a better approach: State pattern • Define a new interface SetState • Needs all the functionality to manipulate Set • SmallSet and BigSet implement the interface • Rep: SetState els; • Now, dispatching is done by runtime sys
Figure 15.7 public class Set { //rename LiskovSet private SetState els; private int t1; // threshold for growing private int t2; // threshold for shrinking public Set () { els = new SmallSet(); } // set the thresholds public boolean empy() { return els.size() == 0; } public void insert (Object x) { int sz = els.size(); els.insert(x); if (sz == t1 && els.size() >t1 && !(els instanceOf BigSet) els = new BigSet(els); } // note state switch in context object, eg Set
Fig 15.7 continued public interface SetState { //O: SetState is used to implement LiskovSet; it provides most // of the Set methods. SetState objects are mutable } public class SmallSet implements SetState { // implementation similar to that shown in fig 5.6 } public class BigSet implements SetState { … }
Alternate Approach: Switch State in the State Objects • Subtypes of state type could detect need for change and modify containing object directly. • Implies state object (eg BigState) would need to alter context object rep (eg els in Set) • Not necessarily a desirable dependency if (state too small) // in BigState remove() return new SmallState(…); } else return this;
Bridge Pattern • Consider the Poly hierarchy: • How do I extend Poly? Poly SparsePoly DensePoly Poly Poly SparsePoly DensePoly SparsePoly DensePoly MyPoly MyPoly
What’s wrong? • Two different uses of inheritance • Conflict between them • Solution? • Split the type • Figure 15.8 • Not a state pattern (though rep can be switched) • The architecture splits the hierarchy! • Works for Poly as well as Set
Figure 15.8 Set SetState ExtSet SmallSet BigSet
Strategy and Command Patterns • Allow use of procedures as objects • Strategy is for when using context has an expectation about a procedure • Examples: Adder, Comparator, Filterer • Command expects only an interface • Example: Runnable • Often useful for procedures to be “closures” • A closure prebinds some arguments • Example: Filterer g = new LTFilterer(100);
Adaptor, Proxy, Decorator Patterns • Interposing an object between client and existing object • Example: lookup(String s) method that returns all associated objects • Adaptor: Goal is to provide client-specified API to existing object • Example: lookup() only returns one object • Proxy: No change to API; Goal is control • Example: Registry for lookup() resides across network • Decorator: Enhanced API • Example: add additional reverseLookup(Object obj) method • Example: Bloch’s Instrumented Set wrapper example (p 84)
Publish/Subscribe Patterns • Object of interest called a “subject” • Objects interested in subject are “observers” • Observer Pattern • Subject maintains list of observers • Observers join/leave the list • Pull • Observers notified of change • API for observers to obtain more data • Push • Observers get data with notification
More Publish/Subscribe Patterns • Inserting a mediator decouples subject from observers • Allows details of communication to be separated from both subject and observers • White Board • Decouples the caller from the identity of objects that provide a service. • Posting/Obtaining information • Form of multicast