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

该项目源码地址: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

你可能感兴趣的:(结构型模式——装饰者模式(二))