• 设计模式——装饰器模式


    一、装饰器模式

    1.1 概述

    上班族大多都有睡懒觉的习惯,每天早上上班时间都很紧张,于是很多人为了多睡一会,就会用方便的方式解决早餐问题。有些人早餐可能会吃煎饼,煎饼中可以加鸡蛋,也可以加香肠,但是不管怎么“加码”,都还是一个煎饼。在现实生活中,常常需要对现有产品增加新的功能或美化其外观,如房子装修、相片加相框等,都是装饰器模式。
    在软件开发过程中,有时想用一些现存的组件。这些组件可能只是完成了一些核心功能。但在不改变其结构的情况下,可以动态地扩展其功能。所有这些都可以釆用装饰器模式来实现。

    1.2 定义

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

    1.3 结构

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

    1.4 案例

    快餐店有炒面、炒饭这些快餐,可以额外附加鸡蛋、火腿、培根这些配菜,当然加配菜需要额外加钱,每个配菜的价钱通常不太一样,那么计算总价就会显得比较麻烦。案例类图:
    在这里插入图片描述

    //快餐接口
    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 void setPrice(float price) {
            this.price = price;
        }
    
        public float getPrice() {
            return price;
        }
    
        public String getDesc() {
            return desc;
        }
    
        public void setDesc(String desc) {
            this.desc = desc;
        }
    
        public abstract float cost();  //获取价格
    }
    
    //炒饭
    public class FriedRice extends FastFood {
    
        public FriedRice() {
            super(10, "炒饭");
        }
    
        public float cost() {
            return getPrice();
        }
    }
    
    //炒面
    public class FriedNoodles extends FastFood {
    
        public FriedNoodles() {
            super(12, "炒面");
        }
    
        public float cost() {
            return getPrice();
        }
    }
    
    //配料类
    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;
        }
    }
    
    //鸡蛋配料
    public class Egg extends Garnish {
    
        public Egg(FastFood fastFood) {
            super(fastFood,1,"鸡蛋");
        }
    
        public float cost() {
            return getPrice() + getFastFood().getPrice();
        }
    
        @Override
        public String getDesc() {
            return super.getDesc() + getFastFood().getDesc();
        }
    }
    
    //培根配料
    public class Bacon extends Garnish {
    
        public Bacon(FastFood fastFood) {
    
            super(fastFood,2,"培根");
        }
    
        @Override
        public float cost() {
            return getPrice() + getFastFood().getPrice();
        }
    
        @Override
        public String getDesc() {
            return super.getDesc() + getFastFood().getDesc();
        }
    }
    
    //测试类
    public class Client {
        public static void main(String[] args) {
            //点一份炒饭
            FastFood food = new FriedRice();
            //花费的价格
            System.out.println(food.getDesc() + " " + food.cost() + "元");
    
            System.out.println("========");
            //点一份加鸡蛋的炒饭
            FastFood food1 = new FriedRice();
    
            food1 = new Egg(food1);
            //花费的价格
            System.out.println(food1.getDesc() + " " + food1.cost() + "元");
    
            System.out.println("========");
            //点一份加培根的炒面
            FastFood food2 = new FriedNoodles();
            food2 = new Bacon(food2);
            //花费的价格
            System.out.println(food2.getDesc() + " " + food2.cost() + "元");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135

    1.5 优缺点

    优点

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

    缺点

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

    1.6 使用场景

    • 当需要给一个现有类添加附加职责,而又不能采用生成子类的方法进行扩充时。例如,该类被隐藏或者该类是终极类或者采用继承方式会产生大量的子类。
    • 当需要通过对现有的一组基本功能进行排列组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰器模式却很好实现。
    • 当对象的功能要求可以动态地添加,也可以再动态地撤销时

    1.8 和动态代理区别

    • 装饰者(Decorator)和被装饰者(Decoratee)都实现一个接口。对代理模式来说,代理类(Proxy Class)和真实处理的类(Real Class)都实现同一个接口。
    • 装饰者模式关注于对对象功能的增强,代理模式关注于对对象的访问控制。
    • 代理模式一般在代理类中自动创建,常常要隐藏被目标类。装饰者模式一般是在类外传进来,不需要特地隐藏目标类。
    • 代理类的功能是静态定义好的,一般不支持多层嵌套,使用时一般是对目标类固定的功能增强。装设器类支持层层嵌套,使用时可以目标类动态的功能增强。

    1.7 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\\Think\\Desktop\\a.txt");
            BufferedWriter bw = new BufferedWriter(fw);
            //写数据
            bw.write("hello Buffered");
    
            bw.close();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    小结:

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

  • 相关阅读:
    远程连接GPU服务器的Jupyter Notebook
    string(讲解)
    centos+docker+pytest+jenkins+gitee+allure部署自动化环境
    JavaScript 67 JavaScript HTML DOM 67.10 JavaScript HTML DOM 事件监听程序
    Cerebral Cortex:从任务态和静息态脑功能连接预测儿童数学技能
    网站备份很重要:WordPress七牛云镜像存储插件,一键镜像静态资源到七牛云
    基于单片机设计的自动门控制系统
    JAVA【操作BLOB类型字段】【批量操作】
    bash调试方法总结
    AIGC视频生成/编辑技术调研报告
  • 原文地址:https://blog.csdn.net/qq_51114283/article/details/125874965