您的位置:首页 > 其它

设计模式之装饰者模式

2017-12-31 13:00 316 查看

装饰者概述

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


问题的抛出

开始的类设计如下:




但是有的顾客需要添加调料,比如说豆浆,摩卡等等。还需要根据这些调料收取不同的费用,所以订单系统开始尝试变化,第一个尝试如下:



看看这个设计有多么的糟糕,没中不同的饮料加调料的组合都需要生成一个新类,不敢想象会有多少个类存在,况且一旦某个调料价格变动,要改多少的类,维护很困难。

那么再来看看第二种尝试:是利用实例变量和继承,把这些调料全部放在超类中。



我们来仔细思考下这种设计的缺陷:

1)调料价格的改变会使我们更改现有的代码

2)一旦出现新的调料,就需要加上新的方法,并改变超类中的cost方法

3)比如有个新的饮料茶,某些调料并不合适(奶泡),但是子类都必须继承haswhip方法。这是很糟糕的事,就像不会叫的鸭子还必须有会飞的方法!

4)利用继承设计子类的行为,是在编译时候就静态决定了,而且所有的子类都会继承相同行为。

我们所需要的系统是能够巧妙的将多个新行为,或者设计超类的时候还没想到的职责加在对象上,而且不用修改代码,为此而努力

由这个想法我们提出新的设计原则:类应该对扩展开放,对修改关闭。这样的设计具有弹性可以应对改变,可以接受新的行为来应对改变的需求。但是我们需要注意在选择需要被扩展的代码部分要十分小心。每个地方都使用开放-关闭原则是一种浪费,也没必要,还会导致代码变得更加复杂且难以理解。

认识装饰者模式

比方说顾客想要摩卡咖啡。

1)先拿一个深焙咖啡对象

2)以摩卡对象装饰它

3)调用cost方法,依赖委托将调料的价格加上去



从上图可以看出:

1)装饰者和被装饰者具有相同的超类型

2)可以用一个或者多个装饰者去包装一个对象

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

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

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

现在我们可以来看看代码了:

package designPattern;

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

public String getDescription(){
return description;
}
//cost()必须在子类中实现
public  abstract double cost();
}

//必须让Condiment Decorator 能够取代Beverage 所以扩展自Beverage类
abstract class  CondimentDecorator extends  Beverage{
//所有调料装饰者必须重新实现getDescription方法
public abstract  String getDescription();
}
//饮料  浓缩咖啡 需要为具体饮料设置描述以及实现cost方法
class Espresso extends Beverage{

public Espresso(){
description="Espresso";
}
@Override
public double cost() {
return 1.99;
}
}
class HouseBlend extends Beverage{

public HouseBlend(){
description="HouseBlend";
}
@Override
public double cost() {
return 0.99;
}
}

//装饰者摩卡
class  Mocha extends CondimentDecorator{
/*思路:
1)用一个实例变量记录饮料,也就是被装饰者
2)想办法让被装饰者被记录到实例变量中,用构造函数把饮料当做
构造函数参数传入到实例变量中
* */
Beverage beverage;
public  Mocha(Beverage beverage){
this.beverage=beverage;
}

//为了完整的描述饮料(包括使用的调料)
@Override
public double cost() {
return 0.2+beverage.cost();
}

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

//装饰者 Soy
class  Soy extends CondimentDecorator {
Beverage beverage;

public Soy(Beverage beverage) {
this.beverage = beverage;
}
@Override
public double cost() {
return 0.6 + beverage.cost();
}
@Override
public String getDescription() {
return beverage.getDescription() + ",Soy";
}
}
//装饰者 Whip
class  Whip extends CondimentDecorator {
Beverage beverage;

public Whip(Beverage beverage) {
this.beverage = beverage;
}
@Override
public double cost() {
return 0.7 + beverage.cost();
}
@Override
public String getDescription() {
return beverage.getDescription() + ",Whip";
}
}

//下订单了
class StarbuzzCoffee{
public static void main(String[] args) {
//制造一杯浓缩咖啡 用Mocha,奶泡,豆浆装饰的
Beverage beverage=new Espresso();
beverage=new Soy(beverage);
beverage=new Mocha(beverage);
beverage=new Whip(beverage);
System.out.println(beverage.getDescription()+" $"+beverage.cost());

}
}


要点

�继承属于扩展形式之一,但不见得是达到弹性设计的最佳方式。

� 在我们的设计中,应该允许行为可以被扩展,而无须修改现有的代码。

� 组合和委托可用于在运行时动态地加上新的行为。

� 除了继承,装饰者模式也可以让我们扩展行为。

� 装饰者模式意味着一群装饰者类 , 这 些 类 用 来 包 装 具 体 组

件。

� 装饰者类反映出被装饰的组件类型(事实上,他们具有相同的类型都经过接口或继承实现)。

� 装饰者可以在被装饰者的行为前面与/或后面加上自己的行为 , 甚 至 将 被 装 饰 者 的 行 为整个取代掉,而达到特定的目的。

� 你可以用无数个装饰者包装一个组件。

� 装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型。

� 装饰者会导致设计中出现许多小对象,如果过度使用,会让程序变得很复杂

小练习

package designPatternExe.Decorator;

/*
*  1:米线有三种,干浆、酸浆和水米线。(以后也不好说)
*  2. 配料有三种,豆腐、鸡蛋、牛肉,今后还会更多。
*  3. 客户可疑随心所欲的要各种米线搭配各种配料,配料可以加同一种加多份,或者不同种加多份。
* */
public class dec_1 {
public static void main(String[] args) {
/*不断的装饰,一个类包装一个类,有点像在类
中加入新的功能,把上个类的引用给当前类就好了,
所以这也是为什么装饰类和被装饰类要类型一致,
还能附加新的功能。
* */
//1:不需要配料的酸浆米线
MiXian sjmx=new SuanJiang();
System.out.println(sjmx.getDescription()+","+sjmx.cost());
//2:来一份撒酸浆牛肉米线
MiXian sjnrmx=new SuanJiang();
sjnrmx=new Beef(sjnrmx,1);
System.out.println(sjnrmx.getDescription()+","+sjnrmx.cost());
//3:来一份酸浆牛肉鸡蛋米线
MiXian sjnrjdmx=new SuanJiang();
sjnrjdmx=new Beef(sjnrjdmx,1);
sjnrjdmx=new Egg(sjnrjdmx,1);
System.out.println(sjnrjdmx.getDescription()+","+sjnrjdmx.cost());
//4:来一份酸浆牛肉米线 ,牛肉来两份
MiXian sjnr2mx=new SuanJiang();
sjnr2mx=new Beef(sjnr2mx,2);
System.out.println(sjnr2mx.getDescription()+","+sjnr2mx.cost());
}
}
//米线(被装饰者)
abstract  class  MiXian{
String description="unkown mixian";

public String getDescription() {
return description;
}

abstract double  cost();
}
class GanJiang extends  MiXian{
public GanJiang() {
this.description="Ganjiang";
}

@Override
double cost() {
return 1.1;
}
}
class SuanJiang extends MiXian{
public SuanJiang() {
this.description="SuanJIang";
}

@Override
double cost() {
return 2.2;
}
}
class ShuiMiXian extends MiXian{
public ShuiMiXian() {
this.description="ShuiMixian";
}

@Override
double cost() {
return 3.3;
}
}

//配料(装饰者)必须与被装饰者保持一样类型
//从而能取代被装饰者
abstract class  PeiLiao  extends MiXian{
public abstract String getDescription();
}
class DouFu extends  PeiLiao{
MiXian miXian;
public int count=1;
public DouFu() {
this.description="DouFu";
}

public DouFu(MiXian miXian,int count) {
this.description="DouFu";
this.miXian = miXian;
this.count=count;
}

@Override
double cost() {
return miXian.cost()+0.2*count;
}

@Override
public String getDescription() {
return miXian.getDescription()+","+this.description;
}
}
class  Egg extends  PeiLiao{
MiXian miXian;
public  int count=1;
public Egg(MiXian miXian,int count) {
this.miXian = miXian;
this.count=count;
this.description="Egg";
}

public Egg() {
this.description="Egg";
}

@Override
double cost() {
return miXian.cost()+0.3*count;
}

@Override
pub
a4bf
lic String getDescription() {
return miXian.getDescription()+","+this.description;
}
}
class  Beef extends  PeiLiao{
public int count=1;
MiXian miXian;
public Beef() {
this.description="Beef";
}

public Beef(MiXian miXian,int count) {
this.miXian = miXian;
this.count=count;
this.description="Beef";
}

@Override
double cost() {
return miXian.cost()+0.5*count;
}

@Override
public String getDescription() {
return miXian.getDescription()+","+this.description;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: