装饰器模式

基本介绍

装饰器模式意图为一个对象扩展新的功能,且不改变原有的结构,装饰器模式属于结构型设计模式

一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀

使用场景

  • 扩展一个类的功能
  • 动态增加功能,动态撤销

 

假设有一家咖啡公司,姑且咱就叫怪兽咖啡吧,该咖啡公司是以扩展速度最快而闻名的咖啡公司(像瑞幸咖啡一样),但是最近由于扩展速度太快,它们想重新设计一套订单系统,以满足它们现在的饮料需求。

他们最初始的设计如下:

 

然鹅用户在购买咖啡时,通常会添加一些调料,例如:摩卡、豆浆、奶泡等等。怪兽咖啡并根据业务去计算相应的总价,这就要求咖啡公司在设计订单时需要考虑这些调料的部分。然后就设计出这么一套庞杂的系统,因为奶茶种类太多,调料太多。种类*调料 = 类爆炸,实在太疯狂了

 

这么设计的问题显而易见

  • 调料价格变化时,需要更改现有代码
  • 出现新的调料,需要添加新的方法,并改动超类中的cost()方法
  • 以后开发新的饮料,对于这些饮料,某些调料并不适合,但是这个设计中,子类仍需继承那些不需要的方法

 

装饰器模式

从上述的设计方案来看,这显然并不是一个聪明的结果,因为会遇到 类爆炸、设计过于冗余,以及基类加入的新功能并不一定适用于所有子类

 

所以我们要考虑换一种方式,设想一下 我们能不能 以饮料为红花,用调料作为 绿叶 装饰它

比如,客户想要 摩卡和奶泡 搭配黑莓咖啡(口味独特),那么,我们怎么做呢?

  • 拿黑莓咖啡(DarkRoast)作为对象
  • 用摩卡(Mocha)对象装饰它
  • 用奶泡(Whip)对象装饰它
  • 调用 cost() 方法,并通过依赖将调料价格和摩卡价格想加

 

 

上图详细的介绍了,装饰模式的总体过程

 

上图为装饰器模式的结构类图以及各个类的作用说明

 

好,下面我们详细介绍订单系统的正确设计方案

1、我们先设计饮料组件

/**
 * 创建一个饮料抽象类
 */
public abstract class Beverage {

    String description = "UnKnown Beverage";

    public String getDescription() {
        return description;
    }

    public abstract Double cost();

}

 

2、假设目前店铺中,有 综合咖啡、浓缩咖啡、黑莓

public class HouseBlend extends Beverage {

    public HouseBlend() {
        description = "综合咖啡";
    }

    @Override
    public Double cost() {
        return 23.2;
    }
}
public class Expresso extends Beverage{

    public Expresso(){
        description = "浓缩咖啡";
    }

    @Override
    public Double cost() {
        return 19.9;
    }
}
public class DarkRoast extends Beverage {

    public DarkRoast() {
        description = "黑莓咖啡";
    }

    @Override
    public Double cost() {
        return 21.8;
    }

}

 

3、下面我们实现调料类的抽象类,也就是装饰者类

/**
 * 调料抽象类
 */
public abstract class Condiment extends Beverage {

    Beverage beverage;

    Condiment(Beverage decoratedBeverage){
        this.beverage = decoratedBeverage;
    }

    public abstract String getDescription();

}

 

4、 然后我们再实现相应的 Mocha、Whip、Soy 的调料代码

/**
 * 摩卡
 */
public class Mocha extends Condiment {


    public Mocha(Beverage beverage) {
        super(beverage);
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + "【摩卡】";
    }

    @Override
    public Double cost() {
        return new BigDecimal(beverage.cost().toString()).add(new BigDecimal(2.2)).doubleValue();
    }
}
/**
 * 豆浆
 */
public class Soy extends Condiment {

    public Soy(Beverage beverage) {
        super(beverage);
    }

    @Override
    public String getDescription(){
        return beverage.getDescription() + "【豆浆】";
    }

    @Override
    public Double cost() {
        return new BigDecimal(beverage.cost().toString()).add(new BigDecimal(3.6)).doubleValue();
    }
}
/**
 *  奶泡
 */
public class Whip extends Condiment {

    public Whip(Beverage beverage){
        super(beverage);
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + "【奶泡】";
    }

    @Override
    public Double cost() {
        return new BigDecimal(beverage.cost().toString()).add(new BigDecimal(2.7)).doubleValue();
    }
}

 

好了,参考装饰者模式,订单模块的基本实现已经基本完成。下面是测试代码

public class Client {
    public static void main(String[] args) {
        //一杯DarkRoast 不需要调料
        Beverage darkRoast = new DarkRoast();
        System.out.println(darkRoast.getDescription() + "," + darkRoast.cost());

        //再点一个浓缩咖啡 加 双倍摩卡 一份奶泡
        Beverage beverage = new Expresso();
        beverage = new Mocha(beverage);
        beverage = new Mocha(beverage);
        beverage = new Whip(beverage);
        System.out.println(beverage.getDescription() + "," + beverage.cost());
    }
}


 

IO体系中的装饰器


由图可见,InputStream就是装饰者模式中的超类(Component),
ByteArrayInputStream,FileInputStream相当于 被装饰者(ConcreteComponent),这些类都提供了最基本的字节读取功能。
而另外一个和这两个类是同一级的类FilterInputStream 即为 抽象装饰者(AbstarctDecorator)
BufferedInputStream,DataInputStream,PushbackInputStream(都继承了FilterInputStream类),它们为 装饰者(Decorator),在原有基础功能上都实现了功能的扩展和增强。
 
例:用BufferedInputStream 装饰 FileInputStream,和上面Mocha(摩卡) 装饰 DarkRoast(黑莓)如出一辙

File file = new File ("hello.txt"); 
FileInputStream in=new FileInputStream(file); 
BufferedInputStream inBuffered=new BufferedInputStream (in); 
posted @ 2020-07-20 14:05  丁可乐  阅读(629)  评论(0编辑  收藏  举报