《Head First 设计模式》读书笔记——装饰者模式

读书继续,今天看书中讲到的第三个设计模式,装饰者模式, 通过装饰,我们可以在不修改代码的基础上,给这段代码赋予新的功能。

装饰者模式概念

装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

这个概念看起来很简单,名字也很通俗,装饰,就好比包装袋一样,我们如果给他套上生日礼物的外包装,那它就可以当作生日礼物,套上定情信物的包装,那它就可以当作定情信物。

书里给的例子是咖啡厅买咖啡,需要添加什么材料添加什么材料。我们需要在咖啡中加入各种调料,比如蒸奶、豆浆、摩卡或者覆盖奶泡等等。而咖啡店会根据加入的调料而收取不同的费用。

最简单的构建方式就是设计一个超类 Beerage,包括几个属性比如牛奶、豆浆等等,一个 cost() 方法,计算各种调料的总价,子类会重写 cost() 方法加上基本饮料的价钱。

一个错误的示范

延承上面咖啡的例子,我们来看一下一个非常让人头疼的设计。

最先是调料类,重复的结构设计。

public class Milk {
    private String description;
    private float price;

    public Milk(float price) {
        this.description = "Milk";
        this.price = price;
    }

    public float getPrice() {
        return price;
    }

    @Override
    public String toString() {
        return "Milk{" +
                "price=" + price +
                '}';
    }
}

public class Mocha {
    private String description;
    private float price;

    public Mocha(float price) {
        this.description = "Mocha";
        this.price = price;
    }

    public float getPrice() {
        return price;
    }

    @Override
    public String toString() {
        return "Mocha{" +
                "price=" + price +
                '}';
    }
}

调料类就简单的放两个吧,毕竟让我写的麻烦了,其实只要有两个属性以及一个 getter() 方法就足够了。接下来是饮料超类,包含所有的调料属性,并且还有一个 cost() 方法,计算所有调料的总价。

public class Beverage {
    private String description;
    private Milk milk;
    private Soy soy;
    private Mocha mocha;
    private Whip whip;
    private float price;

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Milk getMilk() {
        return milk;
    }

    public void setMilk(Milk milk) {
        this.milk = milk;
    }

    public Soy getSoy() {
        return soy;
    }

    public void setSoy(Soy soy) {
        this.soy = soy;
    }

    public Mocha getMocha() {
        return mocha;
    }

    public void setMocha(Mocha mocha) {
        this.mocha = mocha;
    }

    public Whip getWhip() {
        return whip;
    }

    public void setWhip(Whip whip) {
        this.whip = whip;
    }

    public float getPrice() {
        return price;
    }

    public void setPrice(float price) {
        this.price = price;
    }

    public float cost() {
        return milk.getPrice() + soy.getPrice() + mocha.getPrice() + whip.getPrice();
    }
}

最后就是饮料类了,随便设计一个 Aco 类,这就是一个饮料类,主要作用就是重写 cost() 方法,进行饮料和配料价格的计算并返回。

忍不了有没有?这个设计太蠢了,每一种饮料都要新设计一个新类,如果配料多的话,那饮料类的设计怕不是要爆表。

我们又接触到一个设计原则:类应该对扩展开放,对修改关闭。

开放-关闭原则,最重要的设计原则之一。允许类容易扩展,在不修改现有代码的情况下,就可搭配新的行为。如能实现这样的目标,我们的设计就具有很好的弹性,可以应对改变,可以接收新的功能来应对改变的需求。

如上所示,继承肯定不是解决这个问题的好办法,所以我们需要将饮料作为我们的主题,调料来装饰饮料,比如现在需要准备一份摩卡和奶泡深焙咖啡,步骤如下:

  • 拿一个深焙咖啡对象
  • 以摩卡对象装饰它
  • 以奶泡对象装饰它
  • 调用 cost() 方法,并依赖委托将调料的价钱加上去

这样一份咖啡就准备完成了。现在来看看使用装饰者模式的代码。

代码实现

首先是饮料类,它是所有类的父类,包含一个属性 description,一个 cost() 抽象方法,用来计算价格。

public abstract class Beverage {
    String description = "Unknown Beverage";

    public String getDescription() {
        return description;
    }

    public abstract double cost();
}

接下来是调料类的抽象父类。

public abstract class CondimentDecorator extends Beverage {
    public abstract String getDescription();
}

两个饮料类。

public class Espresso extends Beverage {

    public Espresso() {
        description = "Espresso";
    }

    @Override
    public double cost() {
        return 1.99;
    }
}

public class HouseBlend extends Beverage {
    public HouseBlend() {
        description = "House Blend Coffee";
    }

    @Override
    public double cost() {
        return .89;
    }
}

一个调料类。

public class Mocha extends CondimentDecorator {
    Beverage beverage;

    public Mocha(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ", Mocha";
    }

    @Override
    public double cost() {
        return .20 + beverage.cost();
    }
}

一个测试用例。

public class StarbuzzCoffee {
    public static void main(String[] args) {
        Espresso espresso = new Espresso();
        System.out.println(espresso.getDescription() + " $" + espresso.cost());

        HouseBlend houseBlend = new HouseBlend();
        Mocha mocha = new Mocha(houseBlend);
        Mocha mocha1 = new Mocha(mocha);
        System.out.println(mocha1.getDescription() + " $" + mocha1.cost());
    }
}

来看看使用方法,首先创建一个饮料,我们通过继承的 getDescrition() 方法获取饮料描述,cost() 方法获取价格。

第二个创建一个咖啡,通过配料类包装两次,再打印它的信息和价格。

代码清晰明了,当我们需要添加新的配料时,就通过继承配料父类,设置好配料信息,因为包含一个饮料属性,所在创建的时候设置好饮料信息,就可以将这层包装纸包到饮料外,也就是加了这个配料的咖啡制作完成,通过 cost() 方法里面调用饮料的 cost() 方法,计算结果。

到这里整个装饰者模式就结束了,只需要记得需要被装饰的对象和装饰对象都继承一个顶层父类,然后,在装饰者对象里包含被装饰属性,通过相同的方法进行对被装饰者相同方法的装饰。很实用,在 Java 中 IO 内包含很多装饰者模式的应用,可以查看一下源码。

发布了26 篇原创文章 · 获赞 2 · 访问量 2330

猜你喜欢

转载自blog.csdn.net/qq_42909545/article/details/104811276