460 likes | 479 Views
Understand how Façade simplifies interactions with complex systems, and how Decorator adds dynamic responsibilities to objects. Explore examples, structures, sequences, and code samples in Java.
E N D
Structural Design Patterns Yaodong Bi October 23, 2019
Structural design patterns • Façade • Decorator • Composite • Proxy • Adapter • Bridge • Flyweight
Façade • Design Purpose • Provide a single and simple interface to a package of classes • Design Pattern Summary • Define a single and simple interface for clients to use the functionality of the package
Façade - examples • A compiler package • It normally contains many classes/subpackages like Scanner, Parser, etc. • Most clients only want to compile their programs, i.e., they don’t care about functions of individual components in the package • Use Façade to provide a simple default interface to most clients.
Client Use op1(), op2(), op3(), and op4() Facade op1() op2() op3() op4() Façade - Structure ClassA op3_a() ClassC op4_c() ClassB op1_b() op2_b()
Sequence Diagram Client Facade B:ClassB A:ClassA C:ClassC op1() op1_b() op3() op3_a() op4() op4_c() op2() op2_b()
Client Compiler.compile(“test.c”) Compiler compile() Façade - Structure ProgramNodeBuilder Scanner Parser CodeGenerator
Class Scanner { public Scanner(InputStreamsourcecode) public Token scan() { … } } Class Parser { public parse(Scanner s, ProgramNodeBudiler p) { … } } Class ProgramNodeBuilder { public ProgramNodenewVariable(…) { … } public ProgramNodenewAssignment(…) { … } public ProgramNodenewReturnStmt(…) { … } } Class CodeGenerator { public void visitStatementNode(…) { … } public void visitExpressionNode(…) { … } } Façade – Sample code Class Compiler { public void compile(InputStream sc, OutputStreambytecode) { Scanner sc = new Scanner(sc); ProgramNodeBuilderpnb = new ProgramNodeBuilder(); Parser parser = new Parser(); parser.parse(sc, pnb); IntelCodeGenerator cg = new IntelCodeGenerator(bytecode) ProgramNodenodetree = pnb.getRootNode(); parsetree.traverse(cg); }
Façade - Examples framework Customer getCustomerName() getNumAccounts() getPersonalNote() getAccount( int ) AccountException Account getAccountNum() deposit( int ) getBalance() CustomerException BankCustomers 1..n BankCustomer BankAccount Client BankCustomers doDeposit( int amt, Customer cust, Account acc ) getBankAccount( Customer cust, int accNum ) getBankCustomer( String custName )
Façade - comments • Façade can reduce the degree of dependency between packages • Packages are dependent on each other only through their facades, not individual classes • Use Façade to provide a simple default view of the package that is enough for most clients • Façade does not try to encapsulate/hide the components in the package since there may be clients who need to access individual components in the package
Decorator • Design Purpose • Add responsibilities to an object at runtime. • Design Pattern Summary • Provide for a linked list of objects, each encapsulating responsibility.
Decorator - examples • The word processor example • A text view may have a border and a scroll bar and maybe other bells and whistles attached to it • How can those bells and whistles be added to the text view? • Inheritance?
Decorator – structure Component operation() Client ConcreteComp operation() Decorator Operation() comp void operation() { // do actions of the decorator comp.operation(); // do actions of the decorator }
Decorator – structure VisualComponent draw() Client TextView draw() Decorator draw() comp comp.draw() ScrollDecorator draw() scrollTo() scrollPosition BorderDescrotor draw() drawBorder() borderWidth super.draw() this.drawBorder()
Decorator – examples Client :Decorator1 :Decorator2 :ConcreteComp
Decorator - Sequence Diagram Client Component :Decorator1 :Decorator2 :ConcreteComp operation() operation() operation() operation() return return return return
Decorator – examples :Reader 1 : BufferedStreamReader :InputStreamReader System.in:InputStream
Decorator – examples : BufferedStreamReader :InputStreamReader System.in:InputStream
Decorator – key concept • allows addition to and removal from objects at runtime
Decorator – sample in Java interface VisualComponent { public void draw(); } public class Decorator implements VisualComponent { private VisualComponent component; public Decorator(VisualComponent comp) { this.component = comp; } public void draw() {component.draw(); } } public class Border extends Decorator { int width = 0; public Border(VisualComponent comp, int width) { super(comp); this.width = width; } public void draw() { System.out.print("Border["+ width + "]"); super.draw(); System.out.print("[" + width + "]Border"); } } public class TextView implements VisualComponent { private String text = null; public void setText(String text) { this.text = text; } public void draw() { System.out.print("--" + text + "--"); } } public class Scroll extends Decorator { public Scroll(VisualComponent stream) { super(stream); } public void draw() { System.out.print("Scroll["); super.draw(); System.out.print("]Scroll"); } }
Decorator – sample in Java public class Driver { public static void main(String[] args) { TextViewtextView = new TextView(); textView.setText("Hello World!"); System.out.println("\n\TEXIVIEW ONLY "); Window window = new Window(textView); window.draw(); System.out.println("\n\nWITH SCROLL "); VisualComponent comp = new Scroll(textView); window.setComponent(comp); window.draw(); System.out.println("\n\nSCROLL + BORDER"); comp = new Border(new Scroll(textView), 3); window.setComponent(comp); window.draw(); } } public class Window { private VisualComponent component; public Window(VisualComponent comp) { this.component = comp; } public void setComponent(VisualComponent comp) { this.component = comp; } public void draw() { component.draw(); } }
Decorator – sample in C++ class VisualComponent { virtual void Draw(); virtual void Resize(); }; Class TextView: public VisualComponet { void draw(0 { // draw } void resize() { // resize }; } class Decorator : public VisualComponent { Decorator(VisualComponent*); void Decorator::Draw () { _component->Draw(); } VisualComponent* _component; }; class BorderDecorator : public Decorator { BorderDecorator(VisualComponent*, int borderWidth); void Draw(){ Decorator::Draw(); DrawBorder(_width); } private void DrawBorder(int); private int _width; }; Class Window { void SetContents(VisualComponent* contents) { // ... } Class Driver { public static void main() { Window* window = new Window(); TextView* textView = new TextView; window->SetContents(textView); Window.draw(); // textview only window->SetContents( new BorderDecorator( new ScrollDecorator(textView), 1 ) ); Window.draw(); // textview with a border // and scrolls. } }
Composite • Design Purpose • Represent a Tree of Objects • Design Pattern Summary • Use a Recursive Form in which the tree class aggregates and inherits from the base class for the objects.
Composite - structure Objects Classes 1..n Component non-leaf node “non-leaf nodes have one or more components” “every object involved is a Component object” leaf node NonLeafNode
Composite - structure Component add( Component ) Remove(component) doIt() 1..n Client comp LeafNode doIt() NonLeafNode doIt() for all elements e in comp e.doIt() TypeANonLeafNode doIt() TypeBNonLeafNode doIt()
Composite – A Class Diagram :Client N0:NonLeafNode N1:NonLeafNode N2:NonLeafNode L3:LeafNode L2:LeafNode L1:LeafNode
Composite – sequence diagram :Client N0:NonLeafNode N1:NonLeafNode L1:LeafNode L2:LeafNode L3:LeafNode N2:NonLeafNode doIt() doIt() doIt() doIt() doIt() doIt()
Composite – examples Component 1..n Composite in java.awt Container component … . . Window Canvas
Proxy • Design Purpose • Avoid the unnecessary execution of expensive functionality in a manner transparent to clients. • Design Pattern Summary • Interpose a substitute class which accesses the expensive functionality only when required.
Proxy – examples Instantiate with Proxy object BaseActiveClass expensiveMethod() anotherMethod() Client Proxy expensiveMethod() anotherMethod() RealActiveClass expensiveMethod() anotherMethod() if ( realActiveObject == null ) // not loaded yet { realActiveObject = getRealActiveObject(); realActiveObject.expensiveMethod(); } else { realActiveObject.expensiveMethod(); }
Proxy – sequence diagram Client BaseActiveClass Proxy RealActiveClass expensiveMethod() create() if needed expensiveMethod()
Proxy – examples Instantiate with Proxy object Graphics Display() TexDoc graphics Image display() bitmap ImageProxy display() fileName image if ( image == null ) { // not loaded yet image = new Image(fileName); } Image.display();
Class TextDoc { graphics g; TextDoc(Graphics ip) { g = ip; } void display() { g.display(); } } Class ImageProxy implements Graphics { FileNamefileName; Image image; ImageProxy(FileNamefn) { fileName = fn; } display() { if (image == null) image = new Image(fileName); image.display(); } } Proxy – Sample code Interface Graphics { display(); } Class Image Implements Graphics { Bitmap bitmap; Image(FileName fn) { bitmap = readImage(fn); } display() { // draw the bitmap } readImage(FileName fn) { // read from the file(fn) // create a bitmap } }
Adapter • Design Purpose • Allow an application to use external functionality in a retargetable manner. • Design Pattern Summary • Write the application against an abstract version of the external class; introduce a subclass that aggregate the external class.
Adapter - examples • Interact with legacy systems • When you design a new system which has to interact with a legacy system, you may not want to the new system tightly coupled with (or dependent upon) the legacy system since the legacy system may be replaced in the future. • Using 3rd party systems • You may want to be able to easily substitute the current 3rd party system with another one.
Client Object Adapter - Structure Target +request() Adaptee +requestedMethod(); adaptee Adapter +request() adaptee.requestedMethod()
Adapter – sequence diagram Client Target Adapter Adaptee request(TargetIn):TargetOut request(TargetIn):TargetOut convert(TargetIn):AdapteeIn requestedMethod(AdapteeIn):AdapteeOut Return TargetOut Return TargetOut convert(AdapteeOut):TargetOut
Object Adapter – sample code Interface Target { public TargetOut request(TargetIn); } Class Adaptee { … public AdapteeOut requestedMethod(AdapteeIn) { // produce AdapteeOut; return AdapteeOut; } … } Class Adapter implements Target { private Adapteeadaptee; public Adapter(Adapteead) { adaptee = ad; } public TargetOut request(TargetInti) { AdapteeInai = convert(ti); AdapteeOutao = adaptee.requestedMethod(ai); return convert(ao); } private AdapteeInconvert(TargetInti) { // convert TargetIn to AdapteeIn } private TargetOut convert(AdapteeOutao) { // convert AdapteeOut to TargetOut } }
DrawingTool Object Adapter - Example Shape +boundingBox() +createManipulator() TextView +getExtent(); text TextShape +boundingBox() +createManipulator() text.getExtent() return new TextManipulator()
Design for Adaption • Pluggable Adapters • A small set of operations is specified for adapter & adaptee to implement • Using Abstract Operations • Client and target are the same entity • Adapter overrides abstract operations to delegate operations to adaptee • Using Delegate Objects • A delegate interface specifies operations the adapter needs to implement
Adapter - comments • An adapter may have more than one adaptee • There may not be a one-to-one correspondence between operations of Target and those of Adaptee • So it is possible that an operation of Target is realized with two separate adaptee classes. • The pattern decouples Client from adaptee. • When a different adaptee is needed, we only need to change to another adapter and the client does not need to change at all.
Structural Patterns - Summary • Facade provides an interface to collections of objects • Decorator adds to objects at runtime • Composite represents trees of objects • Proxy avoids calling expensive operations unnecessarily • Adapter decouples client from an existing system