设计模式系列之「装饰模式」

133 阅读5分钟
原文链接: www.jianshu.com

小Y:Hello,大家好,欢迎来到魂斗罗.归来的世界,下面让小Y带领大家一起去采访一下叼烟大汉比尔·雷泽,让大家更加理解这个粗狂的战斗汉子。Let's go。!

小Y:你最喜欢干什么?

比尔·雷泽:最喜欢冲关打爆大机。

小Y:比尔,你想对观众说些什么?

比尔·雷泽:想挑战我,随时奉陪!一颗不够,给你来三颗!

小Y:......

比尔·雷泽作为魂斗罗这么经典的人物,原来也是一个粗狂耿直boy呀。为了保存住他的光辉形象和让大家更加了解他,小Y决定把比尔·雷泽的攻击技能装饰一番介绍给大家认识。

一、案例设想

比尔·雷泽在战场上在战场上基本上用散弹枪可以横扫小兵,但是遇到一些特殊的情况还是需要通过G5手雷、集速手雷、爆破飞弹等技能来进行辅助。那么如何设置这些技能才合适呢?简单,小Y脑海中立马闪过几种方案:

(1)采用继承的方式,设定一个比尔·雷泽类,有多少个技能就写多少个子类来继承这个比尔·雷泽类。

看完这个图,小Y自己都蒙逼了,每增加一个技能可以组合出N个子类,这只是一个比尔·雷泽,再来个艾薇、寒锋什么的,那不就要加到天荒地老?

(2)直接抽象一个角色类,里面包含了每个角色的技能。

这样子有多少个角色就增加多少个子类就可以了,不用根据技能增加类,避免造成子类爆炸,但是,每个角色的技能是可以叠加使用的,角色越多或者技能叠加种类越多,那么就要在超类增加越多的方法,而且直接修改超类不符合开闭原则(超类对扩展开放,对修改关闭)。

(3)今天的主题— 装饰模式。

二、装饰模式的概念

1.装饰模式定义

装饰模式(Decorator Pattern)是一种比较常见的模式,动态地给一个对象添加一些额外的职责(就增加功能来说,装饰模式相比生成子类更为灵活)。

2.使用的场景

  • 需要扩展一个类的功能,或者给一个类添加附加职责(即核心功能不变,对其进行扩展)。
  • 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实,使得子类数目呈爆炸性增长。

3.角色介绍

  • Component抽象构件

    Component是一个接口或者是抽象类,就是定义我们最核心的对象,也就是最原始的对象(在装饰模式中,必然有一个最基本、最核心、最原始的接口或抽象类充当Component抽象构件) 。

  • ConcreteState具体状态角色

    ConcreteComponent 具体构件,是Component的具体实现,要装饰的就是它 。

  • Decorator装饰角色

    一般是一个抽象类,实现Component接口或者抽象方法,它里面可不一定有抽象的方法呀,在它的属性里必然有一个private变量指向Component抽象构件(如果具体装饰角色只有一个,这个可以省略)。

  • ConcreteDecorator具体装饰角色

    把你最核心的、最原始的、最基本的东西装饰成想要的东西。

4.案例实现

(1)装饰模式实现角色装饰UML图

①抽象角色类

public abstract class Character {
    public abstract void attack();
} 

②实现角色比尔·雷泽(对它进行装饰)

public class BillRizer extends Character {
    @Override
    public void attack() {
        //角色最基本的技能
        System.out.print("散弹枪进行扫射!");
    }
}

③技能装饰抽象类

public abstract class SkillDecorator extends Character{
    private Character character;

    public SkillDecorator(Character character) {
        this.character = character;
    }

    @Override
    public void attack() {
        this.character.attack();
    }
}

④G5手雷技能

public class G5GrenadeDecorator extends SkillDecorator {
    public G5GrenadeDecorator(Character character) {
        super(character);
    }

    @Override
    public void attack() {
        super.attack();
        System.out.print("G5手雷辅助攻击!");
    }
}

⑤集速手雷技能

public class SetspeedDecorator extends SkillDecorator {
    public SetspeedDecorator(Character character) {
        super(character);
    }

    @Override
    public void attack() {
        super.attack();
        System.out.print("集速手雷辅助!");
    }
}

⑥爆破飞弹技能

public class ExplosiveMissileDecorator extends SkillDecorator {
    public ExplosiveMissileDecorator(Character character) {
        super(character);
    }

    @Override
    public void attack() {
        super.attack();
        System.out.print("爆破飞弹辅助!");
    }
}

⑦客户端

public class Client {
    public static void main(String[] args){
        //需要装饰的BillRizer
        BillRizer billRizer=new BillRizer();
        //使用G5手雷辅助
        G5GrenadeDecorator g5GrenadeDecorator=new G5GrenadeDecorator(billRizer);
        g5GrenadeDecorator.attack();
    
        //使用G5手雷+集速手雷+爆破飞弹辅助
        SetspeedDecorator setspeedDecorator=new SetspeedDecorator(g5GrenadeDecorator);
        ExplosiveMissileDecorator explosiveMissileDecorator=new ExplosiveMissileDecorator(setspeedDecorator);
        explosiveMissileDecorator.attack();
    }
}

输出结果:

①散弹枪进行扫射!G5手雷辅助攻击!
②散弹枪进行扫射!G5手雷辅助攻击!集速手雷辅助!爆破飞弹辅助!

把比尔·雷泽的技能介绍一番,大家会发现原来这个看似粗狂的大汉真的霸气十足啊,有资格看你不爽就来一句“想挑战我,随时奉陪!一颗不够,给你来三颗”。

三、总结分析

  • 优点:
    ①装饰类和被装饰类可以独立发展,而不会相互耦合。Component类无须知道Decorator类,而Decorator也不用知道具体的构件,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”。
    ②装饰模式是继承关系的一个替代方案。装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。
    ③通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。

  • 缺点:
    ①这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。
    ②使用装饰模式进行系统设计时将产生很多小对象,这些装饰类和小对象的产生将增加系统的复杂度,加大学习与理解的难度。

看到装饰模式很多小伙伴会发现跟代理模式很相似,下一篇小Y会对这两种模式进行对比,请期待!!!!

掘金地址:juejin.cn/post/684490…

Android技术交流吧