设计模式--装饰者模式(在IO体系中的应用)

上一篇介绍了适配器模式,它是将一个类的接口,转化成客户期望的另一个接口,适配器让原本接口不兼容的类可以合作无间。装饰者模式:动态的将责任附加到对象上(因为利用组合而不是继承来实现,而组合是可以在运行时进行随机组合的)用来扩展功能。若要扩展功能,装饰者提供了比继承更富有弹性的替代方案(同样地,通过组合可以很好的避免类暴涨,也规避了继承中的子类必须无条件继承父类所有属性的弊端)。我们的目标是允许类统一扩展,在不修改现有代码的情况下,就可搭配新的行为且符合开闭原则。对于装饰者模式,JDK中典型的应用莫过于是IO体系。

装饰者模式

其特点是:
装饰者类拥有被装饰者类的对象,一般是当构造参数传入。
在装饰者类当中调用被装饰者类的方法,封装成新的功能方法。
装饰者设计模式主要是利用多态,将子类对象作为参数互相传递(主要为了传递实现的函数),达到互相装饰的效果,从而减少代码重复率,优化代码结构。
设计模式--装饰者模式(在IO体系中的应用)_第1张图片
与component是实现和聚合关系 (aggregation)。

//.Component : 定义一个对象接口,可以给这些对象动态地添加职责。
interface Component {
     public void operation();
}
// ConcreteComponent : 实现 Component 定义的接口。
class ConcreteComponent implements Component {
    @Override
    public void operation() {
        System.out.println("初始行为");
    }
}

Decorator : 装饰抽象类,继承了 Component, 从外类来扩展 Component 类的功能,但对于 Component 来说,是无需知道 Decorator 的存在的。

class Decorator implements Component {
    // 维护一个 Component 对象,和 Component 形成聚合关系
    protected Component component;
    // 传入要进一步修饰的对象
    public Decorator(Component component) {
        this.component = component;
    }
    @Override
    // 调用要修饰对象的原方法
    public void operation() {
        component.operation();
    }
}

ConcreteDecorator : 具体的装饰对象,起到给 Component 添加职责的功能。

class ConcreteDecoratorA extends Decorator {
    private String addedState = "新属性1";

    public ConcreteDecoratorA(Component component) {
        super(component);
    }
//对原方法进行了增强
    public void operation() {
       super.operation();
       System.out.println("添加属性: " + addedState);

    }
}

class ConcreteDecoratorB extends Decorator {
    public ConcreteDecoratorB(Component component) {
     super(component);
  }

    public void operation() {
       super.operation();
       AddedBehavior();
     }

    public void AddedBehavior() {
       System.out.println("添加行为");
   }

}

调用代码是

public class DecoratorPattern {

    public static void main(String[] args) {

        Component component = new ConcreteComponent();

        component.operation();

        System.out.println("======================================");

        Decorator decoratorA = new ConcreteDecoratorA(component);

        decoratorA.operation();

        System.out.println("======================================");

        Decorator decoratorB = new ConcreteDecoratorB(decoratorA);

        decoratorB.operation();
  }

}

其应用场景为:
a.需要动态的、透明的为一个对象添加职责,即不影响其他对象。
b.需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
c.需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。
d.当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。

与代理模式的区别

上边介绍的装饰者模式对类进行扩展增强,我们会思考,这和代理模式有些类似,都是对类功能进行增强。这和aop有些类似,aop是由代理模式实现的,且对于JDK动态代理,其代理类和被代理类也必须得实现同一个接口,其区别在哪呢?
我们可以先看这两种设计模式的定义:
代理模式(Proxy Pattern),为其它对象提供一种代理以控制对这个对象的访问。
装饰模式(Decorator Pattern),动态地给一个对象添加一些额外的职责。
换句话说:代理模式的目标是控制对被代理对象的访问,而装饰模式是给原对象增加额外功能。虽然代理模式也可以实现对被代理对象功能的增强,但其核心是隐藏对被代理类的访问。
设计模式--装饰者模式(在IO体系中的应用)_第2张图片
从uml图中也可以看出:当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问。
我们可以用另外一句话来总结这些差别:使用代理模式,代理和真实对象之间的的关系通常在编译时就已经确定了,而装饰者能够在运行时递归地被构造。
代理类所能代理的类完全由代理类确定,装饰类装饰的对象需要根据实际使用时客户端的组合来确定被代理对象由代理对象创建,客户端甚至不需要知道被代理类的存在;被装饰对象由客户端创建并传给装饰对象。我们可以动态组合装饰对象。

装饰者模式在Java IO体系中的应用

java 的IO体系很好地应用了装饰者模式,下边我们可以先看一下Java IO流体系。
设计模式--装饰者模式(在IO体系中的应用)_第3张图片
从图中可以看出,InputStream就是装饰者模式中的超类(Component),ByteArrayInputStream,FileInputStream相当于被装饰者(ConcreteComponent),这些类都提供了最基本的字节读取功能。
而另外一个和这两个类是同一级的类FilterInputStream即是装饰者(Decorator),BufferedInputStream,DataInputStream,PushbackInputStream(都继承了FilterInputStream类)…这些都是被装饰者装饰后形成的成品。
根据装饰者模式的特点,我们可以总结出这些IO流的使用方法:

File file = new File ("hello.txt"); 
FileInputStream in=new FileInputStream(file); 
BufferedInputStream inBuffered=new BufferedInputStream (in); 

这里BufferedInputStream主要是提供了缓存机制,先读入一个byte[],等count到达缓存Byte[]的大小的时候,再一次读入。 当然你也可以写成:

BufferedInputStream inBuffered = 
        new BufferedInputStream (new FileInputStream(new File ("hello.txt"))); 

从使用的角度来看装饰者模式,可以看出它的一个缺点:装饰者模式的实现对于使用者是透明的,当使用者不熟悉你的实现的时,就很难理解。 同理你可以学习一下另外一个结构outputStream 。

适配器模式比较简单就不多讲了,主要是解决了java无法多继承的问题,下面大概讲一下IO包中是怎么用这个模式的,用它来做什么? InputStreamReader和InputStream的功能的不同点在于InputStream是以二进制输入 / 输出, I/O 速度快且效率高,由于读到的是字节,也就不存在乱码问题,平台移植性好。但是它的 read ()方法读到的是一个字节,很不利于人们阅读。InputStreamReader类将字节转换为字符。 你可以在构造器中指定编码的方式,如果不指定的话将采用底层操作系统的默认编码方式。
在字符流中:

File file = new File ("hello.txt");   
FileInputStream in=new FileInputStream(file); 
InputStreamReader inReader=new InputStreamReader(in); 
BufferedReader bufReader=new BufferedReader(inReader); 

可以看出步骤2到3使用的是适配器模式,而3到4使用的是装饰者模式 。

你可能感兴趣的:(设计模式)