php装饰器模式 简书,装饰器模式(从放弃到入门)

前面介绍了两篇设计模式,策略模式和观察者模式,其实自己也是在学习阶段,感觉收益很大。所以想继续分享,把java中的23中设计模式都总结一遍,在以后才能在实践中灵活运用。感兴趣的童鞋可以看看前面分享的两篇:

前面两篇都是上来就是例子,需求,我想改变一下套路,今天先介绍装饰器的理论结构,再说例子。还是要再声明:例子来自于《HeadFirst 设计模式》,推荐大家看看原书,写得很浅显易懂。

理论知识

41415f5de552

6.png

实际问题

今天的例子是一个 coffe 的例子,相信大家都去星巴克喝过咖啡,或者奶茶,我们在买coffe的时候,首先选择一个基本的coffe类型,比如 卡布奇洛,然后添加各种佐料:摩卡,豆浆,蒸奶等。基本类型是基础价,佐料又要宁外算钱。(真是聪明)

现在当前的系统设计是:

41415f5de552

1.png

本来想自己画UML图,发现自己画也一样,还损失了书上一些重要信息,所以直接盗图好了,大家不要介意。

Beverage: 饮料,抽象类,getDescription() 返回描述(类型,佐料...),cost() 抽象方法,每种饮料话费不一样,所以子类自己去实现。

然后派生了4中饮料的子类: HoseBlend, DarkRoast, Decaf, Espresso(尼玛,我都没喝过),分别实现 cost方法,返回价格。

代码很简单,这里就不贴了,然后这个时候,如果饮料有100种,呵呵,这种设计类图就是这样:

41415f5de552

2.png

类爆炸!这种设计,明显重用率太低,好吧,换种思路,我们把所有的佐料的放到公共父类中,让所有子类都拥有所有的佐料,只是在类中判断到底加没加,例如这样:

41415f5de552

3.png

代码:

Beverage .java

public abstract class Beverage {

protected String description;

protected boolean milk;

protected boolean soy;

protected boolean mocha;

protected boolean whip;

public Beverage(){

this.description = "unknown beverage";

}

public String getDescription() {

return description;

}

public abstract double cost();

}

HoseBlend.java

public class HoseBlend extends Beverage {

public HoseBlend() {

description = "HoseBlend";

milk = true;

soy = false;

mocha = false;

whip = false;

}

public double cost() {

double money = 10.0;

if (milk)

money += 2.0;

if (soy)

money += 3.0;

if (mocha)

money += 3.0;

if (whip)

money += 2.0;

return money;

}

}

使用:

Beverage hoseBlend = new HoseBlend();

System.out.println(hoseBlend.getDescription());

System.out.println(hoseBlend.cost());

其他类省略了,当然这里写得不规范,每种佐料的价格应该写成常量,这里直接用了数字,这里不是重点。这样的类设计出来,我们可以用一段话来描述:一个抽象的Beverage类,里面包含了很多佐料,子类决定是否添加这些佐料,并且计算初始价格和佐料价格。

与前一种不同的是,这种更加规范,父类决定了所有的佐料和价格,只是你填不填加,自己决定。突然想到了一种更好的方法,为何不让Beverage去计算价格呢?

Beverage.java

public abstract class Beverage {

protected String description;

protected double money;

public Beverage(){

this.description = "unknown beverage";

this.money = 10.0;

}

public String getDescription() {

return description;

}

public double cost(){

return money;

}

public void addMilk() {

this.money += 2.0;

}

public void addSoy(){

this.money += 3.0;

}

public void addMocha(){

this.money += 3.0;

}

public void addWhip(){

this.money += 2.0;

}

}

添加4中 addXXX() 方法,然后计算money,将计算价格留给父类,子类只需要添加佐料:

HoseBlend .java

public class HoseBlend extends Beverage {

public HoseBlend() {

description = "HoseBlend";

addMilk();

}

}

感觉这样写重用可以更好,并且子类工作也更少了。呵呵,书上没写,自己瞎想的。但是上面的这种设计,当遇到添加一种佐料时,都必须修改Beverate类,在设计模式原则中有一个非常重要的原则,就是开闭原则:对扩展开放,对修改关闭。上面的几种设计都存在一定的问题。我们来看看今天的主角,装饰器模式,怎么来完成。

装饰器模式

先来看一张形象的图:

41415f5de552

4.png

最里层的 DarkRoast 是饮料类型外面添加一层 Mocha, 再外层添加 Whip。最终cost() 就从最外层,一直调用到最里层,累加得到价格,呵呵,是不是有点像递归,对多态的递归,看看类结构:

41415f5de552

5.png

当然这是在最初,我们类爆炸那个例子中的结构扩展的:

Beverage依然是那个抽象类,依然有4种饮料继承与它。不同的是,多了一个 CondimentDecorator,继承于 Beverage,然后4种佐料都继承与 CondimentDecorator,并都包含一个 beverage 的引用,表示自己装饰的对象。

好了,看看代码:

Beverage .java 还是长这样

public abstract class Beverage {

protected String description;

public Beverage(){

this.description = "unknown beverage";

}

public String getDescription() {

return description;

}

public abstract double cost();

}

HouseBlend .java 第一种饮料,最低10.0元

public class HouseBlend extends Beverage {

public HouseBlend(){

description = "HoseBlend";

}

public double cost() {

return 10.0;

}

}

DarkRoast.java 第二种饮料,最低12.0元

public class DarkRoast extends Beverage {

public DarkRoast(){

description = "DarkRoast";

}

public double cost() {

return 12.0;

}

}

CondimentDecorator .java 让子类都重写getDescription() 方法

public abstract class CondimentDecorator extends Beverage {

public abstract String getDescription();

}

Milk.java : 牛奶,每加一份2.0 元

public class Milk extends CondimentDecorator {

Beverage beverage;

public Milk(Beverage beverage) {

this.beverage = beverage;

}

public String getDescription() {

return this.beverage.getDescription() + "," + "Milk";

}

public double cost() {

return this.beverage.cost() + 2.0;

}

}

Mocha.java 摩卡,每份3.0元

public class Mocha extends CondimentDecorator {

Beverage beverage;

public Mocha(Beverage beverage) {

this.beverage = beverage;

}

public String getDescription() {

return this.beverage.getDescription() + "," + "Mocha";

}

public double cost() {

return this.beverage.cost() + 3.0;

}

}

Soy.java 酱油,每份3.0元

public class Soy extends CondimentDecorator {

Beverage beverage;

public Soy(Beverage beverage) {

this.beverage = beverage;

}

public String getDescription() {

return this.beverage.getDescription() + "," + "Soy";

}

public double cost() {

return this.beverage.cost() + 3.0;

}

}

好了,看看我们怎么调用:

public class Main {

public static void main(String[] args) {

Beverage b1 = new HouseBlend();

System.out.println(b1.getDescription() + " $" + b1.cost());

Beverage b2 = new DarkRoast();

b2 = new Mocha(b2);

b2 = new Soy(b2);

b2 = new Milk(b2);

System.out.println(b2.getDescription() + " $" + b2.cost());

}

}

输出:

HoseBlend $10.0

DarkRoast,Mocha,Soy,Milk $20.0

总结

解耦合了吧,饮料和佐料分开,想怎么加怎么加,如果要添加新饮料或者佐料,只需继续添加,而无需修改以前的结构。这就是对扩展开放,对修改关闭。

我前面提到了一句话:递归多态,哈哈哈,自己瞎编的!为什么会出现这种结果,如果你对多态熟悉的话,就很好理解了,看上面代码的 b2:

为什么 Milk 可以赋值给 Beverage , 因为 Milk 的父类继承于 Beverage

b2调用 cost() 是调用 Milk 的 cost() = 2.0+ this.beverage.cost(), this.beverage指向的是 上一个b2,及 Soy, Soy调用cost(), 及调用 Mocha.cost() + 3.0 ... , 知道最后调用 beverage 的 cost() , 是不是很像递归。

再一句话概括装饰者模式吧:

装饰者模式,就是装饰者(Docorator)需要继承与被装饰者(Component),并且持有被装饰者的引用(Component),从而可以通过复写,在原来 Component 的基础上对方法做一定的修饰。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值