装饰器模式【Decorator Pattern】,什么是装饰模式?核心思想?结构?优缺点?应用场景?

目录


设计模式专栏目录(点击进入…)



什么是装饰器模式?

装饰模式(Decorator Pattern)是结构型的设计模式,它允许在运行时动态地向对象添加新的职责或功能,同时保持对象的原始类不变。通过使用装饰器模式,可以在不修改现有代码的基础上扩展对象的功能。


装饰器模式核心思想

装饰模式的核心思想是通过创建一组装饰器类,这些类包裹原始对象(或其他装饰器),并在包裹的对象上添加新的功能。装饰器类与被装饰对象共享相同的接口,因此可以透明地替换或扩展对象的行为。


装饰器模式结构

(1)抽象构件(Component)

定义一个抽象接口以规范准备接收附加责任的对象。

(2)具体构件(Concrete Component)

实现抽象构件,通过装饰角色为其添加一些职责。

(3)抽象装饰(Decorator)

继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。

(4)具体装饰(ConcreteDecorator)

实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。


装饰器模式优缺点

优点

灵活性:可以动态地组合对象的行为,而不需要继承或修改现有代码。
单一职责原则:装饰器类可以集中处理特定功能,使得代码更加清晰和模块化。
组合优于继承:通过组合多个装饰器,可以实现比继承更灵活的功能扩展。

缺点

复杂性:使用过多的装饰器可能导致系统变得难以理解和维护。
调试困难:由于装饰器层次的嵌套,跟踪和调试可能会变得更加复杂。


装饰器模式应用场景

(1)动态地给对象添加额外的职责

装饰器模式允许在运行时动态地将新功能附加到对象上,而不需要修改现有的类代码。

(2)扩展功能更加灵活

相比于继承,装饰器模式提供了更灵活的扩展方式,避免了类的爆炸性增长。

(3)符合开闭原则

装饰器模式可以在不修改现有代码的情况下引入新的装饰器类,从而扩展系统功能。

(4)透明地给单个对象添加职责

装饰器模式可以透明地给对象添加职责,而不会影响到其他对象。


装饰器模式实现

装饰模式在中国使用的那实在是多,中国的文化是中庸文化,说话或做事情都不能太直接,需要有技巧的;比如:说话吧,你要批评一个人,你不能一上来就说你这个做的不对,那个做的不对,你要先肯定他的成绩,表扬一下优点,然后再指出瑕疵,指出错误的地方,最后再来个激励,你修改了这些缺点后,有那些好处;比如你能带更多的小兵,到个小头目等等,否则你一上来就是一顿批评,你瞅瞅看,肯定是不服气,顶撞甚至是直接“此处不养爷,自有养爷处”开溜哇。

举一个什么例子呢?
就说说上小学的的糗事吧。上小学的时候学习成绩非常的差,班级上 40 多个同学,我基本上都是在排名 45 名以后,按照老师给我的定义就是“不是读书的料”,但是老爸管的很严格,明知道我不是这块料,还是往赶鸭子上架,每次考试完毕我都是战战兢
兢的,“竹笋炒肉”是肯定少不了的,能少点就少点吧,肉可是自己的呀。四年级期末考试考完,学校出来个很损的招儿(这招儿现在很流行的),打印出成绩单,要家长签字,然后才能上五年级,我那个恐惧呀,不过也就是几秒钟的时间,玩起来什么都忘记了。

在这里插入图片描述

1、成绩单的抽象类

package com.uhhe.common.design.decorator;

/**
 * 成绩单的抽象类
 *
 * @author nizhihao
 * @version 1.0.0
 * @date 2023/2/28 16:29
 */
public abstract class SchoolReport {

    /**
     * 成绩单的主要展示的就是你的成绩情况
     */
    public abstract void report();

    /**
     * 成绩单要家长签字,这个是最要命的
     *
     * @param name 姓名
     */
    public abstract void sign(String name);

}

2、实现类(四年级成绩单)

package com.uhhe.common.design.decorator;

/**
 * 四年级的成绩单
 *
 * @author nizhihao
 * @version 1.0.0
 * @date 2023/2/28 16:30
 */
public class FouthGradeSchoolReport extends SchoolReport {

    @Override
    public void report() {
        // 成绩单的格式是这个样子的
        System.out.println("尊敬的XXX家长:");
        System.out.println(" ......");
        System.out.println(" 语文 62 数学65 体育 98 自然 63");
        System.out.println(" .......");
        System.out.println(" 家长签名: ");
    }

    @Override
    public void sign(String name) {
        System.out.println("家长签名为:" + name);
    }

}

成绩单出来,你别看什么 62,65 之类的成绩,要知道在小学低于 90 分基本上就是中下等了,唉,爱学习的人太多了!怎么着,那我把这个成绩单给老爸看看?

就这成绩还要我签字?
老爸就开始找笤帚,我的屁股已经做好了准备,肌肉要绷紧,要不那个太疼了!哈哈,幸运的是,这个不是当时的真实情况,我没有直接把成绩单交给老爸,而是在交给他之前做了点技术工作,

要把成绩单封装一下,封装分类两步走:
第一步:跟老爸说各个科目的最高分,语文最高是 75,数学是 78,自然是 80,然老爸觉的我成绩与最高分数相差不多,这个是实情,但是不知道是什么原因,反正期末考试都考的不怎么样,但是基本上都集中在 70 分以上,我这 60 多分基本上还是垫底的角色;

第二步:在老爸看成绩单后,告诉他我是排名第 38 名,全班,这个也是实情,为啥呢?有将近十个同学退学了!这个情况我是不说的。不知道是不是当时第一次发成绩单,学校没有考虑清楚,没有写上总共有多少同学,排名第几名等等,反正是被我钻了个空子。

3、增加一个抽象类(Decorator 的作用是封装 SchoolReport 类)

package com.uhhe.common.design.decorator;

/**
 * 装饰类,把成绩单装饰一下
 *
 * @author nizhihao
 * @version 1.0.0
 * @date 2023/2/28 16:41
 */
public class Decorator extends SchoolReport {

    /**
     * 首先我要知道是那个成绩单
     */
    private final SchoolReport schoolReport;

    /**
     * 构造函数,传递成绩单过来
     *
     * @param schoolReport 成绩单
     */
    public Decorator(SchoolReport schoolReport) {
        this.schoolReport = schoolReport;
    }

    @Override
    public void report() {
        this.schoolReport.report();
    }

    @Override
    public void sign(String name) {
        this.schoolReport.sign(name);
    }

}

4、HighScoreDecorator 实现类(汇报最高成绩装饰器)

package com.uhhe.common.design.decorator;

/**
 * 把我学校的最高成绩告诉老爸
 *
 * @author nizhihao
 * @version 1.0.0
 * @date 2023/2/28 16:43
 */
public class HighScoreDecorator extends Decorator {

    public HighScoreDecorator(SchoolReport schoolReport) {
        super(schoolReport);
    }

    /**
     * 汇报最高成绩
     */
    private void reportHighScore() {
        System.out.println("这次考试语文最高是75,数学是78,自然是80");
    }

    @Override
    public void report() {
        // 最高成绩我要做老爸看成绩单前告诉他,否则等他一看,就抡起笤帚有揍我,我那还有机会说呀
        this.reportHighScore();
        super.report();
    }

}

5、SortDecorator 实现类(汇报排名装饰)

package com.uhhe.common.design.decorator;

/**
 * 汇报排名装饰
 *
 * @author nizhihao
 * @version 1.0.0
 * @date 2023/2/28 16:44
 */
public class SortDecorator extends Decorator {

    public SortDecorator(SchoolReport schoolReport) {
        super(schoolReport);
    }

    private void reportSort() {
        // 告诉老爸学校的排名情况
        System.out.println("我是排名第38名...");
    }

    @Override
    public void report() {
        // 老爸看完成绩单后再告诉他,加强作用
        super.report();
        this.reportSort();
    }

}

6、看成绩单

package com.uhhe.common.design.decorator;

/**
 * @author nizhihao
 * @version 1.0.0
 * @date 2023/2/28 16:46
 */
public class Father {

    /**
     * 装饰模式【Decorator Pattern】
     * 装饰模式非常好的优点:扩展性非常好
     * <p>
     * ①Component 是一个接口或者是抽象类,就是定义我们最核心的对象,也就是最原始的对象,
     * 比如: 上面的成绩单,记住在装饰模式中,必然有一个被提取出来最核心、最原始、最基本的接口或抽象类就是 Component
     * ②ConcreteComponent 这个事最核心、最原始、最基本的接口或抽象类的实现,你要装饰的就是这个东东。
     * ③Decorator 一般是一个抽象类,做什么用呢?
     * 实现接口或者抽象方法,它里面可不一定有抽象的方法呀,在它的属性里必然有一个 private 变量指向 Component。
     * <p>
     * ConcreteDecoratorA 和 ConcreteDecoratorB 是两个具体的装饰类,你要把你最核心的、最原始的、最
     * 基本的东西装饰城啥东西,上面的例子就是把一个比较平庸的成绩单装饰成家长认可的成绩单。
     * <p>
     * 装饰模式是对继承的有力补充,要知道继承可不是万能的,继承可以解决实际的问题,但是在项目中要考虑诸如易维护、易扩展、易复用等,
     * 而且在一些情况下(比如上面那个成绩单例子)要是用继承就会增加很多了类,而且灵活性非常的差,那当然维护也不容易了,也就是说装饰模式可以替代继承,
     * <p>
     * 解决类膨胀的问题?
     * 要知道继承是静态的给类增加功能,而装饰模式则是动态的给增加功能,
     * 看上面的那个例子,不想要 SortDecorator 这层的封装也很简单呀,直接在 Father 中去掉就可以了,如果用继承就必须修改程序。
     */
    public static void main(String[] args) {
        // 成绩单拿过来
        SchoolReport sr;

        // 原装的成绩单
        sr = new FourthGradeSchoolReport();
        // 加了最高分说明的成绩单
        sr = new HighScoreDecorator(sr);
        // 又加了成绩排名的说明
        sr = new SortDecorator(sr);

        // 看成绩单
        sr.report();

        // 然后老爸,一看,很开心,就签名了
        // 我叫小三,老爸当然叫老三
        sr.sign("老三");
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

未禾

您的支持是我最宝贵的财富!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值