搞懂装饰器模式

装饰模式是结构型设计模式之一,其在不必改变类文件和使用继承的情况下,动态地扩展一个对象的 功能,是继承的替代方案之一。它通过创建一个包装对象,也就是装饰来包裹真实的对象。

定义:动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。 装饰模式的结构图如图所示

在装饰模式中有如下角色。

  • Component:抽象组件,可以是接口或是抽象类,被装饰的最原始的对象。
  • ConcreteComponent:组件具体实现类。Component的具体实现类,被装饰的具体对象。
  • Decorator:抽象装饰者,从外类来拓展Component类的功能,但对于Component来说无须知道 Decorator的存在。在它的属性中必然有一个private变量指向Component抽象组件。
  • ConcreteDecorator:装饰者的具体实现类。

我们用一个简单易懂的案例来实现一个装饰器模式:

具体代码在这里

在我们天上人间,来了一个头牌小姐姐,我们叫她冰冰姐吧,冰冰姐对于java来说,是一个基本的对象,她可以提供最基础的聊天服务,但是既然是头牌了,也不可能只会聊天对吧,因此,冰冰姐还有一系列的真功夫,例如我们接下来要实现的唇唇欲动、逼上梁山服务。下面我们来对应下装饰器模式中的组件、类和案例中的冰冰姐的具体技能的关系。

component抽象组件 --- 聊天基础服务

ConcreteComponent组件具体实现类  --- 冰冰姐聊天服务技能

Decorator抽象装饰者  --- 冰冰姐总的技能列表

ConcreteDecorator --- 冰冰姐的具体技能项目

1.聊天基础服务

作为基础服务技能,这个是必要的服务,我们简单来说就是聊天服务:

/**
 * 抽象组件的基础服务:聊天儿
 */
public abstract class GirlService {
    public abstract void liaoTianService();
}

2.冰冰姐聊天服务技能

当然,这里我们的对象就是冰冰姐,由她来实现这个具体的服务,

/**
 * 基础服务的实现类:冰冰小姐姐
 */
public class BingBingMiss extends GirlService {
    @Override
    public void liaoTianService() {
        System.out.println("冰冰小姐姐的基础服务:聊天");
    }
}

3.冰冰姐总的技能列表

既然是头牌,那看家本领肯定不少,这里这个OtherService就是一个总的技能栈,像一个装饰器一样,里面各类服务应有尽有:

/**
 * 抽象装饰者:除了小姐姐聊天基本服务外的,其他服务
 * 例如:唇唇欲动、霸王别姬、逼上梁山等等
 */
public class OtherService extends GirlService {

    private GirlService girlService;

    public OtherService(GirlService service) {
        this.girlService = service;
    }

    @Override
    public void liaoTianService() {
        girlService.liaoTianService();
    }
}

4.冰冰姐的具体技能项目,这里我们列举两项(唇唇欲动、逼上梁上)

唇唇欲动的服务技能:

public class LipsAndLipsService extends OtherService {

    public LipsAndLipsService(GirlService service) {
        super(service);
    }

    public void lipsAndLipsService() {
        super.liaoTianService();
        System.out.println("装饰器的具体实现:唇唇欲动服务");
    }

}

逼上梁上的服务技能:

public class BiShangLiangShanService extends OtherService {


    public BiShangLiangShanService(GirlService service) {
        super(service);
    }

    public void biShangLiangShanService() {
        super.liaoTianService();
        System.out.println("装饰器的具体实现:逼上梁山服务");
    }
}

具体的测试:

BingBingMiss bingMiss = new BingBingMiss();
LipsAndLipsService lipsService = new LipsAndLipsService(bingMiss);
lipsService.lipsAndLipsService();
BiShangLiangShanService biShanService = new BiShangLiangShanService(bingMiss);
biShanService.biShangLiangShanService();

下面我们来看一下具体的执行结果:

System.out: 冰冰小姐姐的基础服务:聊天
System.out: 装饰器的具体实现:唇唇欲动服务
System.out: 冰冰小姐姐的基础服务:聊天
System.out: 装饰器的具体实现:逼上梁山服务

以上就是装饰器模式的具体实现。

装饰模式的使用场景和优缺点 


• 使用场景:


在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。 需要动态地给一个对象增加功能,这些功能可以动态地撤销。 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。 


• 优点:

通过组合而非继承的方式,动态地扩展一个对象的功能,在运行时选择不同的装饰器,从而实现不同 的行为。有效避免了使用继承的方式扩展对象功能而带来的灵活性差、子类无限制扩张的问题。 具体组件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体组件类和具体装饰类,在使 用时再对其进行组合,原有代码无须改变,符合“开放封闭原则”。 


• 缺点:

因为所有对象均继承于Component,所以如果Component内部结构发生改变,则不可避免地影响所有子 类(装饰者和被装饰者)。如果基类改变,则势必影响对象的内部。 比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难。对于多次 装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。所以,只在必要的时候使用装饰模式。 装饰层数不能过多,否则会影响效率

项目地址:https://github.com/buder-cp/DesignPattern/tree/master/DesignPatterns

发布了189 篇原创文章 · 获赞 81 · 访问量 21万+

猜你喜欢

转载自blog.csdn.net/cpcpcp123/article/details/103747483