设计模式笔记-装饰者模式

来源:互联网 发布:看报纸的软件 编辑:程序博客网 时间:2024/04/28 20:39

装饰者模式

继承,是面向对象三大基本特性之一。使用继承能够很容易修改和扩展已有的实现,同时很大程度上能够提升代码的可复用性;事实上在面向对象的设计中,通常也是通过继承来实现对给定类的功能扩展。
但是继承是静态的,在编译时便已经决定了子类的行为,往往不便于我们控制扩展的时机和方式,缺少灵活性,同时破坏了类的封装、不易于后期维护。
这个时候,组合的出现便具有了较大的意义。组合即将一个对象嵌入到另一个对象中,由另一个对象来决定什么时机、是否引用该对象来扩展自己的行为。
不同于继承,使用组合并不会破坏类的封装,而且具有较好的松耦合性、利于系统的维护。当然,最重要的是使用组合能够更为灵活的动态控制对原有类的扩展。
装饰者模式就拥有这样一个非常巧妙的结构,它可以动态添加对象功能。如果要扩展功能,装饰者模式提供了更具有弹性的解决方案。

装饰者模式基本结构:
装饰者模式基本结构

装饰者模式的关键在于装饰者类和真实对象具有相同的接口,并且装饰者类包含一个真实对象的引用,装饰者类将接收到的请求转交给真实对象,并在转交前后添加自己的行为实现。
我们举个例子:
在咖啡店喝咖啡的时候有的人喜欢加牛奶,也有的人喜欢加冰块。
这里写图片描述

继承就相当于咖啡店一开始就生产出含有牛奶和冰块的咖啡,这固然是可以的。但是有的人喜欢喝纯咖啡,有的人不喜欢喝加了冰块的咖啡,也有的人喜欢喝更甜的咖啡;市场的需求是复杂的,在这种情况下,往往会丢失一大批的客户。
所以咖啡店并不会一开始就生产出含有牛奶和冰块的咖啡,而是在生产出咖啡后同时提供冰块、牛奶,让客户在需要的时候自行添加。
所谓设计模式,最大的功能或者说目的就在于更好的应对变化。

我们再来看下简单的代码实现:
1、首先自然是公用接口

package cn.service;public interface Compontent {    public void operation();}

2、喝咖啡方法,也就是我们的真实对象

package cn.service.impl;import cn.service.Compontent;public class Coffee implements Compontent{    @Override    public void operation() {        System.out.println("喝咖啡");    }}

3、因为我们要扩展的功能不止一个(除了加冰还可以加牛奶),所以这里我们使用抽象基类方便共用,实现与真实对象相同的接口

package cn.service.impl;import cn.service.Compontent;public abstract class CompontentImpl implements Compontent {    Compontent compontent;    public CompontentImpl( Compontent c) {        compontent = c;    }}

4、现在到我们的加冰和加牛奶的功能了,因为父类已经实现了接口,所以这里直接继承便可以了

package cn.service.impl;import cn.service.Compontent;public class Ice extends CompontentImpl {    public Ice( Compontent c) {        super(c);    }    @Override    public void operation() {        System.out.println("+冰块");        compontent.operation();    }}
package cn.service.impl;import cn.service.Compontent;public class Milk extends CompontentImpl{    public Milk(Compontent c) {        super(c);    }    @Override    public void operation() {        System.out.println("+牛奶");        compontent.operation();    }}

5、最后我们便可以灵活的添加自己所喜欢的组合了,可以是先加冰再加牛奶,也可以是先加牛奶再加冰,甚至是可以加了牛奶再加牛奶

public static void main( String[] args){        Compontent compontent = new Milk( new Ice( new Coffee()));        compontent.operation();        System.out.println("==========================");        compontent = new Ice( new Milk( new Coffee()));        compontent.operation();        System.out.println("==========================");        compontent = new Milk( new Milk( new Coffee()));        compontent.operation();    }

这里写图片描述

这个时候大家会发现,对原有对象的扩展变得很灵活了,但是同时也产生了很多小对象。更灵活的特性也就意味着系统的复杂性更高,也同时意味着比继承更容易出错,出错后的排查也更为繁琐。
所以在我们使用装饰者模式的时候需要把握一个度,过度的使用并不可取。

装饰者模式可以让使用者针对接口编程,之后通过不同的方式实现接口类而达到实现不同功能的目标。这种使用模式是否有些似曾相识的感觉?
在Java中,装饰者模式有个很典型的应用:InputStream和OutputStream类族的实现。
OutputStream

(注:部分图片源于网络)

0 0