1.问题

(视频中不需要)

现在我们是个汉堡餐厅, 我们的汉堡种类很多, 各种夹心酱料不同组合
我们需要一个信息系统去描述不同的汉堡有哪些夹心, 多少钱
一开始我们是这么设计的

1.1 定义个接口

  1. public interface Hamburger {
  2. String facts();
  3. int cost();
  4. }

1.2 定义各种汉堡

  1. //牛肉汉堡
  2. public class BeefHamburger implements Hamburger{}
  3. //双层牛肉汉堡
  4. public class DoubleBeefHamburger implements Hamburger{}
  5. //双层牛肉加芝士汉堡
  6. public class DoubleBeefCheeseHamburger implements Hamburger{}
  7. //蔬菜汉堡
  8. public class VegetableHamburger implements Hamburger{}
  9. //蔬菜加番茄酱汉堡
  10. public class VegetableWithKetchupHamburger implements Hamburger{}
  11. //蔬菜加番茄酱和鱼肉汉堡
  12. public class VegetableWithKetchupAndFirshHamburger implements Hamburger{}
  13. ...

1.3 问题

发现问题没, 汉堡种类简直是排列组合
….

到后面汉堡种类肯定会不计其数
我们有没有办法用更简单的方式来计算汉堡价格和查看汉堡的夹心料呢?

2.如何用装饰者模式改造

(视频从这开始)

我们是个汉堡餐厅, 每个汉堡会加各种料, 我们希望加任何料之后我们都能获取到这个汉堡的价格
这里我们使用装饰者模式来实现它

为了实现装饰者模式我们需要四种Java类:

  • 被装饰者的基类
  • 装饰者的基类

  • 被装饰者

  • 装饰者

    2.1 定义 被装饰者 的基类

  • 这个Hamburger 是所有被装饰者的基类

    1. public abstract class Hamburger {
    2. //汉堡的组成
    3. public abstract String facts();
    4. //汉堡的价格
    5. public abstract int cost();
    6. }

2.2 定义 装饰者 的基类(非常重要)

  • MoreFilling 继承了 Hamburger, 为了保持行为一致
  • MoreFilling 是所有装饰者的基类
  • MoreFilling 要持有一个汉堡对象, 并由构造函数赋值这个对象

    1. //继承这个类的都被认为是装饰者
    2. public abstract class MoreFilling extends Hamburger {
    3. protected Hamburger hamburger;
    4. public MoreFilling(Hamburger hamburger) {
    5. this.hamburger = hamburger;
    6. }
    7. }

2.3 定义 被装饰者

  • 被装饰者一定是继承Hamburger

    1. //普通汉堡, 没有夹心
    2. public class PlainHamburger extends Hamburger {
    3. @Override
    4. public String facts() {
    5. return "两片面包";
    6. }
    7. @Override
    8. public int cost() {
    9. return 5;
    10. }
    11. }

2.4 定义 装饰者

  • 装饰者一定是继承 MoreFilling
  • 这里多举几个例子

    1. //汉堡加牛肉
    2. public class Beef extends MoreFilling {
    3. public Beef(Hamburger hamburger) {
    4. super(hamburger);
    5. }
    6. @Override
    7. public String facts() {
    8. return hamburger.facts() + ", 牛肉";
    9. }
    10. @Override
    11. public int cost() {
    12. return hamburger.cost() + 10;
    13. }
    14. }
    1. //汉堡加芝士
    2. public class Cheese extends MoreFilling{
    3. public Cheese(Hamburger hamburger) {
    4. super(hamburger);
    5. }
    6. @Override
    7. public String facts() {
    8. return hamburger.facts() + ", 芝士";
    9. }
    10. @Override
    11. public int cost() {
    12. return hamburger.cost() + 50;
    13. }
    14. }
    1. public class Chicken extends MoreFilling {
    2. public Chicken(Hamburger hamburger) {
    3. super(hamburger);
    4. }
    5. @Override
    6. public String facts() {
    7. return hamburger.facts() + ", 鸡肉";
    8. }
    9. @Override
    10. public int cost() {
    11. return hamburger.cost() + 8;
    12. }
    13. }

3. 验证

  1. public static void main(String[] args) {
  2. Hamburger h = new PlainHamburger();
  3. h = new Beef(h); //汉堡加牛肉
  4. h = new Cheese(h); //加芝士
  5. h = new Beef(h); //再加牛肉
  6. h = new Vegetable(h); //加蔬菜
  7. h = new Fish(h); //加鱼肉
  8. h = new Cheese(h); //加芝士
  9. h = new Chicken(h); //加鸡肉
  10. h = new Ketchup(h); //加番茄酱
  11. System.out.println("汉堡有哪些料: " + h.facts());
  12. System.out.println("汉堡的价格是: " + h.cost());
  13. }

image.png

OK 大功告成, 我们

4. 真实的使用

Java标准库中的IO流的设计就是典型的装饰者模式

略, 自己看代码去


5. 什么时候用装饰者模式

如果是新代码, 我的建议是不要用装饰者模式, 因为多个装饰者之间的代码在编译时没有严格的顺序约束, 但实际使用的时候可能会有顺序约束; 平添无谓的出错风险

再加上装饰者模式很容易写很多无用的装饰者, 让后面用你代码的人难以维护;

有人说, 你看JDK的IO流就是用了装饰者模式

我的回答是: 你猜为什么Netty没有用装饰者模式? 为什么Netty是全球最火的网络框架?