Design Patterns_Structural Patterns-《Decorator Patterns》

Design Patterns_Structural Patterns-《Decorator Patterns》

The notes are organized from the detailed explanation of Java design patterns by dark horse programmers, 23 Java design patterns (diagram + framework source code analysis + actual combat)

overview

Let's start with an example of a fast food restaurant.

Fast food restaurants offer fast food such as fried noodles and fried rice. Additional side dishes such as eggs, ham, and bacon can be added. Of course, additional side dishes need to be charged. The price of each side dish is usually different, so calculating the total price will be more troublesome.

image-20230107163114144

Problems with using inheritance:

  • poor scalability

    If we want to add another ingredient (ham sausage), we will find that we need to define a subclass for FriedRice and FriedNoodles respectively. If you want to add a fast food category (fried rice noodles), you need to define more subcategories.

  • too many subclasses

definition

  • It refers to the mode of dynamically adding some responsibilities (that is, adding additional functions) to the object without changing the structure of the existing object.

structure

Roles in the Decorator Pattern :

  • Abstract component (Component) role: define an abstract interface to standardize the object ready to receive additional responsibilities.
  • Concrete Component role: implement an abstract component and add some responsibilities to it by decorating the role.
  • Abstract decoration (Decorator) role: inherit or implement abstract components, and contain instances of concrete components, and can extend the functions of concrete components through its subclasses. This character is 装饰者.
  • Concrete Decorator role: implements related methods of abstract decoration, and adds additional responsibilities to concrete component objects.

the case

We use the decorator pattern to improve the fast food restaurant case and experience the essence of the decorator pattern.

The class diagram is as follows:

  • FastFood is an abstract component role;
  • FriedRice (fried rice) and FriedNoodles (fried noodles) are specific component roles;
  • Garnish (ingredients) is the role of abstract decoration, which is the most important 装饰者, inherits FastFood and at the same time aggregates FastFood;
  • Egg (eggs) and Bacon (bacon) are specific decorative roles.

code show as below:

  • Abstract Component Role-Snack Interface

    public abstract class FastFood {
          
          
        private float price; // 价格 
        private String desc; // 描述
    
        public FastFood() {
          
          
        }
    
        public FastFood(float price, String desc) {
          
          
            this.price = price;
            this.desc = desc;
        }
    
        public void setPrice(float price) {
          
          
            this.price = price;
        }
    
        public float getPrice() {
          
          
            return price;
        }
    
        public String getDesc() {
          
          
            return desc;
        }
    
        public void setDesc(String desc) {
          
          
            this.desc = desc;
        }
    
        public abstract float cost(); // 获取价格
    }
    
  • Concrete component role - fried rice

    public class FriedRice extends FastFood {
          
          
    
        public FriedRice() {
          
          
            super(10, "炒饭");
        }
    
        public float cost() {
          
          
            return getPrice();
        }
    }
    
  • Specific component role - fried noodles

    public class FriedNoodles extends FastFood {
          
          
    
        public FriedNoodles() {
          
          
            super(12, "炒面");
        }
    
        public float cost() {
          
          
            return getPrice();
        }
    }
    
  • Abstract decoration role (decorator class) - ingredient class

    public abstract class Garnish extends FastFood {
          
          
    
        // 聚合-声明快餐类的变量
        private FastFood fastFood;
    
        public FastFood getFastFood() {
          
          
            return fastFood;
        }
    
        public void setFastFood(FastFood fastFood) {
          
          
            this.fastFood = fastFood;
        }
    
        public Garnish(FastFood fastFood, float price, String desc) {
          
          
            super(price, desc);
            this.fastFood = fastFood;
        }
    }
    
  • Specific Decorating Role - Egg Topping

    public class Egg extends Garnish {
          
          
    
        public Egg(FastFood fastFood) {
          
          
            super(fastFood, 1, "鸡蛋");
        }
    
        public float cost() {
          
          
            return getPrice() + getFastFood().getPrice();
        }
    
        @Override
        public String getDesc() {
          
          
            return super.getDesc() + getFastFood().getDesc();
        }
    }
    
  • Specific Decorating Role - Bacon Topping

    public class Bacon extends Garnish {
          
          
    
        public Bacon(FastFood fastFood) {
          
          
            super(fastFood, 2, "培根");
        }
    
        @Override
        public float cost() {
          
          
            return getPrice() + getFastFood().getPrice();
        }
    
        @Override
        public String getDesc() {
          
          
            return super.getDesc() + getFastFood().getDesc();
        }
    }
    
  • test class

    public class Client {
          
          
        public static void main(String[] args) {
          
          
            // 点一份炒饭
            FastFood food = new FriedRice();
            // 花费的价格
            System.out.println(food.getDesc() + " " + food.cost() + "元");
    
            System.out.println("========");
            // 点一份加鸡蛋的炒饭
            FastFood food1 = new FriedRice();
    
            food1 = new Egg(food1);
            // 花费的价格
            System.out.println(food1.getDesc() + " " + food1.cost() + "元");
    
            System.out.println("========");
            // 点一份加培根的炒面
            FastFood food2 = new FriedNoodles();
            food2 = new Bacon(food2);
            // 花费的价格
            System.out.println(food2.getDesc() + " " + food2.cost() + "元");
        }
    }
    

    output

    炒饭 10.0元
    ========
    鸡蛋炒饭 11.0元
    ========
    培根炒面 14.0元
    

benefit

  • The decorator pattern can bring more flexible extension functions than inheritance, and is more convenient to use. You can obtain diverse results with different behavioral states by combining different decorator objects. The decorator pattern is more extensible than inheritance, and it perfectly follows the principle of opening and closing. Inheritance is a static additional responsibility, and decorators are a dynamic additional responsibility.

  • The decorating class and the decorated class can develop independently and will not be coupled with each other. The decorator mode is an alternative mode of inheritance, and the decorator mode can dynamically extend the function of an implementation class.

scenes to be used

  • When the system cannot be expanded by inheritance or inheritance is not conducive to system expansion and maintenance.
    • There are two main categories of situations where inheritance cannot be used:
      • The first category is that there are a large number of independent extensions in the system. To support each combination, a large number of subcategories will be generated, causing the number of subcategories to grow explosively;
      • The second category is because class definitions cannot be inherited (such as final classes)
  • Add responsibilities to individual objects dynamically and transparently without affecting other objects.
  • When the functional requirements of the object can be added dynamically, and can also be revoked dynamically.

Summarize

  • Advantages of Decorator Design Pattern
    • The purpose of both the decoration pattern and the inheritance relationship is to extend the functionality of the object, but 装饰模式可以提供⽐继承更多的灵活性.
    • Using different specific decoration classes and the permutation and combination of these decoration classes can create many combinations of different behaviors, and the original code does not need to be changed, which conforms to the "opening and closing principle" .
  • Disadvantages of decorator design pattern
    • The decoration mode adds many subclasses, and if used excessively, the program will become very complicated (multi-layer packaging).
    • Increase the complexity of the system and increase the difficulty of learning and understanding.

JDK source code analysis - IO stream

The wrapper classes in the IO stream use the decorator pattern: BufferedInputStream, BufferedOutputStream, BufferedReader, BufferedWriter.

Let's take BufferedWriter as an example to illustrate how to use BufferedWriter:

public class Demo {
    
    
    public static void main(String[] args) throws Exception {
    
    
        // 创建BufferedWriter对象
        // 创建FileWriter对象
        FileWriter fw = new FileWriter("C:\\Users\\Think\\Desktop\\a.txt");
        BufferedWriter bw = new BufferedWriter(fw);

        // 写数据
        bw.write("hello Buffered");

        bw.close();
    }
}

It really feels like a decorator pattern to use, let's look at their structure:

BufferedWriter inherits from Writer and aggregates Writer at the same time. This is the decorator pattern.

summary:

BufferedWriter uses the decorator pattern to enhance the Writer sub-implementation class, adding a buffer buffer to improve the efficiency of writing data.

The difference between proxy mode and decorator mode

The difference between static proxy and decorator mode:

  • Same point
    • must implement the same business interface as the target class
    • Declare the target object in both classes
    • can enhance the target method without modifying the target class
  • difference
    • different purpose
      • The decorator is to enhance the target object
      • Static proxy is to protect and hide the target object
    • different places to get target object build
      • The decorator is passed in from the outside world and can be passed through the construction method
      • Static proxies are created inside the proxy class to hide the target object

Guess you like

Origin blog.csdn.net/weixin_53407527/article/details/128627921