1、星巴克咖啡订单项目

1)咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡)

2)调料:Milk、Soy(豆浆)、Chocolate

3)要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便

4)使用 OO 的来计算不同种类咖啡的费用:客户可以点单品咖啡,也可以单品咖啡+调料组合。

2、方案 1

2.1 思路分析:

方案 1-较差的方案

vv644

2.2 问题分析

1)Drink 是一个抽象类,表示饮料

2)description 就是对咖啡的描述,比如咖啡的名字

3)cost()方法就是计算费用,Drink 类做成一个抽象方法。

4)Decaf 就是单品咖啡,继承 Drink,并实现 cost()

5)Espresso && Milk 就是单品咖啡+调料,这个组合很多

6)问题:这样设计,会有很多类,当我们增加一个单品咖啡,或者一个新的调料,类的数量就会倍增,就会出现类爆炸。

3、方案 2

3.1 思路分析:

方案 2-解决星巴克咖啡订单项目(好点)

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

3y2vo

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

3.2 问题分析

1)方案 2 可以控制类的数量,不至于造成很多的类

2)在增加或者删除调料种类时,代码的维护量很大

3)考虑到用户可以添加多份调料时,可以将 hasMilk 返回一个对应 int

4)考虑使用装饰者模式

4、装饰者模式

4.1 定义

1)装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,他比继承更有弹性,装饰着模式也体现了开闭原则(ocp)

2)这里提到的动态的将新功能附加到对象和 ocp 原则,在后面的应用实例上会以代码的形式体验,需要细细体会。

4.2 原理

mavk8

1)装饰者模式就像打包一个快递

  • 主体:比如陶瓷、衣服(Component) // 被装饰者
  • 包装:比如报纸填充、塑料泡沫、纸板、木板(Decorator)// 装饰者

2)Component

主体:比如类似前面的 Drink

3)ConcreteComponent 和 Decorator

  • ConcreteComponent:具体的主体,比如前面的各个单品咖啡
  • Decorator:装饰者,比如各调料

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

4.3 应用实例

1)思路分析

gg2dk

说明:

  1. Drink 类就是前面说的抽象类,Component
  2. ShortBlack 就是单品咖啡
  3. Decorator 是一个装饰类,含有一个被装饰的对象(Drink obj)
  4. Decorator 的 cost 方法进行一个费用的叠加计算,递归的计算架构

2)订单细节说明

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

b20o8

说明:

  1. Milk 包含了 LongBlack
  2. 一份 Chocolate 包含了(Milk+LongBlack)
  3. 一份 Chocolate 包含了(Chocolate+Milk+LongBlack)
  4. 这样不管是什么形式的单品咖啡+调料组合,通过递归的方式可以方便的组合和维护

3)代码实现

Drink 饮料抽象类

package com.wenze.design.decorator;  
  
import lombok.Getter;  
import lombok.Setter;  
  
/**  
 * 饮料抽象类  
 *  
 * @author wenze  
 * @version 1.0  
 * @Date 2023-10-25 18:37:13  
 * @since 1.0  
 */
@Setter  
@Getter  
public abstract class Drink {  
    /**  
     * 描述  
     */  
    public String description;  
    /**  
     * 价格  
     */  
    private double price;  
    public abstract double cost();  
}

Coffee 咖啡类

package com.wenze.design.decorator;  
  
import com.wenze.design.decorator.Drink;  
  
/**  
 * 咖啡类  
 *  
 * @author wenze  
 * @version 1.0  
 * @Date 2023-10-25 18:39:04  
 * @since 1.0  
 */
public class Coffee extends Drink {  
    @Override  
    public double cost() {  
        return super.getPrice();  
    }  
}

Espresso 意大利浓咖啡类

package com.wenze.design.decorator.coffee;  
  
import com.wenze.design.decorator.Coffee;  
  
/**  
 * 意大利浓咖啡  
 *  
 * @author wenze  
 * @version 1.0  
 * @Date 2023-10-25 18:39:31  
 * @since 1.0  
 */
public class Espresso extends Coffee {  
    public Espresso() {  
        setDescription("意大利浓咖啡");  
        setPrice(6.0);  
    }  
}

Decorator 装饰类

package com.wenze.design.decorator;  
  
/**  
 * 装饰者  
 *  
 * @author wenze  
 * @version 1.0  
 * @Date 2023-10-25 18:42:35  
 * @since 1.0  
 */
public class Decorator extends Drink {  
    private Drink obj;  
    public Decorator(final Drink obj) {  
        this.obj = obj;  
    }  
    @Override  
    public double cost() {  
        // 自己的价格 + drink 的价格  
        return super.getPrice() + obj.cost();  
    }  
    @Override  
    public String getDescription() {  
        // 自己的描述+自己的价格+被装饰者的描述  
        return super.description + " " + super.getPrice() + " && " + obj.getDescription();  
    }  
}

Chocolate 巧克力类

package com.wenze.design.decorator.decorator;  
  
import com.wenze.design.decorator.Decorator;  
import com.wenze.design.decorator.Drink;  
  
/**  
 * 巧克力,具体的Decorator,这里就是调味品  
 *  
 * @author wenze  
 * @version 1.0  
 * @Date 2023-10-25 18:51:04  
 * @since 1.0  
 */
public class Chocolate extends Decorator {  
    public Chocolate(final Drink obj) {  
        super(obj);  
        setDescription("巧克力");  
        setPrice(3.0);  
    }  
}

Milk 牛奶类

package com.wenze.design.decorator.decorator;  
  
import com.wenze.design.decorator.Decorator;  
import com.wenze.design.decorator.Drink;  
  
/**  
 * 牛奶  
 *  
 * @author wenze  
 * @version 1.0  
 * @Date 2023-10-25 18:52:12  
 * @since 1.0  
 */
public class Milk extends Decorator {  
    public Milk(final Drink obj) {  
        super(obj);  
        setDescription("牛奶");  
        setPrice(2.0);  
    }  
}

CoffeeBar 咖啡店客户端类

package com.wenze.design.decorator;  
  
import com.wenze.design.decorator.coffee.LongBlack;  
import com.wenze.design.decorator.decorator.Chocolate;  
import com.wenze.design.decorator.decorator.Milk;  
  
/**  
 * 咖啡店,客户端  
 *  
 * @author wenze  
 * @version 1.0  
 * @Date 2023-10-25 18:53:19  
 * @since 1.0  
 */
public class CoffeeBar {  
    public static void main(String[] args) {  
        // 2份巧克力+1 份牛奶+LongBlack  
		
        // 1. 点一份 LongBlack        Drink order = new LongBlack();  
		
        // 2. 加入一份牛奶  
        order = new Milk(order);  
		
        // 3. 加入一份巧克力  
        order = new Chocolate(order);  
		
        // 4. 加入第二份巧克力  
        order = new Chocolate(order);  
		  
        System.out.println("order price: " + order.cost());  
		  
        System.out.println("order description: " + order.getDescription());  
    }  
}

结果为:

1ruyq

5、装饰者模式在 JDK 应用的源码分析

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

n9tjd

// 是一个抽象类,即 Component
public abstract class InputStream implements Closeable{}
// 是一个装饰者类 Decorator
public class FilterInputStream extends InputStream {
	// 被装饰的对象
	protected volatile InputStream in;
}
// FilterInputStream子类,也继承了被装饰的对象 in
class DataInputStream extends FilterInputStream implements DataInput {}

说明:

  1. InputStream 是抽象类,类似我们前面的 Drink
  2. FileInputStream 是 InputStream 子类,类似前面的 Espresso,LongBlack
  3. FilterInputStream 是 InputStream 子类,类似前面的 Decorator 装饰者
  4. DataInputStream 是 FilterInputStream 子类,具体的修饰者,类似前面的 Milk等
  5. FilterInputStream 类 有 protected volatile InputStream in; 即含被装饰者
  6. 分析得出在 jdk 的 io 体系中,就是使用装饰者模式