8. 装饰者模式(Decorator Pattern)

本文深入解析装饰者模式,探讨其定义、类型、使用场景及优缺点,并通过煎饼果子的代码示例,展示如何利用装饰者模式动态扩展对象功能,实现代码的灵活性与复用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

定义

  • 在不改变原有对象的基础之上,将功能附加到对象上
  • 提供了不继承更有弹性的替代方案(扩展原有对象功能)

类型

  • 结构型

使用场景

  • 扩展一个类的功能或给一个类添加附加职责
  • 动态的给一个对象添加功能,这功能可以再动态的撤销

优点

  • 继承的有力补充,比继承灵活,不改变原有对象的情况下给一个对象扩展功能
  • 通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同的效果
  • 符合开闭原则

缺点

  • 会出现更多的代码,更多的类,增加程序复杂性
  • 动态装饰时,多层装饰时会更复杂

相关设计模式

  • 装饰者模式和代理模式

装饰者模式关注一个对象动态的添加方法,代理模式在于控制对对象的访问,代理模式的代理类,可以对他的客户隐藏一个对线的具体信息,通常在使用代理模式时,在代理类中创建一个对象的实例,而使用装饰者模式时,通常会把原始对象做一个参数,传递给装饰者的构造器。

  • 装饰者模式和适配器模式

两者都可以叫做包装模式,装饰者和被装饰者可以实现相同的接口,或者装饰者是被装饰者的子类。在适配器中,适配器和被适配的类具有不同的接口,有可能部分接口是重合的。

代码示例

首先我们假设一个场景。我们早上吃早饭。买个煎饼果子,可以加蛋加烤肠。并计算价格。按一般方法我们是这么写的

  1. 创建一个煎饼果子对象
public class Battercake {
    protected String getDesc(){
        return "煎饼果子";
    }

    protected int cost(){
        return 8;
    }
}

  1. 创建一个加蛋的煎饼果子
public class BattercakeWithEgg extends Battercake{
    @Override
    protected String getDesc() {
        return super.getDesc() + "加一个鸡蛋";
    }

    @Override
    protected int cost() {
        return super.cost() + 2;
    }
}

  1. 创建一个加蛋加烤肠的煎饼果子
public class BattercakeWithEggSausage extends BattercakeWithEgg{
    @Override
    protected String getDesc() {
        return super.getDesc() + "加一根烤肠";
    }

    @Override
    protected int cost() {
        return super.cost() + 2;
    }
}

  1. 我们现在可以得到三种类型的煎饼果子
public class Test {
    public static void main(String[] args) {
        Battercake battercake = new Battercake();
        System.out.println(battercake.getDesc() + ",价格:" + battercake.cost());

        BattercakeWithEgg battercakeWithEgg = new BattercakeWithEgg();
        System.out.println(battercakeWithEgg.getDesc() + ",价格:" + battercakeWithEgg.cost());

        BattercakeWithEggSausage battercakeWithEggSausage = new BattercakeWithEggSausage();
        System.out.println(battercakeWithEggSausage.getDesc() + ",价格:" + battercakeWithEggSausage.cost());
    }
}

执行结果

煎饼果子,价格:8
煎饼果子加一个鸡蛋,价格:10
煎饼果子加一个鸡蛋加一根烤肠,价格:12

UML
在这里插入图片描述
通过uml我们可以看出。这就是一个单纯的继承,它现在的扩展性是非常差的。假设我们的煎饼果子可以加很多不同的东西。那它的组合是非常复杂的。那按我们现在的这种写法,可能会发生类数量爆炸的情况。这是我们就要考虑让我们的代码更加优雅,如何实现呢,当然是我们的装饰者模式啦。

步骤

组成:抽象实体类,确定的实体类,抽象的装饰者,确定的装饰者

  1. 抽象实体类,我们要对煎饼进行抽象。
public abstract class AbstractBattercake {
    protected abstract String getDesc();
    protected abstract int cost();
}

  1. 确定的实体类,具体的煎饼
public class Battercake {
    protected String getDesc(){
        return "煎饼果子";
    }

    protected int cost(){
        return 8;
    }
}
  1. 抽象的装饰者
public abstract class AbstractDecorator extends AbstractBattercake{
    private AbstractBattercake abstractBattercake;
	//我们需要引入被修饰对象的实例变量,所以就作为构造参数输入了
    public AbstractDecorator(AbstractBattercake abstractBattercake) {
        this.abstractBattercake = abstractBattercake;
    }

    @Override
    protected String getDesc() {
        return this.abstractBattercake.getDesc();
    }

    @Override
    protected int cost() {
        return this.abstractBattercake.cost();
    }
}
  1. 具体的装饰者
public class EggDecorator extends AbstractDecorator{
    public EggDecorator(AbstractBattercake abstractBattercake) {
        super(abstractBattercake);
    }

    @Override
    protected String getDesc() {
        return super.getDesc() + "加一个鸡蛋";
    }

    @Override
    protected int cost() {
        return super.cost() + 2;
    }
}
public class SausageDecorator extends AbstractDecorator{
    public SausageDecorator(AbstractBattercake abstractBattercake) {
        super(abstractBattercake);
    }

    @Override
    protected String getDesc() {
        return super.getDesc()  + "加一根烤肠";
    }

    @Override
    protected int cost() {
        return super.cost() + 2;
    }
}

  1. 为了体现与开始写法不同。我们要获得一个 加两个鸡蛋一根烤肠的煎饼果子
public class Test {

    public static void main(String[] args) {
        AbstractBattercake abstractBattercake;
        abstractBattercake = new Battercake();
        abstractBattercake = new EggDecorator(abstractBattercake);
        abstractBattercake = new EggDecorator(abstractBattercake);
        abstractBattercake = new SausageDecorator(abstractBattercake);

        System.out.println(abstractBattercake.getDesc() + ",价格:" + abstractBattercake.cost());
    }
}

执行结果

煎饼果子加一个鸡蛋加一个鸡蛋加一根烤肠,价格:14

UML
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值