当前位置: 首页 > >

Java设计模式之装饰者模式

发布时间:

装饰者模式,通过这种设计模式为一个对象动态的加上一系列的动作,而不需要因为这些动作的不同而产生大量的继承类。这个模式在JDK中几乎无处不在,例如:
java.io.BufferedInputStream
java.io.FileReader

这些类够熟悉吧,理解了装饰者模式就能更好地理解Java的IO体系,今天我们结合《Head First 设计模式》的例子来学*一下装饰者模式是怎么一回事。


装饰者模式的前世

先看看为什么需要这一模式。
原来我们要拓展新功能都是通过继承父类去产生新的子类。就拿书上的咖啡店举例子,饮料是基类,饮料各式各样,名称做法不一,就会拓展出一堆子类,随着客户需求增多,饮料的品种也会增多,如果我们一个新品种一个子类的话,回过头去看系统,我们会发现简直爆炸,有很多类需要我们去维护。一旦原料价格有变化或者饮料配方需要改进,我们就不得不去修改原有的类。这种做法违背了我们面向对象设计原则之一:类应该对扩展开放,对修改关闭。原有的代码可能经过多次测试确定功能无误,但你一旦修改了原来的代码,就不能保证原来的确定性了,所以我们应该尽可能拒绝这种做法。基于以上的分析,对于饮料,我们可以这样设计,最基本的饮料,然后配上(装饰)想要的配料,再委托计算出新饮料的价格(配料价格+原来的价格)。我们看看书上对装饰者模式的定义:
装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
类图如图所示:

我们可以看到ConcreteComponent和Decorator都用到继承,这样做的是要保证装饰者和被装饰者都要是同一类型,我们利用继承来保证类型匹配,而不是利用继承来获得行为。那行为从哪来?装饰者和组件组合在一起,就产生新的行为。利用组合还有一个好处就是运行时动态扩展。下来我们直接看用代码是怎么实现的。


装饰者模式的今生

借用书上的例子,自己敲一遍。
(1)饮料基类


package Decorator;

/**
* Created by gray on 2017/9/23.
*/
public abstract class Beverage {
String desc = "UnKnown Beverage";

public String getDesc() {
return desc;
}

public abstract double cost();
}

(2)调料基类


package Decorator;

/**
* Created by gray on 2017/9/23.
*/
public abstract class CondimentDecorator extends Beverage{
public abstract String getDesc();
}

(3)普通咖啡


package Decorator;

/**
* Created by gray on 2017/9/23.
*/
public class Espresso extends Beverage {

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

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

(4)调料(牛奶和摩卡)


package Decorator;

/**
* Created by gray on 2017/9/24.
*/
public class Milk extends CondimentDecorator {
Beverage beverage;
public Milk(Beverage beverage){
this.beverage = beverage;
}

@Override
public String getDesc() {
return beverage.getDesc() + ", Milk";
}

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

package Decorator;

/**
* Created by gray on 2017/9/23.
*/
public class Mocha extends CondimentDecorator {
Beverage beverage;

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

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

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

(5) 最后是测试代码和测试结果


package Decorator;

/**
* Created by gray on 2017/9/24.
*/
public class Test {
public static void main(String args[]){
Beverage beverage = new Espresso();
System.out.println("no Condiment : "+ beverage.getDesc() + " | total : $" + beverage.cost());

Beverage beverage1 = new Mocha(new Espresso());
System.out.println("have Mocha : " + beverage1.getDesc() + " | total : $" + beverage1.cost());

Beverage beverage2 = new Milk(new Mocha(new Espresso()));
System.out.println("have Milk,Mocha : " + beverage2.getDesc() + " | total : $" + beverage2.cost());
}
}


总结:装饰者模式要说的东西不是很多,弄清楚装饰者模式再去看jdk中的I/O的相关类就显得简单了,里面绝大多数类都是装饰者。装饰者模式虽然比继承多了些灵活性,但同时也不可避免地出现许多小类,增加了API的复杂性,所有得看场景,当我们需要为某个现有的对象,动态的增加一个新的功能或职责时,可以考虑使用装饰者模式;还有就是如上面分析所说的,子类会爆炸性增长地时候就要考虑使用装饰者模式。



友情链接: