Moe's Tech Blog

[Design Pattern] Decorator Pattern 본문

Software Design Pattern/Notes

[Design Pattern] Decorator Pattern

moe12825 2022. 8. 22. 09:36

Motivation

  • In software, like devising a new beverage (by mixing multiple ingredients), it's beneficial to have flexible combinations of overall behaviors
    • But, we encounter an issue trying to do this dynamically at runtime
      • This means, a change cannot be made to classes while our program is running
      • We need to create a new class in order to achieve a new combination of behaviors
      • Given how there are lots of combinations, there will be many many classes and we don't want that
    • Can we add additional behaviors or responsibilities dynamically?

 

Decorator Design Pattern

  • Uses aggregation to combine behaviors at runtime
    • aggregation is used to represent a "has a" or "weak containment" relationship between two objects
    • "has a" relationship can be used to build stack of objects where each level of the stack contains an object that knows about it's own behaviors and augments the one underneath it in the stack

Decorator Design Pattern Using UML Diagram

  • Here, the component interface is the WebPage
    • This will define all subclasses in the pattern as a type of WebPage which have their own ways of how to display themselves
  • The concrete component class is the BasicWebPage
    • It consists of HTML, stylesheet, and scripts, which we will represent as strings for simplicity
    • This basic web page knows how to display all of its web page elements
  • The decorator componet class is the WebPageDecorator
    • Here in this case, the abstract WebPageDecorator class is required to augment the basic web page
    • The WebPageDecorator is the subtype of webpage

  • This drastically reduces the number of classes used to create program

 

How to create Decorator Design Pattern

  • Step 1: Design the component interface
public interface WebPage {
    public void display();
}
  • Step 2: Implement the interface with your base concrete component class
public class BasicWebPage implements WebPage {
    private String html = ...;
    private String styleSheet = ...;
    private String scripts = ...;

    public void display() {
        /*Renders the HTML to the stylesheet, and run any embedded scripts*/
        System.out.println("Basic Web Page");
    }
}
  • Step 3: Implement the interface with your abstract decorator class
    • The implementation of the decorator class is important despite how little code there is
    • Note:
      • 1. When a webpage decorator contains only one instance of web page, this allows us to stack decorators on top of the basic web page, and on top of each other.
        • Each type of web page is responsible for it's own behavior and will recursively invoke the next web page on the stack to execute it's behavior
      • 2. The constructor will let you link different subtypes of web page together in a stack
        • all you need to do is tell it what instance of web page subtype you want to stack upon
        • since the constructor allows you to link any web page subtype onto the stack, the order in which you build the stack matters
        • the key is that the basic web page must be the first one in the stack
public abstract class WebPageDecorator implements WebPage {
    protected WebPage page;

    public WebPageDecorator(WebPage webpage) {
        this.page = webpage;
    }

    public void display() {
        this.page.display();
    }
}
  • Step 4: Inherit from the abstract decorator and implement the component interface with concrete decorator classes
public class AuthorizedWebPage extends WebPageDecorator {
    public AuthorizedWebPage (WebPage decoratedPage) {
        super(decoratedPage);
    }

    public void authorizedUser() {
        System.out.println("Authorizing user");
    }

    public display() {
        super.display();
        this.authorizedUser();
    }
}

public class AuthenticatedWebPage extends WebPageDecorator {
    public AuthenticatedWebPage (WebPage decoratedPage) {
        super(decoratedPage);
    }

    public void authenticateUser() {
        System.out.println("Authenticating user");
    }

    public display() {
        super.display();
        this.authenticateUser();
    }
}
  • Step 5: Put everything together
    • The idea is to link the calls displayed all the way to the bottom (i.e. AuthenticatedWebpage) and bubble the execution back up
      • e.g.. Here, basic webpage is built first, then Authorized webpage, and then AuthenticatedWebpage
public class Program {
    public static void main(String args[]) {
        WebPage myPage = new BasicWebPage();
        myPage = new AuthorizedWebPage(myPage);
        myPage = new AuthenticatedWebPage(myPage);
        myPage.display(); 
    }
}

 

Summary

  • The decorator design allows you embellish your objects in a unique way
    • decorator design lets you dynamically modify objects
    • decorator design reduces the variety of classes you need to write
  • The key concepts for this design pattern are that:
    • 1. We can add any number of behavior dynamically to an object at runtime by using aggregation as a subtitute for pure inheritance
    • 2. Polymorphism is achieved by implementing a single interface
    • 3. Aggregation lets us create a stack of objects
    • 4. Each decorator object in the stack is aggregated in a one-to-one relationship with the object below it in the stack
    • 5. By comining aggregation and polymorphism, we can recursively invoke the same behavior down the stack and have the behavior execute upwards from the concrete component object
  • The decorator design is useful where
    • 1. you are writing a large codebase
      • A larger code base not only takes more time to complete, but it is also difficult to maintain, and can reduce the flexibility of the system
      •  You want to have a robust system but without the headache of having an enormous amount of code to write and look after
      • Decorator pattern allows you to create complex software without the complex overhead