设计模式轻松学【十一】装饰模式

简介: 如果要扩展一些功能,我们可以采用装饰模式来实现。装饰者模式以对客户透明的方式动态地给一个对象附加上更多的责任。换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰者模式可以在不使用创造更多子类的情况下,将对象的功能加以扩展。

定义与特点

  • 定义

    装饰者模式动态地将责任附加到对象身上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

  • 详细概述

    装饰者模式又名包装(Wrapper)模式,是一种对象结构型模式。是一种用于替代继承的技术,它通过一种无须定义子类的方式来给对象动态增加职责,使用对象之间的关联关系取代类之间的继承关系。

  • 参与角色

    • 抽象构件(Component):具体构件和抽象装饰类的共同父类,声明了在具体构件中实现的业务方法。
    • 具体构件(ConcreteComponent):实现了抽象构件中声明的方法,装饰器可以给它增加额外的职责(方法)。
    • 抽象装饰(Decorator):继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
    • 具体装饰(ConcreteDecorator):抽象装饰类的子类,负责向具体构件添加新的职责。每一个具体装饰类都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法,并可以增加新的方法用以扩充对象的行为。
  • 类结构图

    image.png

  • 结构代码分析

    • 定义抽象构件

      interface Component{
          public void operation();
      }
    • 具体构件

      class ConcreteComponent implements Component{
          public ConcreteComponent(){
              System.out.println("创建具体构件角色");       
          }   
          public void operation(){
              System.out.println("调用具体构件角色的方法operation()");           
          }
      }
    • 抽象装饰类

      class Decorator implements Component{
      
          private Component component;
          
          public Decorator(Component component){
              this.component = component;
          }
          
          public void operation(){
              component.operation();
          }
      }
    • 具体装饰类

      //具体装饰角色
      class ConcreteDecorator extends Decorator{
      
          public ConcreteDecorator(Component component){
              super(component);
          }   
          
          public void operation(){
              super.operation();
              addedFunction();
          }
          
          public void addedFunction(){
              System.out.println("为具体构件角色增加额外的功能addedFunction()");           
          }
      }
    • 客户端调用

      public class DecoratorPattern{
          public static void main(String[] args){
              Component p = new ConcreteComponent();
              p.operation();
              
              System.out.println("---------------------------------");
              
              Component d = new ConcreteDecorator(p);
              d.operation();
          }
      }
      
      //运行结果
      创建具体构件角色
      调用具体构件角色的方法operation()
      ---------------------------------
      调用具体构件角色的方法operation()
      为具体构件角色增加额外的功能addedFunction()

模式实现分析

清楚了装饰模式的结构组成后,我们来做一个小小的手抓饼案例。我们知道,手抓饼主要靠配料挣钱,比如加黄瓜、煎蛋、火腿之类的,每个配料不一样价格也不一样。

image.png

这个手抓饼加配料的过程,我们采用装饰模式来解决,主体是手抓饼和肉夹馍,而配料则是装饰者,我先用UML类图来描述一下类之间的协作关系。

image.png

  • 抽象构件

    public abstract class Pancake {
        
        public String desc = "抽象饼";
    
        public String getDesc() {
            return desc;
        }
        
        public abstract double price();
    
    }
  • 具体构件

    public class Roujiamo extends Pancake {
        public Roujiamo() {
            desc = "肉夹馍";
        }
    
        @Override
        public double price() {
            return 6;
        }
    }
  • 抽象装饰类

    public abstract class Condiment extends Pancake {
        public abstract String getDesc();
    }
  • 具体装饰类

    //煎蛋
    public class FiredEgg extends Condiment {
        private Pancake pancake;
        
        public FiredEgg(Pancake pancake) {
            this.pancake = pancake;
        }
    
        @Override
        public String getDesc() {
            return pancake.getDesc() + ", 煎蛋";
        }
    
        @Override
        public double price() {
            return pancake.price() + 2;
        }
    }
    
    //火腿
    public class Ham extends Condiment {
        private Pancake pancake;
        
        public Ham(Pancake pancake) {
            this.pancake = pancake;
        }
    
        @Override
        public String getDesc() {
            return pancake.getDesc() + ", 火腿";
        }
    
        @Override
        public double price() {
            return pancake.price() + 1.5;
        }
    }
  • 客户端调用程序

    public class MyTest {
        
        @Test
        public void test() {
            Pancake roujiamo = new Roujiamo();
            roujiamo = new FiredEgg(roujiamo);
            roujiamo = new FiredEgg(roujiamo);
            roujiamo = new Ham(roujiamo);
            roujiamo = new MeatFloss(roujiamo);
            roujiamo = new Cucumber(roujiamo);
            //我好饿
            System.out.println(String.format("%s ¥%s", roujiamo.getDesc(), roujiamo.price()));
        }
    
    }

由于具体构件类和装饰类都实现了相同的抽象构件接口,因此装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任,换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。

譬如我们给肉夹馍加上一个鸡蛋可以这么写 roujiamo = new FiredEgg(roujiamo);,客户端仍然可以把 roujiamo 当成原来的 roujiamo一样,不过现在的 roujiamo已经被装饰加上了鸡蛋

总结

  • 优点

    (1)装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。装饰模式允许系统动态决定“贴上”一个需要的“装饰”,或者除掉一个不需要的“装饰”。继承关系则不同,继承关系是静态的,它在系统运行前就决定了。

    (2)通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。

  • 缺点

    由于使用装饰模式,可以比使用继承关系需要较少数目的类。使用较少的类,当然使设计比较易于进行。但是,在另一方面,使用装饰模式会产生比使用继承关系更多的对象。更多的对象会使得查错变得困难,特别是这些对象看上去都很相像。

JDK中的应用

装饰者模式在Java语言中的最著名的应用莫过于Java I/O标准库的设计了。

image.png

根据上图可以看出:

  • 抽象构件(Component)角色:InputStream,OutputStream,Reader,Writer,为各种子类型提供统一的接口。
  • 具体构件(ConcreteComponent)角色:由ByteArrayInputStream、FileInputStream、PipedInputStream、StringBufferInputStream等类扮演。它们实现了抽象构件角色所规定的接口。
  • 抽象装饰(Decorator)角色:FilterInputStream,FilterOutputStream。它实现了InputStream所规定的接口。
  • 具体装饰(ConcreteDecorator)角色:由几个类扮演,分别是BufferedInputStream、DataInputStream以及两个不常用到的类LineNumberInputStream、PushbackInputStream。
相关文章
|
21天前
|
设计模式 PHP
php设计模式--装饰模式(七)装饰模式完成文章编辑
php设计模式--装饰模式(七)装饰模式完成文章编辑
12 0
|
4月前
|
设计模式 中间件 PHP
设计模式 | 装饰模式
设计模式 | 装饰模式
15 0
|
6月前
|
设计模式
设计模式系列教程(12) - 装饰模式
设计模式系列教程(12) - 装饰模式
16 0
|
7月前
|
设计模式 算法 uml
结构型设计模式01-装饰模式
结构型设计模式01-装饰模式
15 0
|
7月前
|
设计模式
设计模式13 - 装饰模式【Decorator Pattern】
设计模式13 - 装饰模式【Decorator Pattern】
15 0
|
6天前
|
设计模式 Go
[设计模式 Go实现] 结构型~装饰模式
[设计模式 Go实现] 结构型~装饰模式
|
10月前
|
设计模式
设计模式——装饰模式
设计模式——装饰模式
43 0
|
5月前
|
设计模式 容器
设计模式之装饰模式(2)--有意思的想法
设计模式之装饰模式(2)--有意思的想法
|
5月前
|
设计模式
设计模式之装饰模式--优雅的增强
设计模式之装饰模式--优雅的增强
设计模式之装饰模式--优雅的增强
|
7月前
|
设计模式 Java 数据库连接
JAVA设计模式8:装饰模式,动态地将责任附加到对象上,扩展对象的功能
JAVA设计模式8:装饰模式,动态地将责任附加到对象上,扩展对象的功能