装饰模式(包装模式)

装饰模式: 是以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。

装饰模式是以对客户透明的方式动态的给一个对象附加上更多的责任,装饰模式可以在不增加任何子类的情况下将对象的功能加以扩展

使用装饰模式,我们没有对类进行修改就实现了为该类的对象添加了新的功能,当然我们也可以通过继承来建立一个子类实现我们添加功能的目的,但是这样一来就要写好多的子类,而且这些子类仅有少许微小的差别,不利于后续的修改和维护。因此在实际应用的时候能够通过组合来实现的,就不要通过继承来实现,因为继承是静态的,一旦通过继承实现一个子类,这个子类的功能就被确定了,十分不方便,通过装饰模式可以在不增加任何子类的情况下将对象的功能加以扩展,有巨大的优势,java中体现这一点最明显的就是装饰模式: Java 的 I/O输入输出系统

(说点题外话)我们都知道java是单继承的,但是可以实现多个接口,优势在哪?重点内容

第一: 这是因为多继承存在着缺陷,比如A类继承自B类和C类,A要调用 cout()方法,但是B类和C类都存在着cout()方法,这样会发生冲突,系统就不知道使用哪个方法了,而多个接口就没有此种问题,这就是C++中的菱形继承问题,因为接口中的方法都是抽象方法,方法必须在子类中加以实现,因此调用cout方法时,调用A中的方法,接口中的成员变量都为静态成员变量 static finnal关键字修饰,当在一个接口中定义了一个变量a,如果在另一个接口中也定义变量a,编译是通不过的,报错!

第二: 在程序设计中,我们要遵循着单一原则,即一个类只负责一个功能,使用多继承的话,必然会使该子类要完成好几个功能,修改和维护成本高。

装饰模式和继承的对比
用来扩展特定对象的功能 用来扩展类的功能
不需要子类 需要子类
动态 静态
可以看到,装饰模式可以防止由于子类而导致的复杂和混乱,更加灵活,对于一个给定 的对象,同时可能有不用的装饰对象,客户端可以通过它的需要选择合适的装饰对象发送消息

装饰模式的几个角色:在 I/O输入输出流中都可以找到对应的类或者接口

Component抽象构件角色: Component是一个接口或者是抽象类,就是定义我们最核心的对象,也就是最原始的对象,比如 InputStream 和OutputStream

ConcreteComponent 具体构件角色:是抽象构件角色接口或抽象类的实现,指定了特定的功能,你要装饰的就是它,在这个类的对象实现的功能上装饰上新的功能。
比如:由 节点流类: ByteArrayInputStream、FileInputStream、PipedInputStream、StringBufferInputStream等类扮演

Decorator装饰角色: 一般是一个抽象类,它的功能和特点如下:
第一: 要实现抽象构件角色的接口,使得抽象构件角色接口类型的引用可以指向它的子类,也就是实际的添加功能的类
第二: 保持一个抽象构件角色的引用, 该类会根据传进来的实际对象类型调用其本身的方法(多态)
比如: 由FilterInputStream扮演, 它实现了InputStream所规定的接口 或者FilterOutputStream

具体装饰角色: 继承自Decorator装饰角色类,是两个具体的装饰类,新添加的功能就是在这里面。 分别是 过滤流类: BufferedInputStream、DataInputStream以及两个不常用到的类LineNumberInputStream、PushbackInputStream。

下面一个例子会带你更加深入的走进装饰模式:
下面说一下程序执行的流程:这个我也是想了很久才搞懂的,原来不懂为什么可以装饰角色的两个子类互相装饰,我理解的难点在于,他们不是平行关系的吗?为什么一个可以调用另一个,相同流程后我就明白了。
首先: 构造对象:我们就不讲解了
再看 调用decorate.doSomething() 是怎么执行的吧:
1. decorate是一个ConcreteDectate2 对象,所以首先到了ConcreteDectate2 中的super.doSomething()这一句
2. 调用AbstactDecrator 父类的doSomething() ,方法 执行到component.doSomething() 这一句,注意到这一句的component是一个抽象构件类型的引用,多态会使得调用对象本身的doSomething() 方法,此时传进来的对象是一个ConcreteDectate1 对象 ,因此执行ConcreteDectate1 的doSomething()方法
3. 在 ConcreteDectate1 的doSomething()方法中第一句是 super.doSomething() ,因此程序有执行到 AbstactDecrator 父类的doSomething(),执行 component.doSomething() 这一句,注意到对于ConcreteDectate1 来说传进来的对象是一个 ConcreteComponent类的对象, 此时调用的是 ConcreteComponent类的
doSomething()方法,输出了功能A。
4. 之后再把程序 一一执行完

可以看到,每次在执行的时候,它都会返回到抽象的装饰类,由它根据多态的机制决定调用哪个对象的方法。

// 客户端的调用,测试
public class DecorateTest {
    public static void main(String[] args) {
        ConcreteDectate2 decorate =  
                new ConcreteDectate2( new ConcreteDectate1( new ConcreteComponent()));
        decorate.doSomething();  // 输出  功能A    功能B     功能C 
    }
}

抽象的构件

// 定义一个抽象的构件
public interface AbstractComponent {
    public void doSomething();
}

具体的构件角色

// 具体的构件角色,实现了抽闲构件角色的接口,自身实现功能A
public class ConcreteComponent  implements AbstractComponent{
    @Override
    public void doSomething() {
        System.out.println("功能A");
    }
}

抽象的装饰角色 对它的理解尤其重要

// 抽象的装饰角色
// 第一: 要实现抽象构件角色的接口,使得抽象构件角色接口类型的引用可以指向它的子类
// 第二: 保持一个抽象构件角色的引用, 该类会根据传进来的实际对象类型调用其本身的方法(多态)
public class  AbstactDecrator implements AbstractComponent {
     private AbstractComponent component;
     public AbstactDecrator(AbstractComponent  component ){
         this.component = component;
     }
    @Override
    //  实现转发 
    public void doSomething() {
        // 父类接收到一个抽象构件接口类型的引用,多态机制会使它调用它本身的类型中的doSomething()方法
        component.doSomething();
    }
}

具体的装饰角色1

public class ConcreteDectate1 extends AbstactDecrator {
        public ConcreteDectate1( AbstractComponent component){
            super(component);
        }
        public void doSomething(){
            // 先实现原有的功能,可以发现,它把调用哪个对象的方法的决定权付给了父类
            super.doSomething(); 
            // 在添加自己的新的功能
            System.out.println("功能B");
        }
}

具体的装饰角色2

// 可以看到 它集成自装饰类(实现了抽象构件的接口), 因此一个抽象构件的接口的引用可以指向该对象
// 
public class ConcreteDectate2 extends AbstactDecrator {
       // 直接调用父类的构造方法
        public ConcreteDectate2( AbstractComponent component){
            super(component);
        }
        // 注意的是必须有在这个和接口中声明的方法中添加新的功能, 这个方法名不能改变
        // 这是因为super.doSomething(); 中会调用实际对象类型的doSomething方法
        public void doSomething(){
            // 实现原有的功能
            super.doSomething();
            // 再实现自己的新的功能
            System.out.println("功能C");
        }
}

装饰模式应用
装饰模式的优点
A、装饰类和被装饰类可以独立发展,而不会相互耦合。换句话说,Component类无须知道Decorator类,Decorator类是从外部来扩展Component类的功能,而Decorator也不用知道具体的构件。
B、装饰模式是继承关系的一个替代方案。我们看装饰类Decorator,不管装饰多少层,返回的对象还是Component,实现的还是is-a的关系。
C、装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。装饰模式允许系统动态决定“贴上”一个需要的“装饰”,或者除掉一个不需要的“装饰”。继承关系则不同,继承关系是静态的,它在系统运行前就决定了。
D、过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。
装饰模式的缺点
A、由于使用装饰模式,可以比使用继承关系需要较少数目的类。使用较少的类,当然使设计比较易于进行。但是,在另一方面,使用装饰模式会产生比使用继承关系更多的对象。更多的对象会使得查错变得困难,特别是这些对象看上去都很相像。
B、多层的装饰是比较复杂的。
装饰模式的使用场景
A、需要扩展一个类的功能,或给一个类增加附加功能。
B、需要动态地给一个对象增加功能,这些功能可以再动态地撤销。
C、需要为一批的兄弟类进行改装或加装功能,当然是首选装饰模式。

部分内容转自:http://blog.csdn.net/caihuangshi/article/details/51334097

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值