美文网首页
结构型模式——装饰者模式(二)

结构型模式——装饰者模式(二)

作者: 最后的轻语_dd43 | 来源:发表于2019-05-22 09:37 被阅读0次

    该项目源码地址:https://github.com/lastwhispers/code/tree/master/java-basic/design-pattern
    (设计模式相关代码与笔记)

    1. 定义

    在不改变原有对象的基础之上,动态地给一个对象添加一些额外的职责。

    2. 适用场景

    • 扩展一个类的功能或给一个类添加附加职责
    • 动态的给一个对象添加功能,这些功能可以再动态的撤销
    • 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。

    例如:在java的I/O类中有很深刻的体现。

    3. 类图与角色

    装饰者模式类图
    • Component(被装饰对象的基类)
      ​ 定义一个对象接口,可以给这些对象动态地添加职责。
    • ConcreteComponent(具体被装饰对象)
      ​ 定义一个对象,可以给这个对象添加一些职责。
    • Decorator(装饰者抽象类)
      ​ 维持一个指向Component实例的引用,并定义一个与Component接口一致的接口。
    • ConcreteDecorator(具体装饰者)
      ​ 具体的装饰对象,给内部持有的具体被装饰对象,增加具体的职责。

    4. 相关设计模式

    装饰者模式和代理模式

    • 装饰者模式:关注在一个对象上动态添加方法。
    • 代理模式:关注在控制对对象的访问,代理模式的代理类可以对客户隐藏一个对象具体的信息。
    • 在使用代理模式的时候,常常在一个代理对象中创建对象的实例。而当我们在使用装饰者模式时,通常会把原始对象作为参数,传给装饰者的构造器

    装饰者模式和适配器模式

    • 装饰者模式和适配器模式都叫做包装模式。在装饰者模式中,装饰者与被装饰者都可以实现相同的接口,或者装饰者是被装饰者的子类。在适配器模式中,适配类与被适配类具有不同的接口,当然也有可能有部分接口是重合的。

    5. 模式实例

    背景:公司门口有一个小摊卖煎饼的,点了煎饼之后往往还可以在这个基础之上增加一些配料,例如煎蛋,火腿等等,每个配料的价格都不一样,不管你怎么配配料,最终价格是煎饼基础价加上每一种所选配料价格的总和。

    5.1 V1版本

    (1)相关类

    有一个煎饼类:

    public class Battercake {
        protected String getDesc() {
            return "煎饼";
        }
        protected int cost() {
            return 8;
        }
    }
    

    如果此时需要一个加鸡蛋的煎饼,只需要让煎饼加鸡蛋类继承煎饼类即可。

    煎饼加鸡蛋类:

    public class BattercakeWithEgg extends Battercake {
        @Override
        public String getDesc() {
            return super.getDesc()+"加一个鸡蛋";
        }
    
        @Override
        public int cost() {
            return super.cost()+1;
        }
    
    }
    

    如果此时需要一个加鸡蛋加香肠的煎饼,只需要让煎饼加鸡蛋加香肠类继承煎饼加鸡蛋类即可。

    煎饼加鸡蛋加香肠类:

    public class BattercakeWithEggSausage extends BattercakeWithEgg{
        @Override
        public String getDesc() {
            return super.getDesc()+"一个香肠";
        }
    
        @Override
        public int cost() {
            return super.cost()+2;
        }
    }
    

    (2)测试

    public class Test {
        public static void main(String[]args){
            Battercake battercake = new Battercake();
            System.out.println(battercake.getDesc()+"销售价格:"+battercake.cost());
    
            BattercakeWithEgg battercakeWithEgg = new BattercakeWithEgg();
            System.out.println(battercakeWithEgg.getDesc()+"销售价格:"+battercakeWithEgg.cost());
    
            BattercakeWithEggSausage battercakeWithEggSausage = new BattercakeWithEggSausage();
            System.out.println(battercakeWithEggSausage.getDesc()+"销售价格:"+battercakeWithEggSausage.cost());
    
        }
    }
    

    测试结果:

    测试结果

    此时类图,单纯的继承结构:

    类图

    如果我们此时想要一个煎饼加两个鸡蛋,就需要重新继承煎饼类Battercake,生成一个“加两个鸡蛋的煎饼类”。加三个鸡蛋...以此类推,每加一种类型的煎饼就会多生成一个类。下面使用装饰者模式进行改造。

    5.2 V2版本

    现在,我们用装饰者模式来改写V1版本:

    (1)相关类

    Component 有一个抽象的煎饼类:

    public abstract class ABattercake {
        protected abstract String getDesc();
        protected abstract int cost();
    }
    

    ConcreteComponent 煎饼类继承于上面的抽象兼饼类:

    public class Battercate extends ABattercake {
        @Override
        protected String getDesc() {
            return "煎饼";
        }
    
        @Override
        protected int cost() {
            return 8;
        }
    }
    

    Decorator 我们让抽象的装饰者类继承抽象的实体:

    public class AbstractDecorator extends ABattercake{
        private ABattercake aBattercake;
    
        public AbstractDecorator(ABattercake aBattercake) {
            this.aBattercake = aBattercake;
        }
    
        @Override
        protected String getDesc() {
            return aBattercake.getDesc();
        }
    
        @Override
        protected int cost() {
            return aBattercake.cost();
        }
    }
    

    ConcreteDectrator 煎饼添加鸡蛋的装饰类继承于抽象的装饰类:

    public class EggDecorator extends AbstractDecorator{
    
        public EggDecorator(ABattercake aBattercake) {
            super(aBattercake);
        }
    
        @Override
        protected String getDesc() {
            return super.getDesc()+"加一个鸡蛋";
        }
    
        @Override
        protected int cost() {
            return super.cost()+1;
        }
    }
    

    ConcreteDectrator 煎饼添加香肠的装饰类,继承于抽象的装饰类:

    public class SausageDecorator extends AbstractDecorator{
    
        public SausageDecorator(ABattercake aBattercake) {
            super(aBattercake);
        }
    
        @Override
        protected String getDesc() {
            return super.getDesc()+"加一根香肠";
        }
    
        @Override
        protected int cost() {
            return super.cost()+2;
        }
    }
    

    (2)测试

    public class Test {
        public static void main(String[]args){
            ABattercake aBattercake;
            aBattercake = new Battercate();
            aBattercake = new EggDecorator(aBattercake);
            aBattercake = new EggDecorator(aBattercake);
            aBattercake = new SausageDecorator(aBattercake);
            System.out.println(aBattercake.getDesc()+"价格为:"+aBattercake.cost());
        }
    }
    

    测试结果:

    测试结果

    此时不管想加多少个鸡蛋还是香肠,都可以很灵活的添加上。

    此时的类图:

    V2版本类图

    6. 优缺点

    优点:

    • 继承的有力补充,比继承灵活,不改变原有对象的情况下给一个对象扩展功能
    • 通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果
    • 符合开闭原则

    缺点:

    • 会出现更多的代码,更多的类,增加程序复杂性
    • 动态装饰时,多层装饰时会使程序更复杂

    7. 扩展-JDK1.7以及框架源码中的装饰者模式

    7.1 Java I/O

    Reader与java.io.BufferedReader

    Reader BufferedReader

    Reader是被装饰者,BufferedReader是装饰者

    InputStream与FilterInputStream

    InputStream FilterInputStream

    InputStream是被装饰者,FilterInputStream是装饰者

    下面是InputStream相关类图的装饰者模式体现。

    InputStream类结构

    7.2 Spring

    TransactionAwareCacheDecorator

    Cache TransactionAwareCacheDecorator

    7.3 Spring-session

    org.springframework.session.web.http.SessionRepositoryFilter.SessionRepositoryRequestWrapper

    7.4 Tomcat

    javax.servlet.ServletRequestWrapper

    ServletRequestWrapper

    7.5 Mybatis

    org.apache.ibatis.cache.Cache

    org.apache.ibatis.cache.Cache

    相关文章

      网友评论

          本文标题:结构型模式——装饰者模式(二)

          本文链接:https://www.haomeiwen.com/subject/gynezqtx.html