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

设计背景:

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

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

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

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

RedShapeDecorator 是实现了 ShapeDecorator 的实体类。

DecoratorPatternDemo,我们的演示类使用 RedShapeDecorator 来装饰 Shape 对象。
Java设计模式:装饰设计模式_第1张图片
步骤 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 border
Shape: Circle

Circle of red border
Shape: Circle
Border Color: Red

Rectangle of red border
Shape: Rectangle
Border 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

你可能感兴趣的:(JAVA)