Java设计模式:装饰设计模式

来源:互联网 发布:大学生数据统计分析 编辑:程序博客网 时间:2024/06/07 02:23

设计背景:

中秋节快到了,到了吃月饼的时候了。假如月饼只有吃的功能,不具备送礼的功能(不允许对原功能进行增加)。但是我想用月饼送礼,我想给月饼添加一个送礼的功能又该怎么办呢?

装饰模式可以做到这一点,我们只需要创建一个月饼盒,将月饼添加到其中,让月饼盒拥有送礼的功能就可以了。
像这样的例子还有很多都是大同小异,下面我们就来学习一下装饰模式。

通过一个图形的例子初识装饰设计模式

我们将创建一个 Shape 接口和实现了 Shape 接口的实体类。然后我们创建一个实现了 Shape 接口的抽象装饰类 ShapeDecorator,并把 Shape 对象作为它的实例变量。

RedShapeDecorator 是实现了 ShapeDecorator 的实体类。

DecoratorPatternDemo,我们的演示类使用 RedShapeDecorator 来装饰 Shape 对象。
这里写图片描述
步骤 1
创建一个接口。
Shape.java

public interface Shape {   void draw();}

步骤 2
创建实现接口的实体类。
Rectangle.java

public class Rectangle implements Shape {   @Override   public void draw() {      System.out.println("Shape: Rectangle");   }}

Circle.java

public class Circle implements Shape {   @Override   public void draw() {      System.out.println("Shape: Circle");   }}

步骤 3
创建实现了 Shape 接口的抽象装饰类。
ShapeDecorator.java

public abstract class ShapeDecorator implements Shape {   protected Shape decoratedShape;   public ShapeDecorator(Shape decoratedShape){      this.decoratedShape = decoratedShape;   }   public void draw(){      decoratedShape.draw();   }    }

步骤 4
创建扩展了 ShapeDecorator 类的实体装饰类。
RedShapeDecorator.java

public class RedShapeDecorator extends ShapeDecorator {   public RedShapeDecorator(Shape decoratedShape) {      super(decoratedShape);           }   @Override   public void draw() {      decoratedShape.draw();                 setRedBorder(decoratedShape);   }   private void setRedBorder(Shape decoratedShape){      System.out.println("Border Color: Red");   }}

步骤 5
使用 RedShapeDecorator 来装饰 Shape 对象。
DecoratorPatternDemo.java

public class DecoratorPatternDemo {   public static void main(String[] args) {      Shape circle = new Circle();      Shape redCircle = new RedShapeDecorator(new Circle());      Shape redRectangle = new RedShapeDecorator(new Rectangle());      System.out.println("Circle with normal border");      circle.draw();      System.out.println("\nCircle of red border");      redCircle.draw();      System.out.println("\nRectangle of red border");      redRectangle.draw();   }}

步骤 6
验证输出。

Circle with normal borderShape: CircleCircle of red borderShape: CircleBorder Color: RedRectangle of red borderShape: RectangleBorder Color: Red

我来简单说一下这个例子:有一个图形接口Shape,有两个类实现了这个接口,他们分别是矩形Rectangle和圆形Circle。这两个子类都有draw方法显示自己是啥图形,但是你不甘心,想让Circle和Rectangle这两个类在不增加方法的前提下实现显示自身颜色的功能。不能修改类却要类能实现额外的功能,这怎么可能?所以我们采用装饰设计模式,我们创建了抽象装饰类ShapeDecorator 实现 Shape接口。再创建扩展了 ShapeDecorator 类的实体装饰类RedShapeDecorator.java再看下面这句话:

Shape redRectangle = new RedShapeDecorator(new Rectangle());

通过RedShapeDecorator把Rectangle”装饰“一下,你会惊奇的发现在不增加Rectangle方法的前提下,Rectangle的draw方法“增强了”(改变了)。
这里Rectangle就是月饼,RedShapeDecorator是月饼盒,经过月饼盒的装饰它就有了别的功能!

装饰设计模式的特点

(1)装饰者和被装饰者有相同的接口(或有相同的父类)
(2)装饰者保存了一个被装饰者的引用。
(3)装饰者接受所有客户端的请求,并且这些请求最终都会返回给被装饰者。
(4)在运行时动态地为对象添加方法,不必改变对象的结构。
(5)装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
(6) 动态增加功能,动态撤销。
(7)缺点:多层装饰比较复杂

装饰设计模式其实就在你身边

装饰者模式在Java中经常出现的地方就是JavaIO。提到JavaIO,脑海中就冒出了大量的类:InputStream、FileInputStream、BufferedInputStream……等,真是头都大了,其实,这里面大部分都是装饰类,只要弄清楚这一点就容易理解了。我们来看看JavaIO是怎样使用装饰者模式的。
从字符流来分析,我们知道,有两个基类,分别是InputStream和OutputStream,它们也就是我们上面所述的Component基类。接着,它有如下子类:FileInputStream、StringBufferInputStream等,它们就代表了上面所述的ConcreteComponent,即装饰对象。此外,InputStream还有FilterInputStream这个子类,它就是一个抽象装饰者,即Decorator,那么它的子类:BufferedInputStream、DataInputStream等就是具体的装饰者了。那么,从装饰者模式的角度来看JavaIO,是不是更加容易理解了呢?

下面,我们来自己实现自己的JavaIO的装饰者。要实现的功能是:把一段话里面的每个单词的首字母大写。我们先新建一个类:UpperFirstWordInputStream.java

public class UpperFirstWordInputStream extends FilterInputStream {    private int cBefore = 32;    protected UpperFirstWordInputStream(InputStream in) {        //由于FilterInputStream已经保存了装饰对象的引用,这里直接调用super即可        super(in);    }    public int read() throws IOException{        //根据前一个字符是否是空格来判断是否要大写        int c = super.read();        if(cBefore == 32)        {            cBefore = c;            return (c == -1 ? c: Character.toUpperCase((char) c));        }else{            cBefore = c;            return c;        }    }}

接着编写一个测试类:InputTest.java

public class InputTest {    public static void main(String[] args) throws IOException {        int c;        StringBuffer sb = new StringBuffer();        try {            //这里用了两个装饰者,分别是BufferedInputStream和我们的UpperFirstWordInputStream            InputStream in = new UpperFirstWordInputStream(new BufferedInputStream(new FileInputStream("test.txt")));            while((c = in.read()) >= 0)            {                sb.append((char) c);            }            System.out.println(sb);        } catch (FileNotFoundException e) {            e.printStackTrace();        }    }}

(注意:上面的test.txt文件需要你自行创建,放到同一个文件夹内即可,内容可随意填写。)
最后,我们看下运行结果:

Here Are Some Words.

本文有大范围的摘抄结合自己的理解,希望能帮助到有疑惑的朋友。
参考资料:
http://www.runoob.com/design-pattern/decorator-pattern.html
http://blog.csdn.net/a553181867/article/details/52108423