头图

写在前面

  • 记录学习设计模式的笔记
  • 提高对设计模式的灵活运用

学习地址

https://www.bilibili.com/vide...

https://www.bilibili.com/vide...

参考文章

http://c.biancheng.net/view/1...

项目源码
https://gitee.com/zhuang-kang/DesignPattern

12,装饰器模式

12.1 装饰器模式的定义和特点

装饰器(Decorator)模式的定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。

装饰器模式的主要优点有:

  • 装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用
  • 通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果
  • 装饰器模式完全遵守开闭原则

其主要缺点是:

  • 装饰器模式会增加许多子类,过度使用会增加程序得复杂性。

12.2 装饰器模式的结构与实现

通常情况下,扩展一个类的功能会使用继承方式来实现。但继承具有静态特征,耦合度高,并且随着扩展功能的增多,子类会很膨胀。如果使用组合关系来创建一个包装对象(即装饰对象)来包裹真实对象,并在保持真实对象的类结构不变的前提下,为其提供额外的功能,这就是装饰器模式的目标

12.2.1 装饰器模式的结构

  1. 抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。
  2. 具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责。
  3. 抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
  4. 具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

12.2.2 代码实现

关系类图

image

FastFood 抽象构件角色

package com.zhuang.decorator;

/**
 * @Classname FastFood
 * @Description  快餐类 接口
 * @Date 2021/3/23 21:54
 * @Created by dell
 */

public abstract class FastFood {
    private float price;
    private String desc; //描述

    public FastFood() {
    }

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

    public float getPrice() {
        return price;
    }

    public void setPrice(float price) {
        this.price = price;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    //抽象方法  获取价格
    public abstract float cost();
}

FiredRice 具体构建角色

package com.zhuang.decorator;

/**
 * @Classname FiredRice
 * @Description  炒饭类  继承快餐类
 * @Date 2021/3/23 21:54
 * @Created by dell
 */

public class FiredRice extends FastFood {

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

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

}

FiredNoodles 具体构建角色

package com.zhuang.decorator;

/**
 * @Classname FiredNoodles
 * @Description  炒面类 继承 快餐类
 * @Date 2021/3/23 21:54
 * @Created by dell
 */

public class FiredNoodles extends FastFood {

    public FiredNoodles() {
        super(15, "炒面");
    }

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

Garnish 抽象装饰角色

package com.zhuang.decorator;

/**
 * @Classname Garnish
 * @Description  抽象配料类 继承快餐类
 * @Date 2021/3/23 21:58
 * @Created by dell
 */

public abstract class Garnish extends FastFood{
    private FastFood fastFood;

    public FastFood getFastFood() {
        return fastFood;
    }

    public void setFastFood(FastFood fastFood) {
        this.fastFood = fastFood;
    }

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

Egg 具体装饰角色

package com.zhuang.decorator;

/**
 * @Classname Egg
 * @Description  鸡蛋配料类 继承配料类
 * @Date 2021/3/23 21:55
 * @Created by dell
 */

public class Egg extends Garnish {

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

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

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

Bacon 具体装饰角色

package com.zhuang.decorator;

/**
 * @Classname Bacon
 * @Description  培根类 继承配料类
 * @Date 2021/3/23 22:03
 * @Created by dell
 */

public class Bacon extends Garnish {

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

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

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

Client

package com.zhuang.decorator;

/**
 * @Classname Client
 * @Description  装饰器模式测试类
 * @Date 2021/3/23 21:53
 * @Created by dell
 */

public class Client {
    public static void main(String[] args) {
        //点一份炒饭
        FastFood rice = new FiredRice();
        //价格
        System.out.println(rice.getDesc() + "-->" + rice.cost() + "元");

        System.out.println("=============================");

        //点一份加鸡蛋的炒饭
        FastFood eggRice = new FiredRice();
        //加鸡蛋
        eggRice = new Egg(eggRice);
        System.out.println(eggRice.getDesc() + "-->" + eggRice.cost() + "元");

        System.out.println("=============================");

        //点一份加培根的炒面
        FastFood baconNoodles = new FiredNoodles();
        //加培根
        baconNoodles = new Bacon(baconNoodles);
        System.out.println(baconNoodles.getDesc() + "-->" + baconNoodles.cost() + "元");

    }
}

image

12.3 装饰器模式的应用场景

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

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

    • 第一类是系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长;
    • 第二类是因为类定义不能继承(如final类)
  • 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
  • 当对象的功能要求可以动态地添加,也可以再动态地撤销时。

12.4 JDK源码解析

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

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

public class Demo {
    public static void main(String[] args) throws Exception{
        //创建BufferedWriter对象
        //创建FileWriter对象
        FileWriter fw = new FileWriter("C:\\Users\\dell\\Desktop\\a.txt");
        BufferedWriter bw = new BufferedWriter(fw);

        //写数据
        bw.write("hello Buffered");

        bw.close();
    }
}

结构

image

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

静态代理和装饰者模式的区别:

  • 相同点:

    • 都要实现与目标类相同的业务接口
    • 在两个类中都要声明目标对象
    • 都可以在不修改目标类的前提下增强目标方法
  • 不同点:

    • 目的不同 装饰者是为了增强目标对象 静态代理是为了保护和隐藏目标对象
    • 获取目标对象构建的地方不同 装饰者是由外界传递进来,可以通过构造方法传递 静态代理是在代理类内部创建,以此来隐藏目标对象

写在最后

  • 如果我的文章对你有用,请给我点个👍,感谢你😊!
  • 有问题,欢迎在评论区指出!💪

康小庄
12 声望6 粉丝

代码不停,思考不止.