设计模式的学习(三)—— 装饰者模式

来源:互联网 发布:无法安装java 编辑:程序博客网 时间:2024/06/01 09:41

这一篇博客的主要内容是装饰者模式,在我看来是一种较为容易理解的设计模式:

  • 装饰者模式的主要应用场景
  • HeadFirst Design Pattern 书中示例
  • JDK中装饰者模式的应用实例

装饰者模式的主要应用场景

装饰者模式主要适用于需要将责任动态地附加到对象上的应用场景,也就是“装饰”的效果。同样是实现功能扩展的目的,装饰者模式提供了比继承更有弹性的方案。

HeadFirst Design Pattern书中示例

在书中,作者给出的示例是星巴克的不同种类的咖啡,如加牛奶的深焙咖啡、加摩卡的意式浓缩等。需要计算出每种咖啡的价格,那么需要怎么样进行开发呢。

不好的设计

最容易想到的设计是设计出各种各样的子类,让所有的类似于加牛奶的深焙咖啡、加摩卡的意式浓缩都继承自咖啡的基类,然后分别对每一个子类计算其价格。然而,这种设计的问题是随着“调料组合”的增加,这可能会生产出无穷多的子类,从而产生“类爆炸”的现象。
而且考虑到”change”这一软件设计中永恒不变的真理,我们采用上述的继承计算价格的方法可能会因为出现以下的情况,而必须要修改代码:
- 调料(摩卡、奶泡、豆浆等)价格的改变
- 出现了新的调料
- 顾客需要双倍摩卡的咖啡
- …
这些情况的出现都是需要修改原代码的,这就违反了软件设计中的开闭原则了。

采用装饰者模式的设计

//声明Baverage接口public abstract class Baverage{    public abstract double cost();}//此处采用继承,只是为了声明CondimentDecorator是Baverage,方便使用多态的性质public abstract class CondimentDecorater extends Baverage{}//声明两种咖啡类型public class Espresso extends Baverage{    public double cost(){        return 1.99;    }}public class HouseBlend extends Baverage{    public double cost(){        return 0.89;    }}//声明两个装饰类public class Mocha extends CondimentDecorater{    Baverage baverage;    public Mocha(Baverage baverage){        this.baverage = baverage;    }    public double cost(){        return 0.20 + baverage.cost();    }}public class Whip extends CondimentDecorater{    Baverage baverage;    public Whip(Baverage baverage){        this.baverage = baverage;    }    public double cost(){        return 0.15 + baverage.cost();    }}//测试代码public class StarbuzzCoffee{    public static void main(String args[]){        Baverage baverage = new Espresso();        baverage = new Mocha(baverage);        baverage = new Mocha(baverage);        baverage = new Whip(baverage);        System.out.println("the cost of the final coffe is: " + baverage.cost());;    }}

测试结果为:
这里写图片描述

对比继承而言,以上代码的可扩展性是很非常强的。

JDK中装饰者模式的应用实例

在JDK中,装饰者模式中应用最典型的地方是在I/O相关的api中。以输入流为例,InputStream是一个抽象类,类似于上例中的InputStream,而FileInputStream、AudioInputStream等为待装饰的基类, 装饰者类也是InputStream类的子类,但为了与基类作区别,装饰者类在实现过程中有一个抽象类FilterInputStream(此处的抽象指FilterInputStrem的read方法仅返回in.read(),不实现具体修饰。)。
了解到这些,我们可以写以下代码,以实现将输入字符都变成小写字符的功能。

//LowerCaseInutStream,以实现将所有的大写的输入字符变成小写字符的功能。import java.io.FilterInputStream;import java.io.InputStream;import java.io.IOException;public class LowerCaseInputStream extends FilterInputStream{    public LowerCaseInputStream(InputStream in){        super(in);    }    public int read() throws IOException{        int c = super.read();        return (c == -1? c:Character.toLowerCase((char)c));     }}//测试import java.io.InputStream;import java.io.FileInputStream;import java.io.BufferedInputStream;import java.io.IOException;public class InputTest{    public static void main(String args[]){        int c;        try{            InputStream in = new LowerCaseInputStream(                new BufferedInputStream(                    new FileInputStream("test.txt")));            while((c = in.read()) >= 0){                System.out.print((char)c);            }            in.close();        }catch(IOException e){            e.printStackTrace();        }    }}

test.txt中的内容为:

This is Decorative Pattern.

输出结果为:
这里写图片描述
由此可见装饰者模式的应用原理和实现了。