【Java设计模式】结构型模式-装饰模式

源代码:https://gitee.com/AgentXiao/DecroatorPattern
要点:
1、装饰模式解决的问题
2、装饰模式的各个模块实现
3、装饰模式的优缺点
4、装饰模式和桥接模式的区别

一、场景引入

场景引入

如上图所示,有一个狗(Dog)类,下面有三个子类,分别是会飞的狗(FlyDog)、会游泳的狗(SwimDog)、会跑的狗(RunDog)。假设此时三个子类都是单一功能的,也就说FlyDog只会飞,游泳和跑步都不会。

如果此时需要一个既会飞也会跑的狗,我们可以通过继承的方式实现,一次类推,可以得到:

继承具有新功能的狗

这显然不是最好的方法!当功能非常多时,需要继承父类的子类数量将会大大增加!而装饰模式就是解决这个问题的一种设计模式。

二、装饰模式

装饰模式是一种用于代替继承的技术,无需通过继承增加子类就能够扩展对象的新功能。使用对象的组合关系(区分于“组合设计模式”)代替继承关系,避免类型体系的快速膨胀。

简单地说,装饰模式用于动态增加一个对象的新功能,或成为功能增强。

装饰模式实现细节:

装饰模式实现细节.png

(1)抽象构建角色Component。这是真实对象和装饰对象都需要实现的接口,便于客户端可以使用相同的方式交互装饰对象和真实对象,指上文的Dog。
(2)真实对象ConcreteComponent。具体需要装饰的对象,定义为MyDog。
(3)装饰对象Decorator。持有一个抽象构件的引用(核心)。装饰对象接受所有客户端的请求,并把这些请求通过引用转发给真实对象。这样,就能在真实对象调用前后增加新的功能,定义为SuperDog。
(4)具体装饰对象ConcreteDecorator:指上文的Fly、Swim、Run。

三、装饰模式的实现

1、抽象角色

/**
 * @InterfaceName Dog
 * @Description 抽象构建角色
 * @Author xwd
 * @Date 2018/10/25 9:52
 */
public interface Dog {
    /**
     * @MethodName showPower
     * @Descrition 展示能力
     * @Param []
     * @return void
     */
    void showPower();
}

2、具体角色

/**
 * @ClassName MyDog
 * @Description 具体对象
 * @Author xwd
 * @Date 2018/10/25 9:53
 */
public class MyDog implements Dog{
    @Override
    public void showPower() {
        System.out.println("我还没有什么能力!");
    }
}

3、装饰角色(核心)

/**
 * @ClassName SuperDog
 * @Description 装饰对象
 * @Author xwd
 * @Date 2018/10/25 9:54
 */
public class SuperDog implements Dog{
    protected Dog dog;//持有抽象构建角色的引用

    public SuperDog(Dog dog) {
        this.dog = dog;
    }

    @Override
    public void showPower() {
        dog.showPower();
    }
}

4、具体的装饰角色

/**
 * @ClassName Fly
 * @Description 飞行能力
 * @Author xwd
 * @Date 2018/10/25 9:57
 */
public class Fly extends SuperDog{
    public Fly(Dog dog) {
        super(dog);
    }

    @Override
    public void showPower() {
        super.showPower();
        System.out.println("我具备飞行功能!");
    }
}
/**
 * @ClassName Run
 * @Description 奔跑能力
 * @Author xwd
 * @Date 2018/10/25 9:57
 */
public class Run extends SuperDog{
    public Run(Dog dog) {
        super(dog);
    }

    @Override
    public void showPower() {
        super.showPower();
        System.out.println("我具备奔跑功能!");
    }
}
/**
 * @ClassName Swim
 * @Description 游泳能力
 * @Author xwd
 * @Date 2018/10/25 9:57
 */
public class Swim extends SuperDog{
    public Swim(Dog dog) {
        super(dog);
    }

    @Override
    public void showPower() {
        super.showPower();
        System.out.println("我具备游泳功能!");
    }
}

5、测试

/**
 * @ClassName Client
 * @Description 测试装饰模式
 * @Author xwd
 * @Date 2018/10/25 9:59
 */
public class Client {
    public static void main(String[] args) {

        //原生态的狗,还没有什么能力
        Dog dog = new MyDog();
        dog.showPower();
        System.out.println("*********************");

        //添加了装饰模式的狗,但是还没有添加具体功能
        SuperDog superDog = new SuperDog(dog);
        superDog.showPower();
        System.out.println("*********************");

        //添加了飞行能力的狗
        SuperDog flyDog = new Fly(dog);
        flyDog.showPower();
        System.out.println("*********************");

        //添加了游泳能力的狗
        SuperDog swimDog = new Swim(dog);
        swimDog.showPower();
        System.out.println("*********************");

        //既添加了飞行能力又添加了奔跑能力的狗
        SuperDog runDog = new Run(dog);
        SuperDog fsDog = new Fly(runDog);
        fsDog.showPower();
        System.out.println("*********************");
    }
}

6、测试结果

测试结果

由此可见,需要为一个对象添加新功能时,只需要建立这个功能,将具体对象传入。如果传入的对象是已经具备某些功能的,就相当远在那个基础上添加新的功能。

四、开发中使用的场景

  • IO中输入流和输出流的设计。
  • Swing包中图形界面构件功能。
  • Servlet API 中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper,HttpServletRequestWrapper类,增强了request对象的功能。
  • Struts2中,request,response,session对象的处理。

在这里只解释其中一种:
在IO流中,抽象构建对象是InputStream、OutputStream、Reader、Writer。

真实对象是FileInputStream、FileOutputStream。

真实对象

装饰对象是FilterInputStream、FilterOutputStream。

装饰对象

具体的装饰对象:BufferedOutputStream、BufferedInputStream等。

继承装饰对象
构造器中传入抽象构建对象

五、总结

1、功能

装饰模式(Decorator)也叫包装器模式(Wrapper)。

装饰模式降低系统的耦合度,可以动态的增加或删除对象的职责,并使得需要装饰的具体构建类和具体装饰类可以独立变化,以便增加新的具体构建类和具体装饰类。

2、优点

  • 扩展对象功能,比继承灵活,不会导致类个数急剧增加。
  • 可以对一个对象进行多次装饰,创造出不同行为的组合,得到功能更加强大的对象。
  • 具体构建类和具体装饰类可以独立变化,用户可以根据需要自己增加新的具体构件子类和具体装饰子类。

3、缺点

  • 产生很多小对象。大量小对象占据内存,一定程度上影响性能。
  • 装饰模式易于出错,调试排查比较麻烦。

4、装饰模式和桥接模式的区别

两个模式都是为了解决过多子类对象问题。但他们的诱因不一样。

  • 桥接模式是对象自身现有机制沿着多个维度变化。
  • 装饰模式是为了增加新的功能。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,588评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,456评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,146评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,387评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,481评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,510评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,522评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,296评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,745评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,039评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,202评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,901评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,538评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,165评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,415评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,081评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,085评论 2 352