05-03-设计模式 装饰模式

2022-05-28 09:22:25 浏览数 (1)

星巴克咖啡订单项目(咖啡馆)

  1. 咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡)
  2. 调料:Milk、Soy(豆浆)、Chocolate
  3. 要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便
  4. 使用OO的来计算不同种类咖啡的费用:客户可以点单品咖啡,也可以单品咖啡 调料组合。

方案一实现

其实这个就是穷举, 将所有的方式拼接

分析问题

  1. Drink是一个抽象类,表示饮料
  2. des就是对咖啡的描述,比如咖啡的名字
  3. cost()方法就是计算费用,Drink类中做成一个抽象方法.
  4. Decaf就是单品咖啡,继承Drink,并实现cost
  5. Espress&&Milk就是单品咖啡 调料,这个组合很多
  6. 问题:这样设计,会有很多类,当我们增加一个单品咖啡,或者一个新的调料,类的数量就会倍增,就会出现类爆炸

方案二实现

前面分析到方案1因为咖啡单品 调料组合会造成类的倍增,因此可以做改进,将调料内置到Drink类,这样就不会造成类数量过多。从而提高项目的维护性(如图)

说明:milk,soy,chocolate可以设计为Boolean,表示是否要添加相应的调料.

分析问题

  1. 方案2可以控制类的数量,不至于造成很多的类
  2. 在增加或者删除调料种类时,代码的维护量很大
  3. 考虑到用户可以添加多份调料时,可以将hasMilk返回一个对应int
  4. 考虑使用装饰者模式

装饰者模式定义

  1. 装饰者模式: 动态的将新功能附加到对象上, 在对象功能扩展方面, 它比继承更有弹性, 装饰者模式也体现了开闭原则
  2. 这里提到的动态的将新功能附加到对象和ocp原则, 在后面的应用实例上会以代码的形式体现

装饰者模式原理

  • 装饰者模式就像打包一个快递
    • 主体:比如:陶瓷、衣服(Component)//被装饰者
    • 包装:比如:报纸填充、塑料泡沫、纸板、木板(Decorator)
  • Component主体:比如类似前面的Drink
  • ConcreteComponent和Decorator
    • ConcreteComponent:具体的主体,比如前面的各个单品咖啡
  • Decorator:装饰者,比如各调料.

在如图的Component与ConcreteComponent之间,如果ConcreteComponent类很多,还可以设计一个缓冲层,将共有的部分提取出来,抽象层一个类。

装饰者模式解决方案

装饰者模式下的订单: 2份巧克力 一份牛奶的LongBlack

装饰者模式解决方案代码实现

代码语言:javascript复制
package com.flower.zhaungshizhe;

import lombok.AllArgsConstructor;
import lombok.Data;

public class TestMain {
    public static void main(String[] args) {
        // 装饰者模式下订单 2份巧克力   一份牛奶   一份LongBlank咖啡
        // 一份LongBlank咖啡
        Drink drink = new LongBlack();
        // 添加一份牛奶
        drink = new Milk(drink);
        // 添加一份巧克力
        drink = new Chocolate(drink);
        // 添加二份巧克力
        drink = new Chocolate(drink);
        float cost = drink.cost();
        System.out.println("价格为:"   cost);
        System.out.println(drink.getDes());
    }
}

/**
 * 饮用抽象类
 */
@Data
abstract class Drink{
    public String des;
    private float price = 0.0F;
    // 计算价格
    public abstract float cost();
}

/**
 * 咖啡类
 */
class Coffee extends Drink{

    @Override
    public float cost() {
        return super.getPrice();
    }
}

/**
 * 意大利咖啡
 */
class Espresso extends Coffee{
    public Espresso(){
        setDes("意大利咖啡");
        setPrice(6.0F);
    }
}
/**
 * 美式咖啡
 */
class LongBlack extends Coffee{
    public LongBlack(){
        setDes("美式咖啡");
        setPrice(10.0F);
    }
}

/**
 * 装饰者
 */
@AllArgsConstructor
class Decorator extends Drink{
    // 被装饰者
    private Drink drink;
    @Override
    public float cost() {
        // 自身价格   coffee价格
        return super.getPrice()   drink.cost();
    }

    @Override
    public String getDes() {
        return super.getDes()   " "   super.getPrice()   " && "   drink.getDes()   " "   drink.getPrice();
    }
}

/**
 * 巧克力
 */
class Chocolate extends Decorator{
    public Chocolate(Drink drink) {
        super(drink);
        setDes("巧克力调味品");
        setPrice(3.0F);
    }
}

/**
 * 牛奶
 */
class Milk extends Decorator{
    public Milk(Drink drink) {
        super(drink);
        setDes("牛奶调味品");
        setPrice(2.0F);
    }
}

类图

源码剖析

JDK源码

Java的IO结构FilterInputStream就是一个装饰者

0 人点赞