Upload
oscar-fisher
View
213
Download
0
Embed Size (px)
Citation preview
Design Patterns
Gang Qian
Department of Computer ScienceUniversity of Central Oklahoma
Objectives
Overview Hiding Object Creation Patterns that save space or time Bridge Pattern Procedures as Objects Composites Indirection Publish/Subscribe
What Is A Design Pattern
Ways that programs are organized Often utilizes type hierarchy
Benefits Improving performance or modifiability Providing a vocabulary for understanding and discussing
designs Problem
Increased complexity Should be used when benefits outweigh
disadvantage Should always satisfy the substitution principle
Patterns That We Have Discussed Iterator pattern
Provide efficient access to elements of collection objects without revealing the rep
Template pattern Implementing concrete methods in a superclass
using abstract methods Those abstract methods will be implemented in
subclasses The concrete method defines a template for how
execution proceeds, while details are filled in later, when the subclasses are implemented
Hiding Object Creation
Motivation: Much OO code works based on an object’s
behavior rather than the class that implements the object
However, code depends on the constructor of an implementing class to create the object
Sometimes, we want to limit the dependencies on classes Easy to replace the class No need to manually select a certain subclass to use for
multiple implementations
Factory Pattern
Factory pattern Instead of using the constructor, factory methods
are used to create objects of some class Example:
Iterator methods are factory methods that create generator objects. We do not use the constructor of the inner class
It is flexible since we can change the inner class at any time without affecting the using code
Figure: P does not directly depend on SetGen<E>
Factory Class Factory methods may be gathered together into a
factory class A factory class may provide a collection of static
methods Those methods may create objects of a single type or of
several different types
public class PolyProcs {
/** EFFECTS: Creates the zero polynomial */
public static Poly makePoly()
/** EFFECTS: If n < 0, throws
NegativeExponentException; else returns the
polynomial cxn */
public static Poly makePoly(int n, int c)
throws NegativeExponentException
}
Factory Objects A factory class may also be used to create its own
instances/objects called factory objects The advantage is that the using code no long
depends on the factory class that implements the factory methods. More flexibility can be achieved
Example in the figure: There are two different flavors of S and T that are
created by Factory1 and Factory2 respectively M passes a factory object (Factory1 and/or Factory2)
to P which uses the Factory interface but is unaware of the subclasses of Factory
When to use a factory pattern Useful when you want to hide the actual
implementing class from the using code Example: When multiple implementations of a type are
used, the factory method can choose a proper implementation for the using code
There are two other design patterns that are closely related to factories
Builder pattern A builder method not only hides the
implementation choices for one or more types but also constructs a collection of objects of these types
Prototype pattern A prototype is an object that can create other
objects of its type After a prototype is created by a module, the rest
of the code calls a method of the prototype to obtain other objects of the prototype’s type
Different from clone() Newly created objects are in an initial state, not in the
same state as that of the prototype
Patterns That Save Space or Time These patterns are useful for either speeding
up a computation or reducing its storage requirements
Flyweight Pattern
Flyweight pattern Allows a program to reuse/share identical objects The shared objects are called flyweights Requires shared objects to be immutable
Must avoid creating duplicate objects So constructors cannot be used Need a factory method, which checks a table that
keeps track of existing objects
There are two possible structures In the first, the table is not accessible to the
using code The flyweight class provides a static factory
method used to create flyweights The table is maintained within the class as a static
variable So the table is shared by all flyweights
/** OVERVIEW: Words are strings that provide methods
to produce them in various forms. Words are
immutable and for each unique string there is
at most one word. */
public class Word {
private static Hashtable t; // maps strings to words
/** EFFECTS: Returns the word corresponding to s */
public static Word makeWord(String s)
/** EFFECTS: Constructor. Creates a word from s */
private Word(String s)
/** EFFECTS: Returns the string corresponding to
this in the form suitable for context c */
public String mapWord(Context c)
...
}
The second structure allows users to access the table Used when the table is used for other purposes,
such as iterating over its elements, or when more than one table is needed
To implement: There is a table object that contains all the existing
flyweights The factory method is a method of the object See an example on page 379
Entries need to be removed from the table if they are no longer in use May use WeakReference See a Java Text
Singleton Pattern
Singleton pattern Ensures that additional objects are not created if a
type just needs a single object, called a singleton The constructor of the type should not be
accessible to using code The EmptyIntList class is one example
public class IdentTable {
/** OVERVIEW: An IdentTable is a mutable collection
of identifiers; each distinct string has at
most one entry in the table. There is only
one IdentTable object. */
private static IdentTable t; // the singleton table
public static IdentTable getTable() {
if (t == null) t = new IdentTable();
return t;
}
private IdentTable() { ... }
...
}
State Pattern
State pattern Supports objects whose rep changes dynamically
Those objects that need to store different information in different states, or
Those whose performance can be improved by changing their rep in different states
Different from multiple implementation When state changes, state pattern changes the rep of an
object to a different type, while multiple implementation changes to another object with a different rep
State pattern is made possible by using multiple implementation as the rep
Example: Set<E> implementation Use Vector<E> for small sets For large sets, use HashSet<E> Two thresholds T1 and T2 are needed
When set size exceeds T1, switch to HashSet<E> When size reduces to T2, switch back to Vector<E> T2 < T1 to prevent unstable implementation for those
sets whose size stays around T1
A simple solution is to use Object as the rep in Set<E> directly:Object els; // a vector or a hash set els can reference either a vector for a small set or a hash
set for a large set Disadvantage: Code of each method needs to determine the
current state and then cast els accordinglyif (els instanceof Vector<?>) {
Vector<E> v = (Vector<E>)els; // process v
} else {
HashSet<E> t = (HashSet<E>)els; // process t
} Both inconvenient and expensive
The state pattern offers a better solution by separating the type being implemented from the type used to implement it The implemented type is called context The implementing type is called state type
Example: Set<E> Context: Set<E> State types: SetState<E>, SmallSet<E>, BigSet<E>
Notes: With the state pattern, context type does not need
to determine the type of els using instanceof More efficient
The context is not in the hierarchy of state types. So they can have different methods
The place to switch to a different state type is set in the code of the context type We may also do this in the state type. Not good, since it
requires the state type to use the context type, adding unnecessary dependency
The state pattern obviously increases programming complexity. Use it only when there is a significant benefit
More notes: State pattern can only be used for mutable types? Multiple implementations can be used for both mutable
and immutable types But it seems that, if multiple implementation is to be used
directly by the using code, then it is more suitable for immutable types.
For mutable types, it seems that multiple implementation is more suitable as the state types, i.e., implementing classes
Bridge Pattern
Motivation Type hierarchy used to extend behavior can interfere
with multiple implementations if they share the same superclass
Example 1: Need to create ExtendedPoly as a subtype of Poly. The
new subtype ExtendedPoly adds more behaviors. However, SparsePoly and DensePoly have already been created as subtypes of Poly for multiple implementation
We will need to implement ExtendedPoly with SparsePoly and DensePoly too Not good since SparsePoly and DensePoly do not need to
have the extended methods of ExtendedPoly
Example 2: Set<E>, BigSet<E>, SmallSet<E> Now need to add ExtendedSet<E>
To solve the problem, we can separate the implementation hierarchy from the type family
Bridge pattern Uses two different type hierarchies that has a
relationship (a bridge) Notes:
The bridge pattern occurs naturally when the state pattern is used
It can also be used when a super type has both multiple implementations and extension subtypes
It adds complexity so use it only when necessary