320 likes | 477 Views
Design Patterns II. Behavioral Pattern Visitor: intent and structure. Represent an operation to be performed on the elements of an object structure. Visitor: structure. Visitor: participants. Visitor (IChef)
E N D
Behavioral PatternVisitor: intent and structure Represent an operation to be performed on the elements of an object structure.
Visitor: participants • Visitor (IChef) • declares a Visit operation for each class of ConcreteElement in the object structure. The operation's name and signature identifies the class that sends the Visit request to the visitor. • ConcreteVisitor (ChefWong, ChefZung) • implements each operation declared by Visitor. • Element (AEater) • defines an Accept operation that takes a visitor as an argument. • ConcreteElement (Vegetarian, Carnivore ) • implements an Accept operation that takes a visitor as an argument. • ObjectStructure • may provide a high-level interface to allow the visitor to visit its elements
Visitor: implementation /* Visitor */ interfaceIChef { String cookVeggie(Vegetarian h, Integer n); String cookMeat(Carnivore h, Integer n);} /* Concrete Visitor 1 */ classChefWongimplementsIChef { publicString cookVeggie(Vegetarian h, Integer n) {return n + ":" + h.getBroccoli() + ", " + h.getSalt(); }publicString cookMeat(Carnivore h, Integer n) {return n + ":" + h.getMeat() + ", " + h.getPepper(); }} /* Concrete Visitor 2 */ classChefZungimplementsIChef { publicString cookVeggie(Vegetarian h, Integer n) {return n + ":" + h.getCorn() + ", " + h.getSalt(); }publicString cookMeat(Carnivore h, Integer n) {return n + ":" + h.getChicken() + ", " + h.getPepper(); }}
Visitor: implementation (cont.) /* Element */ abstract classAEater { String getSalt() { return "salt"; } String getPepper() { return "pepper"; } abstract String order(IChef c, Integer n); } /* Concrete Element 1 */ class Vegetarian extendsAEater { String getBroccoli() { return "broccoli"; } String getCorn() { return "corn"; } String order(IChef c, Integer n) { returnc.cookVeggie(this, n); }} /* Concrete Element 2 */ class CarnivoreextendsAEater { String getMeat() { return "steak"; } String getChicken() { return "cornish hen"; } String order(IChef c, Integer n) { returnc.cookMeat(this, n); }} /* Client */ AEater John = new Carnivore(); AEater Mary = new Vegetarian();Mary.order(newChefWong(), 2); John.order(newChefZung(), 3);
Visitor: consequences • Consequences: • Visitor lets you define a new operation without changing the classes of the elements on which it operates. • A visitor gathers related operations and separates unrelated ones. • Adding new ConcreteElement classes is hard. • Visiting across class hierarchies. • Accumulating state. • Breaking encapsulation.
Structural PatternDecorator: intent and structure Decorators provide a flexible alternative to subclassing for extending functionality.
Decorator: participants • Component (Window) • defines the interface for objects that can have responsibilities added to them dynamically. • ConcreteComponent (SimpleWindow) • defines an object to which additional responsibilities can be attached. • Decorator (WindowDecorator) • maintains a reference to a Component object and defines an interface that conforms to Component's interface. • ConcreteDecorator (ScrollBarDecorator, BorderDecorator) • adds responsibilities to the component.
Decorator: implementation /* Component */ public interface Window { public void draw(); } /* Concrete Component */ public class SimpleWindow implements Window { public void draw() {…} public String toString() { return "Simple window"; } } /* Decorator */ abstract classWindowDecoratorimplements Window { protected Window w; publicWindowDecorator (Window w) { this.w = w; } } /* Concrete Decorator 1 */ classScrollBarDecoratorextendsWindowDecorator { publicScrollBarDecorator(Window w) { super(w); } public void draw() { drawScrollBar(); w.draw(); } private voiddrawScrollBar() {…} public String toString() { return w + " including scroll bar"; } }
Decorator: implementation (cont.) /* Concrete Decorator 2 */ public classBorderDecoratorextendsWindowDecorator { publicBorderDecorator(Window w) { super(w); } publicvoid draw() { drawBorder(); w.draw(); } private voiddrawBorder() {…} public String getDescription(){ return w + " including border"; } } /* Client */ public class Client { public void run() { Window decoratedWindow = newScrollBarDecorator( newBorderDecorator( newSimpleWindow())); System.out.println(decoratedWindow); } }
Decorator: consequences • Consequences: • More flexibility than static inheritance. • Avoids feature-laden classes high up in the hierarchy. • A decorator and its component aren't identical. • Lots of little objects.
Structural PatternAdapter: intent and structure Convert the interface of a class into another interface clients expect.
Adapter: participants • Target (Stack) • defines the domain-specific interface that Client uses. • Client • collaborates with objects conforming to the Target interface. • Adaptee (DList) • defines an existing interface that needs adapting. • Adapter (DListImpStack) • adapts the interface of Adaptee to the Target interface.
Class Adapter: implementation /* Target */ interfaceStack<T> { voidpush (T o); T pop (); T top (); } /* Adaptee */ public classDList<T> { public voidinsertHead (T o) {…} public voidinsertTail (T o) {…} public T removeHead () {…} publicT removeTail () {…} public T getHead () {…} public T getTail(){…} } /* Adapter */ public classDListImpStack<T> extendsDList<T> implements Stack<T> { public void push (T o) { insertTail(o); } public T pop () { returnremoveTail (); } public T top () { returngetTail (); } }
Adapter: consequences • Consequences: • A class adapter • a class adapter won't work when we want to adapt a class and all its subclasses. • lets Adapter override some of Adaptee's behavior • no additional pointer indirection is needed to get to the adaptee. • An object adapter • lets a single Adapter work with many Adaptees • makes it harder to override Adaptee behavior.
Behavioral PatternCommand: intent and structure Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.
Command: participants • Command (Command) • declares an interface for executing an operation. • ConcreteCommand (MoveUpCommand, MoveRightCommand) • defines a binding between a Receiver object and an action. • implements Execute by invoking the corresponding operation(s) on Receiver. • Client (Game) • creates a ConcreteCommand object and sets its receiver. • Invoker (Game) • asks the command to carry out the request. • Receiver (GameBoard) • knows how to perform the operations associated with carrying out a request. Any class may serve as a Receiver.
Command: implementation /* Receiver */ public class GameBoard { public int x = 0; publicint y = 0; publicstaticfinalint XMAX = 10; publicstaticfinalint YMAX = 10; public void incX() { if (x < GameBoard.XMAX) ++x; } public void decX() { if (x > 0) --x; } public void incY() { if (y < GameBoard.YMAX) ++y; } public void decY() { if (y > 0) --y; } } /* Command */ public interface Command { void execute(); void undo(); } /* Concrete Command 1 */ public class MoveUpCommand implements Command { private GameBoardgb; public MoveUpCommand(GameBoardgb) { this.gb = gb; } public void execute() { gb.incY(); } public void undo() { gb.decY(); } }
Command: implementation (cont.) /* Concrete Command 2 */ public class MoveRightCommandimplements Command { private GameBoardgb; public MoveRightCommand(GameBoardgb) { this.gb = gb; } public void execute() { gb.incX(); } public void undo() { gb.decX();} } /* Invoker */ public class Game { privateGameBoardgb = newGameBoard(); private Command moveUpCommand; private Command moveLeftCommand; public Game() { moveUpCommand = newMoveUpCommand(gb); moveLeftCommand = newMoveRightCommand(gb); } public void action() { moveUpCommand.execute(); moveLeftCommand.execute(); } }
Command: consequences • Consequences: • Command decouples the object that invokes the operation from the one that knows how to perform it. • Commands are first-class objects. They can be manipulated and extended like any other object. • You can assemble commands into a composite command. • It's easy to add new Commands, because you don't have to change existing classes.
Behavioral PatternObserver: intent and structure Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
Observer: participants • Subject • knows its observers. Any number of Observer objects may observe a subject. • provides an interface for attaching and detaching Observer objects. • ConcreteSubject (PowerVector) • stores state of interest to ConcreteObserver objects. • sends a notification to its observers when its state changes. • Observer (IObserver) • defines an updating interface for objects that should be notified of changes in a subject. • ConcreteObserver (SizeKeeper) • maintains a reference to a ConcreteSubject object. • stores state that should stay consistent with the subject's. • implements the Observer updating interface to keep its state consistent with the subject's.
Observer: implementation /* Concrete Subject */ public class PowerVector { privateArrayList<Object> items = newArrayList<Object>(); privateArrayList<IObserver> observers = newArrayList<IObserver>(); public intsize() { return items.size(); } public voidaddObserver(IObserver ob) { observers.add(ob); } public void add(Object o) { items.add(o); notifyObservers(); } public void remove(Object o) { items.remove(o); notifyObservers(); } public voidnotifyObservers() { for (IObserver ob: observers) ob.update(this); } }
Observer: implementation (cont.) /* Observer */ public interface IObserver { public void update(PowerVector subject); } /* Concrete Observer */ public class SizeKeeperimplementsIObserver { private intsize; public void update(PowerVector subject) { size = subject.size(); } public int size() { return size; } } /* Client */ public class Client { private PowerVector v = newPowerVector(); public void run(){ v.addObserver(newSizeKeeper()); v.add(1); v.add(2); System.out.println(v.size()); } }
Observer: consequences • Consequences: • Abstract coupling between Subject and Observer. • Support for broadcast communication. • Unexpected updates.
Behavioral PatternChain-of-Responsibility: intent and structure Chain the receiving objects and pass the request along the chain until an object handles it.
Chain-of-Responsibility: participants • Handler (Filter) • defines an interface for handling requests. • (optional) implements the successor link. • ConcreteHandler (EvenFilter, PrimeFilter) • handles requests it is responsible for. • can access its successor. • if the ConcreteHandler can handle the request, it does so; otherwise it forwards the request to its successor. • Client (Client) • initiates the request to a ConcreteHandler object on the chain.
Chain-of-Responsibility: implementation /* Handler */ public class Filter { private Filter next = null; public void attach(Filter other) { other.next = this.next; this.next = other; } public final void invoke(intv) { if (this.handle(v)) return; if (next != null) next.invoke(v); } public boolean handle(int v) { return false;} } /* Concrete Handler 1 */ public class EvenFilter extends Filter { public boolean handle(intv) { return (v % 2 == 0);} } /* Concrete Handler 2 */ public class PrimeFilter extends Filter { public boolean handle(intv) { for (int i = 2; i * i <= v; ++i) if (v % i == 0) return false; return true; } }
Chain-of-Responsibility: implementation (cont.) /* Client*/ public class Client{ private ArrayList<Integer> l = new ArrayList<Integer>(); private Filter chain = new Filter(); public Client() { chain.attach(new Filter() { public boolean handle(intv) { l.add(v); return true; } }); chain.attach(new EvenFilter()); chain.attach(new PrimeFilter()); } public void run() { for (int i = 0; i < 10; ++i) chain.invoke(i); System.out.println("Size = " + l.size()); } }
Chain-of-Responsibility: consequences • Consequences: • Reduced coupling. • Added flexibility in assigning responsibilities to objects. • Receipt isn't guaranteed.