一、什么是装饰者模式

装饰者模式是一种结构型设计模式,它允许你动态地向对象添加新的行为而不影响其原有的行为。它在运行时给对象动态地添加一些额外的职责,通常是在原有的行为基础上,通过装饰器进行一些修饰,实现了更加灵活的代码复用和扩充。

二、角色组成

  1. 抽象构件(Component):要求装饰者对象都实现一个抽象接口或抽象类。(接口或抽象类)
  2. 具体构件(ConcreteComponent):定义了一个具体的对象,也就是需要被装饰的对象。
  3. 抽象装饰器(Decorator):持有一个真实的构建对象的引用,并且可以动态给该对象添加新的功能。(接口或抽象类)
  4. 具体装饰器(ConcreteDecorator):实现具体的装饰器,向真实的构建对象添加新功能。

三、优缺点

优点:

  1. 非常灵活且可扩展,能动态地为对象添加新的职责和行为。
  2. 遵循开闭原则,能够实现代码的可维护性和可扩展性。
  3. 通过使用装饰器对象,可以避免不必要的继承以及子类的数量爆炸性增长问题。

缺点:

  1. 会导致系统变得复杂,增加了许多类和对象的相互关系,需要开发人员具备更高的抽象能力和设计能力。
  2. 增加了代码复杂度,使得项目开发和维护更加困难。

四、应用场景

4.1 生活场景

  1. 穿衣服:我们每天都要穿衣服,根据不同的场合和气温选择不同的服装,比如外套、围巾、帽子等,这些都是具体装饰器(Concrete Decorator),而我们就是被装饰的抽象组件(Component)
  2. 手机配件:如手机壳、钢化膜等。都可以看作是装饰者模式的应用,手机就是抽象组件(Component),手机壳、钢化膜等都是具体装饰器(Concrete Decorator)
  3. 电影特效:如烟雾、爆炸、光影等,影片为抽象组件(Component),各种特效就是具体装饰器(Concrete Decorator)

4.2 java场景

  1. IO流的处理:这是一个典型的装饰者模式的应用。InputSteam和OutputStream是最基本的抽象组件(Component),而各种FilterInputSteam和FilterOutputStream就是具体的装饰器,它们可以实现各种不同的IO流处理功能,如缓冲、压缩、加密等等。
  2. 数据库连接池:连接池为抽象组件(Component),各种不同的连接池实现(如C3P0、DBCP等)则是具体的装饰器(Concrete Decorator),它们可以实现不同的连接池缓存策略、连接池大小、超时时间等属性。
  3. Spring的AOP:在AOP中,切面就是具体的装饰器(Concrete Decorator),而业务逻辑则是抽象组件(Component),通过动态代理技术,将具体的业务逻辑和切面对象组合起来,我们就可以实现在不修改源码的情况下,动态地为业务逻辑添加新的功能。

五、代码实现

下面以英雄联盟中李青(盲僧学技能为例。其中Hero代表英雄(抽象构件)、BlindMonk(盲僧-具体构件)、SkillDecorator(技能-抽象装饰器)、QWERDecorator为英雄的四个技能(具体装饰器

5.0 UML类图

5.1 Hero(英雄-抽象组件)

/**
 * @author Created by njy on 2023/6/6
 * 英雄
 */
public interface Hero {

    /**
     * 学技能
     */
    void learnSkill();
}

5.2 BlindMonk(盲僧-具体构件)

import lombok.Data;
/**
 * @author Created by njy on 2023/6/6
 * 盲僧
 */
@Data
public class BlindMonk implements Hero{

    private String name;

    public BlindMonk(String name){
        this.name=name;
    }

    @Override
    public void learnSkill() {
        System.out.println(getName());
    }
}

5.3 SkillDecorator(技能装饰-抽象装饰器)

/**
 * @author Created by njy on 2023/6/6
 * 学技能(装饰hero)
 */
public interface SkillDecorator extends Hero{

    void learnSkill();
}

5.4 QDecorator(Q技能-具体装饰器)

/**
 * @author Created by njy on 2023/6/6
 * Q技能
 */
public class QDecorator implements SkillDecorator{
    private Hero hero;

    private String name;
    public QDecorator(Hero hero,String name){
        this.name=name;
        this.hero=hero;
    }

    private void learnQ(){
        System.out.println("习得技能"+name);
    }

    @Override
    public void learnSkill() {
        hero.learnSkill();
        learnQ();
    }
}

5.5 WDecorator(W技能-具体装饰器)

/**
 * @author Created by njy on 2023/6/6
 * W技能
 */
public class WDecorator implements SkillDecorator{
    private Hero hero;

    private String name;
    public WDecorator(Hero hero,String name){
        this.name=name;
        this.hero=hero;
    }

    private void learnW(){
        System.out.println("习得技能"+name);
    }

    @Override
    public void learnSkill() {
        hero.learnSkill();
        learnW();
    }
}

5.6 EDecorator(E技能-具体装饰器)

/**
 * @author Created by njy on 2023/6/6
 * E技能
 */
public class EDecorator implements SkillDecorator{
    private Hero hero;

    private String name;
    public EDecorator(Hero hero,String name){
        this.name=name;
        this.hero=hero;
    }

    private void learnE(){
        System.out.println("习得技能"+name);
    }

    @Override
    public void learnSkill() {
        hero.learnSkill();
        learnE();
    }
}

5.7 RDecorator(R技能-具体装饰器)

/**
 * @author Created by njy on 2023/6/6
 * R技能
 */
public class RDecorator implements SkillDecorator{

    private Hero hero;

    private String name;
    public RDecorator(Hero hero,String name){
        this.name=name;
        this.hero=hero;
    }

    private void learnR(){
        System.out.println("习得技能"+name);
    }

    @Override
    public void learnSkill() {
        hero.learnSkill();
        learnR();
    }
}

5.8 TestDecorator

/**
 * @author Created by njy on 2023/6/5
 */
@SpringBootTest
public class TestDecoration {

    @Test
    void testDecoration(){
        QDecorator q=new QDecorator(new BlindMonk("李青"),"Q:天音波/回音击");
        WDecorator w=new WDecorator(q,"W:金钟罩/铁布衫");
        EDecorator e=new EDecorator(w,"E:天雷破/摧筋断骨");
        RDecorator r=new RDecorator(e,"R:猛龙摆尾");
        r.learnSkill();
    }
}

六、总结

  • 给对象添加一些职责,但是又不想改变其原有的接口和实现。
  • 需要对不同组合的对象进行扩展,从而避免出现由于继承关系带来的类很多问题。
  • 在不使用继承的情况下动态地为一个对象添加一些额外的功能。
  • 需要在程序运行时动态地为对象添加不同的功能,或者为对象添加同时使用多个的功能。

一句话,当遇到需要给某个类添加新功能,但又不能改源代码或不希望影响其他对象的情况下,可以考虑装饰者模式。

END:更多设计模式的介绍,推荐移步至👉 23种设计模式学习导航(Java完整版)👈 

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐