10,装饰者模式(Decorator)

10.1,问题引入

10.1.1,星巴克咖啡订单项目

  • 咖啡种类:Espresso(意大利浓咖啡),LongBlack(美式咖啡),Decaf(无因咖啡)
  • 调料:Milk(牛奶),Soy(豆浆),Chocolate(巧克力)
  • 要求在增加新的咖啡时能有更好的扩展性,改动方便,维护方便
  • 使用OO计算不同种类咖啡的价格:包括咖啡价格和调料价格

10.1.2,方式一:穷举类方式

java k java 咖啡_设计模式

  • Drink:是顶层抽象类,表示饮品,price是咖啡价格, description是对咖啡的描述,cost()方法是计算最终咖啡价格
  • 图中第一层的子类表示单品咖啡,即列举出所有的单品咖啡以供单点
  • 图中第二层的子类表示单品咖啡+调料,即对所有可能的咖啡+调料进行组合列举
  • 这种设计方式会造成绝对的类爆炸,优点扯淡,我也不知道我为什么要列它。。。

10.1.3,方式二:聚合方式

java k java 咖啡_Java_02

  • 这种方式较第一种方式的改观,是避免了类爆炸,将调料以聚合的方式依赖到单品咖啡中,在买咖啡时候可以进行调料添加
  • 但是如果需要添加一份调料,此时需要对所有单品咖啡部分代码进行变更,不符合OCP原则
  • 此时可以考虑装饰者模式

10.2,基本介绍

  • 装饰者模式:动态的将新功能附加到对象上,在对象功能扩展方面,比继承更具有弹性,装饰者模式符合开闭原则(OCP原则)
  • 装饰者模式是将整体功能分为两部分,即主体(被装饰者 Component)部分包装(装饰者 Decorator)部分,装饰者通过组合顶层接口,对被装饰者或者*装饰者(包装再包装)*进行包装
  • 当需要增加被装饰者或者装饰者时,只需要添加对应的类即可,后续在客户端即可适配装饰方式

10.3,类图

java k java 咖啡_设计模式_03

  • 顶层抽象类:IDrink,保证整个体系中的强一致性
  • 被装饰者部分:单品咖啡部分,列举出所有的单品咖啡
  • 装饰者部分顶层类:Decorator,对整个调料部分进行管理,进行消费计算
  • 装饰者部分:调料部分,列举出所有调料
  • 最后用装饰者对被装饰者进行组合,再进行对象装饰时构造为一个新的对象,该对象内部的drink属性即表示被装饰过的被装饰者或者装饰者对象

10.4,代码示例

  • 顶层抽象类:IDrink
package com.self.designmode.decorator;

import lombok.Getter;
import lombok.Setter;

/**
 * 装饰者模式: 顶层抽象类, 确保强一致
 * @author PJ_ZHANG
 * @create 2020-07-28 18:11
 **/
@Getter
@Setter
public abstract class IDrink {
    // 价格
    private int price;
    // 描述
    private String des;
    // 花费
    abstract int cost();
}
  • 被装饰者类:LongBlack
package com.self.designmode.decorator;

/**
 * 装饰者:被装饰者类
 * @author PJ_ZHANG
 * @create 2020-07-28 18:18
 **/
public class LongBlack extends IDrink {
    public LongBlack() {
        setDes("美氏咖啡...");
        setPrice(20);
    }
    @Override
    int cost() {
        System.out.println(getDes() + " : " + getPrice());
        return getPrice();
    }
}
  • 被装饰者类:Espresso
package com.self.designmode.decorator;

/**
 * @author PJ_ZHANG
 * @create 2020-07-28 18:20
 **/
public class Espresso extends IDrink {
    public Espresso() {
        setDes("意氏咖啡...");
        setPrice(30);
    }
    @Override
    int cost() {
        System.out.println(getDes() + " : " + getPrice());
        return getPrice();
    }
}
  • 被装饰者类:Decaf
package com.self.designmode.decorator;

/**
 * @author PJ_ZHANG
 * @create 2020-07-28 18:21
 **/
public class Decaf extends IDrink {
    public Decaf() {
        setDes("无因咖啡...");
        setPrice(30);
    }
    @Override
    int cost() {
        System.out.println(getDes() + " : " + getPrice());
        return getPrice();
    }
}
  • 装饰者顶层类:Decorator
package com.self.designmode.decorator;

/**
 * 装饰者: 顶层装饰者类
 * @author PJ_ZHANG
 * @create 2020-07-28 18:22
 **/
public class Decorator extends IDrink {
    private IDrink drink;
    public Decorator(IDrink drink) {
        this.drink = drink;
    }
    @Override
    int cost() {
        System.out.println(this.getDes() + " : " + this.getPrice());
        return drink.cost() + this.getPrice();
    }
}
  • 装饰者类:Milk
package com.self.designmode.decorator;

/**
 * 装饰者: 装饰者类,加牛奶
 * @author PJ_ZHANG
 * @create 2020-07-28 18:25
 **/
public class Milk extends Decorator {
    public Milk(IDrink drink) {
        super(drink);
        setDes("加牛奶...");
        setPrice(5);
    }
}
  • 装饰者类:Soy
package com.self.designmode.decorator;

/**
 * 装饰者: 装饰者类,加豆浆
 * @author PJ_ZHANG
 * @create 2020-07-28 18:26
 **/
public class Soy extends Decorator {
    public Soy(IDrink drink) {
        super(drink);
        setDes("加豆浆...");
        setPrice(3);
    }
}
  • 装饰者类:Chocolate
package com.self.designmode.decorator;

/**
 * 装饰者: 装饰者类,加巧克力
 * @author PJ_ZHANG
 * @create 2020-07-28 18:26
 **/
public class Chocolate extends Decorator {
    public Chocolate(IDrink drink) {
        super(drink);
        setDes("加巧克力...");
        setPrice(2);
    }
}
  • 客户端:Client
package com.self.designmode.decorator;

/**
 * 装饰者模式客户端
 * @author PJ_ZHANG
 * @create 2020-07-28 18:18
 **/
public class Client {
    public static void main(String[] args) {
        int cost = new Chocolate(new Milk(new LongBlack())).cost();
        System.out.println(cost);
    }
}