设计模式-装饰器模式

定义

在不改变目标结构的情况下,动态的给对象增加功能

概述

先来看一个快餐店的例子:快餐店有炒面、炒先来看一个快餐店的例子:快餐店有炒面、炒饭等快餐,可以额外附加鸡蛋、火腿、培根这些配菜,当然加配菜需要额外加钱,每个配菜的价钱通常不太一样,那么计算总价就会显得比较麻烦。下面是使用继承方式的类图:

使用继承的方式存在的问题:

  • 扩展性不好:如果要再加一种配料(火腿肠),我们就会发现需要给 FriedRice 和 FriedNoodles 分别定义一个子类。如果要新增一个快餐品类(炒河粉)的话,就需要定义更多的子类。

  • 产生过多的子类

基本组件

  1. 抽象构件(Component)角色:定义一个抽象接口以规范准备接受附加责任的对象。

  2. 具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰⻆⾊为其添加⼀些职责。

  3. 抽象装饰(Decorator)⻆⾊:继承抽象构件,并包含具体构件的实例,可以通过其⼦类扩展具体构件的功能。

  4. 具体装饰(ConcreteDecorator)⻆⾊:实现抽象装饰的相关⽅法,并给具体构件对象添加附 加的责任。

类图

/**
 * @author zjy
 * Date 2023/7/12 0012  22:05
 * Email 578547270@qq.com
 * Description 抽象构件
 */
public interface Component {

    void execute();
}


/**
 * @author zjy
 * Date 2023/7/12 0012  22:06
 * Email 578547270@qq.com
 * Description 具体构件
 */
public class ConcreteComponent implements Component {
    public void execute() {
        System.out.println("开始原始操作!");
    }
}

/**
 * @author zjy
 * Date 2023/7/12 0012  22:08
 * Email 578547270@qq.com
 * Description 抽象装饰
 */
public abstract class Decorator implements Component {

    private Component component;

    public Decorator(Component component) {
        this.component = component;
    }

    public void execute() {
        component.execute();
    }
}


/**
 * @author zjy
 * Date 2023/7/12 0012  22:09
 * Email 578547270@qq.com
 * Description 具体装饰
 */
public class DecoratorA extends Decorator{
    public DecoratorA(Component component) {
        super(component);
    }

    @Override
    public void execute() {
        System.out.println("具体装饰操作!");
        super.execute();
    }
}

/**
 * @author zjy
 * Date 2023/7/12 0012  22:11
 * Email 578547270@qq.com
 * Description 测试
 */
public class DecoratorTest {
    public static void main(String[] args) {
        Component component = new DecoratorA(new ConcreteComponent());
        component.execute();
    }
}

案例

使用装饰者模式对快餐店案例进行改

/**
 * @author zjy
 * Date 2023/7/12 0012  22:26
 * Email 578547270@qq.com
 * Description 抽象构件角色:快餐
 */
@Data
public abstract class FastFood {

    /**
     * 价格
     */
    private float price;

    /**
     * 描述
     */
    private String desc;

    public abstract float cost();

    public FastFood(float price, String desc) {
        this.price = price;
        this.desc = desc;
    }
}

/**
 * @author zjy
 * Date 2023/7/12 0012  22:33
 * Email 578547270@qq.com
 * Description 具体构件角色:炒饭类
 */
public class FriedRice extends FastFood{

    public FriedRice() {
        super(10, "炒饭");
    }

    public float cost() {
        return getPrice();
    }
}

/**
 * @author zjy
 * Date 2023/7/12 0012  22:36
 * Email 578547270@qq.com
 * Description 抽象装饰者角色:配料类
 */
public abstract class Garnish extends FastFood {

    // 声明快餐类的变量
    private final FastFood fastFood;

    public Garnish(float price, String desc, FastFood fastFood) {
        super(price, desc);
        this.fastFood = fastFood;
    }

    public FastFood getFastFood() {
        return fastFood;
    }
}

/**
 * @author zjy
 * Date 2023/7/12 0012  22:38
 * Email 578547270@qq.com
 * Description 具体装饰者角色:鸡蛋配料类
 */
public class Egg extends Garnish{

    public Egg(FastFood fastFood) {
        super(1, "鸡蛋", fastFood);
    }

    @Override
    public float cost() {
        return super.getPrice() + getFastFood().cost();
    }

    @Override
    public String getDesc() {
        return super.getDesc() + getFastFood().getDesc();
    }
}

/**
 * @author zjy
 * Date 2023/7/12 0012  22:41
 * Email 578547270@qq.com
 * Description 具体装饰者角色:培根配料类
 */
public class Bacon extends Garnish{

    public Bacon( FastFood fastFood) {
        super(2, "培根", fastFood);
    }

    @Override
    public float cost() {
        return super.getPrice() + super.getFastFood().cost();
    }

    @Override
    public String getDesc() {
        return super.getDesc() + super.getFastFood().getDesc();
    }
}

/**
 * @author zjy
 * Date 2023/7/12 0012  22:42
 * Email 578547270@qq.com
 * Description 测试类
 */
public class FastFoodTest {

    public static void main(String[] args) {
        FastFood food = new FriedRice();
        System.out.println(food.getDesc() + "  " + food.cost());
        food = new Egg(food);
        System.out.println(food.getDesc() + "  " + food.cost());
        food = new Bacon(food);
        System.out.println(food.getDesc() + "  " + food.cost());
        /**
         * 炒饭  10.0
         * 鸡蛋炒饭  11.0
         * 培根鸡蛋炒饭  13.0
         */
    }
}

使用场景

  • 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时

    • 不能采用继承的情况主要有两类:

      • 第一类是系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。

      • 第二类是因为类定义不能继承(如 final 类)。

  • 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。

  • 当对象的功能要求可以动态地添加,也可以再动态地撤销时。

JDK源码解析

IO流中的包装类使用到了装饰者模式:BufferedInputStream,BufferedOutputStream,BufferedReader,BufferedWriter。

以 BufferedWriter 举例来说明,先看看如何使用 BufferedWriter:

使用起来感觉确实像是装饰者模式,接下来看它们的结构:

BufferedWriter 使用装饰者模式对 Writer 子实现类进行了增强,添加了缓冲区,提高了写数据的效率。

代理和装饰者的区别

相同点:

  • 都要实现与目标类相同的业务接口

  • 在两个类中都要声明目标对象

  • 都可以在不修改目标类的前提下增强目标方法

不同点:

  • 目的不同

    • 装饰者是为了增强目标对象

    • 静态代理是为了保护和隐藏目标对象

  • 装饰者可以迭代增强,代理只能增强一次

    • 获取目标对象构建的地方不同

    • 装饰者是由外界传递进来,可以通过构造方法传递

    • 静态代理是在代理类内部创建,以此来隐藏目标对象

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值