装饰者模式

            

给出问题:星巴克咖啡连锁店订单系统。这是个需要升级的系统,客户要求可以在咖啡中加入各种调料,例如:蒸奶、豆浆、摩卡等,星巴克会根据加入的调料收取不同的费用,所以升级的订单系统要考虑到这部分。下面给出他们之前的系统。

Beverage

description

getDescription();

cost();

//其他方法。。。

说明:Beverage是一个抽象类,店内所出售的饮料都必须继承此类。

description是一个实例变量,每个子类有不同表述。

getDesciption()很简单,就是为了返回对应子类的表述。

cost()方法是一个抽象方法,每一个子类必须实现此方法,来得出某一种饮料的价格。

分析:这个订单系统会带来什么问题?由于每个子类都要继承Beverage,每种调料也要这样。如果有很多子类,那么这个系统就会有成千上万的类!

解决方法:没有必要使用这么多类,利用实例变量和继承就可以追踪这些调料。下面是一个解决方法:

Beverage

description

milk

soy //黄豆

mocha

whip

getDescription();

cost();


hasMilk();

setMilk();

hasSoy();

setSoy();

hasMocha();

setMocha();



现在,Beverage类中的cost()不再是一个抽象方法,我们在这里给出了实现。让它加入了各种饮料的调料价钱,子类仍将覆盖cost(),但是会调用超类的cost(),计算出基本饮料加上调料的价钱。

这个方法似乎解决了“类爆炸”的问题,但是,调料价钱的改变会促使我们修改现有的代码,新增加了调料也会促使我们修改我们的代码,如果用户想要在一份咖啡中添加双份的牛奶,我们的设计还存在着问题。

这时候我们先引入第五个设计原则:类应该对扩展开放,对修改关闭。

我们的目标是允许类容易扩展,在不修改现有代码的情况下,就可以搭配新的行为,这样的设计的好处能使我们的软件具有弹性,可以应对改变,可以接受新的功能来应对改变的需求。但是,我们没有必要为了遵循这个原则而使我们的工作量无谓的增加。在今后的工作中,还是需要我们积极思考,努力探索出比较均衡的方法。

OK,是时候给出装饰者模式的定义了:装饰者模式动态的将责任附加到对象上,若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

回到星巴克咖啡订单系统,加入用户想要摩卡和豆浆深焙咖啡。我们做法就是以深焙咖啡为主题,然后在运行时以摩卡和豆浆来“装饰”深焙咖啡。如下:

1)以深焙咖啡(DarkRoast)开始.

DarkRoast

cost();

DarkRoast继承自Beverage,且有一个用来计算价格的方法cost().

2)顾客想要加上摩卡(Mocha),所以建立一个摩卡对象,并用这个对象将DarkRoast对象包进来。



Mocha

cost();

DarkRost


3)顾客想要豆浆(Soy),所以建立一个豆浆对象,并用豆浆对象将Mocha对象包进来。

Soy

cost();

Mocha


4)现在开始给顾客结帐的时候了。通过调用最外层装饰者(Soy)就可以办到。Soy的会先委托它装饰的对象Mocha计算出价钱,然后用Soy自身的加个加上Mocha的价格就得了顾客点的这杯咖啡的价钱了;同样的,Mocha在计算价钱的时候,也会先委托它装饰的对象DarkRoast计算出价钱,然后用自身的价钱加上DarkRoast的价格。

下面就是我们要把设计变成真正代码的时候了。这敲代码之前,还有一些东西需要说明。装饰者和被装饰者必须是同一类型,也就是有同一的超类,这是相当关键的地方,在下面的代码里,我们利用继承来达到“类型匹配”,而不是利用继承获得“行为”。

先从Beverage类下手,我们不需要改变他们之前的设计,保留:

public abstract class Beverage{

String description=”Unkown Beverage”;

          public String getDescription(){

                     return description;

           }

           public abstract double cost();

}

Beverage类很简单,让我们来实现Condiment(调料)抽象类,也就是装饰者类吧:

public abstract class Condiment{

          public abstract String getDescription();

}

所有调料装饰者都必须重写getDescription()方法。稍后我们会解释为什么。

下面我们开始写深焙咖啡:

public class DarkRoast extends Beverage{

          public DarkRoast(){

                    description=”DarkRoast”;

          }

          public double cost(){

                    return 0.99;

           }

}

实现了具体组件(DarkRoast),我们来实现具体装饰者Mocha

public class Mocha extends Condiment{

           Beverage beverage;

           public Mocha(Beverage beverage){

                      this.beverage=beverage;

           }


          public String getDescription(){

                    return beverage.getDescription()+”,Mocha”;

          }

         public double cost(){

                    return beverage.cost()+0.20;

         }

}

接下来实现豆浆这个装饰者:

public class Soy extends Condiment{

          Beverage beverage;

          public Soy(Beverage beverage){

                    this.beverage=beverage;

          }


         public String getDescription(){

                   return beverage.getDescription()+”,Soy”;

         }

         public double cost(){

                   return beverage.cost()+0.15;

         }

}

最后,我们来测试下用户点的摩卡和豆浆深焙咖啡。

public class StarbuzzCoffee{

          public static void main(String[] args){

                     Beverage beverage=new DarkRoast();

                     beverage=newMocha(beverage);

                     beverage=newSoy(beverage);

                     System.out.println(beverage.getDescription()+” $”+beverage.cost());

         }

}

输出结果应该很明了了。








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值