android 框架之基础装饰设计模式

来源:互联网 发布:淘宝第三方u站 编辑:程序博客网 时间:2024/05/12 20:47

星巴克(Starbucks)是美国一家连锁咖啡公司的名称,1971年成立,是全球最大的咖啡连锁店,其总部坐落美国华盛顿州西雅图市,早期 的星巴克由于扩展速度过快,所以他们要更新订单系统,以合乎他们的饮料供应要求.他们原先的类设计是这样的:

Beverage(饮料)是一个抽象类,cost()是一个抽象的方法,我们看到下面4个子类都实现了cost()方法,cost()方法表示饮料的价钱,而getDescription()用来描述饮料,

客户在购买咖啡时,也可以要求在其中加入各种调料,例如:蒸奶,豆浆,摩卡,或者其他调料,星巴克会根据所加入的调料收取不同的费用,所以订单系统必须考虑到这些调料部分.那么客户最后结算是根据咖啡+调料最终的总价格,比如有15种调料,那么就必须写15个子类去继承Beverage(饮料),这不是导致类太多,而且不易扩展,这就是使用继承带来的不好维护地方,于是开发人员就要想办法打破这噩梦,他们在想首先要判断客户是否加了某种调料,我要判断如果加了我就把对应的调料的价格加入到总价上,那么这个功能就放在父类上去做,而各种咖啡的价值则是由各种子类自己去实现,

public  class Beverage {public String description;public boolean milk;public boolean soy;public boolean mocha;public boolean whip;public String getDescription() {return description;}public void setDescription(String description) {this.description = description;}public boolean isMilk() {return milk;}public void setMilk(boolean milk) {this.milk = milk;}public boolean isSoy() {return soy;}public void setSoy(boolean soy) {this.soy = soy;}public boolean isMocha() {return mocha;}public void setMocha(boolean mocha) {this.mocha = mocha;}public boolean isWhip() {return whip;}public void setWhip(boolean whip) {this.whip = whip;}/** * 计算歌各种调料的价格 * @return */public  int cost(){int flavour = 0;if(milk){flavour+=5;}if(soy){flavour+=2;}        if(mocha){        flavour+=3;}        if(whip){        flavour+=3;}return flavour;}}
HouseBlend.java

public class HouseBlend extends Beverage {public int coffee;public void setCoffee(int coffee) {this.coffee = coffee;}@Overridepublic int cost() {return coffee+super.cost();}}
上层调用:

public class Clent {/** * @param args */public static void main(String[] args) {HouseBlend houseBlend = new HouseBlend();houseBlend.setDescription("不错的咖啡");houseBlend.setCoffee(15);houseBlend.setMilk(true);houseBlend.setSoy(true);System.out.println(houseBlend.getDescription()+"花了"+houseBlend.cost()+"美元");}}
结果:


这种设计比第一种好在不必要创建那么多子类,而是用boolean值去代替了要加入那种调料,类图如下:


这样的设计优于第一种使用继承的方式,但是这个设计的缺点在于变化不可控,比如节日新增加了新的调料,我们就要在Beverage类上添加一个变量,然后Beverage类中的cost方法也要改变,对后期的维护很不方便,这种设计违背了6大设计原则中的类应该对扩展开放,对修改关闭.也就是说新需求在不修改现有的代码情况下,但是可以添加类什么的来完成后期的需求变化,所以开发人员必须在此设计基础上再次对系统进行改造。

新系统设计思路:

我们要以饮料为主体,然后运行时以调料来 "装饰(decorate)"饮料,比方说,如果顾客想要摩卡和奶泡培咖啡,那么,要做的是:

第一步:拿一个深培咖啡(DarkRoast)对象

第二步:以摩卡(Mocha)对象装饰它

第三步:以奶泡(whip)对象装饰它

第四步:调用cost()方法,并依赖委托(delegate)将调料的价钱加上去,

思路想好了,但是具体到某个开发人员怎么去利用代码实现出来呢?首先如何装饰一个对象,而委托又要如何与此搭配使用呢?架构师点了一下:把装饰者对象当成包装者,苦逼的码农开始在想,首先得创建几个类,

DarkRoast(深培咖啡)


Mocha(摩卡),所以建立一个Mocha对象,并用它将DarkRoast对象包装起来,因为最后计算总价格,是咖啡的钱+饮料的钱,所以在这里要持有DarkRoast的引用,好调用cost()方法,


如果顾客一天没吃饭了,晚上想多吃点,所以顾客还想点一个奶泡(whip),所以需要建立一个Whip装饰者,并用它将Mocha对象包起来,别忘了,DarkRoast继承自Beverage,并有一个cost()方法,用来计算饮料价钱,

当最后顾客结账的时候,通过调用最外层的Whip对象的cost()方法即可,Whip对象的cost()会先委托它装饰的对象(也就是Mocha)计算出价钱,然后再加上奶泡的价钱,

你会发现这些创建的类中都有一个公共的方法cost(),是的因为他们都继承自Beverage这个类,

通过上面的你会发现装饰者和被装饰者有如下几个关系:

1:装饰者和被装饰对象有相同的父类(Beverage)
2:可以用一个或多个装饰者包装一个对象,

3:既然装饰者和被装饰对象有相同的父类,所以在任何需要原始对象(被包装Beverage)的场合,可以用装饰过的对象代替它,

4:装饰者可以在所委托被装饰者的行为之前或之后,加上自己的行为,以达到特定的目的.

5:对象可以在任何时候被装饰,所以可以在运行时动态地,不限量地用你喜欢的装饰者来装饰对象.

写到这里看下装饰模式的概念:

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

它的类图如下:



他们的角色如下:

1.Component(被装饰对象的基类)定义一个对象接口,可以给这些对象动态地添加职责。

2.ConcreteComponent(具体被装饰对象)定义一个对象,可以给这个对象添加一些职责。

3.Decorator(装饰者抽象类)维持一个指向Component实例的引用,并定义一个与Component接口一致的接口。

4.ConcreteDecorator(具体装饰者)具体的装饰对象,给内部持有的具体被装饰对象,增加具体的职责

现在用这个模式来解决星巴克订单系统,设计类图如下:


为了验证系统是否具有扩展和维护,我们随便来个需求,客户点了一个"双倍摩卡豆浆奶泡拿铁咖啡":

代码如下:

Beverage.java

package com.decorate;public abstract class Beverage {String description = "";public String getDescription() {return description;}public abstract double cost();}
CondimentDecorator.java
package com.decorate;/** * 调料被装饰抽象类 */public abstract class CondimentDecorator extends Beverage {public abstract String getDescription();}
DrakRoast.java
package com.decorate;import com.decorate.Beverage;public class DrakRoast extends Beverage {public DrakRoast(){description = "DrakRoast";}@Overridepublic double cost() {return 1.05;}}
Espresso.java
package com.decorate;public class Espresso extends Beverage{@Overridepublic double cost() {return 1.99;}public Espresso(){description = "Espresso";}}
HouseBlend.java
package com.decorate;public class HouseBlend extends Beverage{public HouseBlend(){description = "HouseBlend";}@Overridepublic double cost() {return 0.89;}}
Mocha.java
package com.decorate;public class Mocha extends CondimentDecorator{Beverage beverage;public Mocha(Beverage beverage){this.beverage = beverage;}@Overridepublic String getDescription() {return beverage.getDescription()+",Mocha";}@Overridepublic double cost() {return beverage.cost()+.20;}}
Whip.java
package com.decorate;public class Whip extends CondimentDecorator {Beverage beverage;public Whip(Beverage beverage){this.beverage = beverage;}@Overridepublic String getDescription() {return beverage.getDescription()+",Whip";}@Overridepublic double cost() {return beverage.cost()+.20;}}
测试类:
package com.decorate;/** * 测试类 * @author admin */public class Test {public static void main(String[] args) { Beverage beverage = new Espresso(); System.out.println(beverage.getDescription()+":$"+beverage.cost()); Beverage beverage2 = new DrakRoast(); beverage2 = new Mocha(beverage2); beverage2 = new Mocha(beverage2); beverage2 = new Whip(beverage2); System.out.println(beverage2.getDescription()+":$"+beverage2.cost());}}
运行结果:

Espresso:$1.99
DrakRoast,Mocha,Mocha,Whip:$1.65

装饰模式就讲到这里了!
设计模式就是在特定的场景下解决什么问题而已,就是个套路!








0 1