设计模式之美-结构型模式-装饰器模式

news2024/10/5 8:47:47

        装饰器模式主要解决继承关系过于复杂的问题,通过组合来替代继承。指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,装饰器模式提供了比继承更有弹性的替代方案将功能附加到对象上。因此,装饰器模式的核心功能是功能扩展,使用装饰器模式可以透明且动态的扩展类的功能。装饰器模式还有一个特点,那就是可以对原始类嵌套使用多个装饰器。为了满足这个应用场景,在设计的时候,装饰器类需要跟原始类继承相同的抽象类或者接口。

应用场景:

  1. 用于扩展一个类的功能,或者给一个类添加附加职责

  2. 动态的给一个对象添加功能,这些功能可以再动态的被撤销

  3. 需要为一批平行的兄弟类进行改装或者加装功能

UML类图

由上图可知,装饰器模式主要包含了4个角色;

  1. 抽象组建(Component):可以是一个接口或者抽象类,充当被装饰类的原始对象,规定了被装饰对象的行为

  2. 具体组件(ConcreteComponent):实现/继承Component的一个具体对象,即被装饰对象

  3. 抽象装饰器(Decorator):通用的装饰ConcreteComponent的装饰器,其内部必然有一个属性指向Component,其实现一般是一个抽象类,主要为了让其子类按照其构造形式传入一个Component,这是强制的通用行为。如果系统中装饰逻辑单一,则并不需要实现许多装饰器,可以直接省略该类,而直接实现一个具体装饰器即可

  4. 具体装饰器(ConcreteDecorator):Decorator的具体实现类,理论上,每个ConcreteDecorator都扩展了Component对象的一种功能

装饰器模式的实现原理就是,让装饰器实现与被装饰类相同的接口,使得装饰器与被扩展类类型一致,并在构造函数中传入该接口对象,然后再实现这个接口的被包装类属于同一类型,且构造函数的参数为其实现接口类,因此装饰器模式具备嵌套扩展功能,这样就能使用装饰器模式一层一层的对底层被包装类进行功能扩展了

通用写法

public class Client {
    public static void main(String[] args) {

        ConcreteComponent c1 = new ConcreteComponent();
        ConcreteDecoratorA decoratorA = new ConcreteDecoratorA(c1);
        decoratorA.operation();
        System.out.println("---------------------------");
        ConcreteDecoratorB decoratorB = new ConcreteDecoratorB(c1);
        decoratorB.operation();
        System.out.println("---------------------------");
        ConcreteDecoratorB decoratorB1 = new ConcreteDecoratorB(decoratorA);
        decoratorB1.operation();

    }
    static abstract class Component{
        public abstract void operation();
    }
    static class ConcreteComponent extends Component{

        @Override
        public void operation() {
            System.out.println("处理业务逻辑!!");
        }
    }
    static abstract class Decorator extends Component{
        protected Component component;

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

        public void operation(){
            //转发请求给组建对象,可以在转发前后执行一些附加动作
            component.operation();
        }
    }
    static class ConcreteDecoratorA extends Decorator{

        public ConcreteDecoratorA(Component component) {
            super(component);
        }
        private void  operationFirst(){
            System.out.println("ConcreteDecoratorA装饰operationFirst");
        }
        private void  operationLast(){
            System.out.println("ConcreteDecoratorA装饰operationLast");
        }
        public void operation(){
            operationFirst();
            super.operation();
            operationLast();
        }
    }
    static class ConcreteDecoratorB extends Decorator{

        public ConcreteDecoratorB(Component component) {
            super(component);
        }
        private void  operationFirst(){
            System.out.println("ConcreteDecoratorB装饰operationFirst");
        }
        private void  operationLast(){
            System.out.println("ConcreteDecoratorB装饰operationLast");
        }
        public void operation(){
            operationFirst();
            super.operation();
            operationLast();
        }
    }
}

示例:

使用装饰器模式解决煎饼加码问题

下面用代码来模拟给煎饼加码的业务场景,先来看不用装饰器模式的情况。首先创建一个煎饼Battercake类

public class Battercake {
    protected String getMsg(){
        return "煎饼";
    }
    public int getPrice(){
        return 5;
    }
}

 然后创建一个加鸡蛋的煎饼BattercakeWithEgg类

public class BattercakeWithEgg extends Battercake{

    protected String getMsg(){
        return super.getMsg() + "+ 1个鸡蛋";
    }
    public int getPrice(){
        return super.getPrice() + 1;
    }
}

在创建一个既加鸡蛋又加香肠的BattercakeWithEggAndSausage类

public class BattercakeWithEggAndSausage extends BattercakeWithEgg{
    protected String getMsg(){
        return super.getMsg() + "+ 1根香肠";
    }
    public int getPrice(){
        return super.getPrice() + 2;
    }
}

最后编写客户端测试代码

public class ClientTest {
    public static void main(String[] args) {
        Battercake battercake = new Battercake();
        System.out.println(battercake.getMsg() + ",总价格" + battercake.getPrice());
        BattercakeWithEgg battercakeWithEgg = new BattercakeWithEgg();
        System.out.println(battercakeWithEgg.getMsg() + ",总价格" + battercakeWithEgg.getPrice());
        BattercakeWithEggAndSausage battercakeWithEggAndSausage = new BattercakeWithEggAndSausage();
        System.out.println(battercakeWithEggAndSausage.getMsg() + ",总价格" + battercakeWithEggAndSausage.getPrice());
    }
}

运行结果如下:

煎饼,总价格5
煎饼+ 1个鸡蛋,总价格6
煎饼+ 1个鸡蛋+ 1根香肠,总价格8

运行结果没有问题。但是,如果用户需要一个加2个鸡蛋和1根香肠的煎饼,则用现在的类结构是创建不出来的,也无法自动计算出价格,除非再创建一个类做定制。如果需求在变,那么一直加定制显然是不科学的

下面用装饰器模式来解决上面的问题。首先创建一个煎饼的抽象Battercake类。

public abstract class Battercake {
    protected abstract String getMsg();
    protected abstract int getPrice();
}

创建一个基本的煎饼(或者叫基础套餐)BaseBattercake.

public class BaseBattercake extends Battercake{
    @Override
    protected String getMsg() {
        return "煎饼";
    }
    @Override
    protected int getPrice() {
        return 5;
    }
}

然后创建一个扩张套餐的抽象装饰器BattercakeDecotator类

public abstract class BattercakeDecotator extends Battercake{
    private Battercake battercake;

    public BattercakeDecotator(Battercake battercake){
        this.battercake = battercake;
    }
    protected abstract void doSomething();

    protected String getMsg(){
        return this.battercake.getMsg();
    }
    protected int getPrice(){
        return this.battercake.getPrice();
    }
}

接着创建鸡蛋装饰器EggDecorator

public class EggDecorator extends BattercakeDecotator{
    public EggDecorator(Battercake battercake) {
        super(battercake);
    }

    @Override
    protected void doSomething() {
    }
    protected String getMsg(){
        return super.getMsg() + "+1个鸡蛋";
    }
    protected int getPrice(){
        return super.getPrice() + 1;
    }
}

创建香肠装饰器SausageDecorator类

public class SausageDecorator extends BattercakeDecotator{
    public SausageDecorator(Battercake battercake) {
        super(battercake);
    }
    @Override
    protected void doSomething() {
    }
    protected String getMsg(){
        return super.getMsg() + "+1根香肠";
    }
    protected int getPrice(){
        return super.getPrice() + 2;
    }
}

在编写客户端测试代码

public class Client {
    public static void main(String[] args) {
        //买一个煎饼
        Battercake battercake;
        battercake = new BaseBattercake();
        //煎饼有点小,想再加1个鸡蛋
        battercake = new EggDecorator(battercake);
        battercake = new EggDecorator(battercake);
        battercake = new SausageDecorator(battercake);
        System.out.println(battercake.getMsg() + ",总价: " + battercake.getPrice());
    }
}

运行结果如下图所示:

煎饼+1个鸡蛋+1个鸡蛋+1根香肠,总价: 9

装饰器模式与代理模式区别

从代理模式的UML类图和通用代码实现上看,代理模式与装饰器模式几乎一摸一样。代理模式的Subject对应装饰器模式的Component,代理模式的RealSubject对应装饰器模式的Concrete Component,代理模式的Proxy对应的装饰器模式的Decorator。确实,从代码实现上看,代理模式的确与装饰器模式是一样的,但是这两种设计模式多面向的功能扩展面是不一样的。

装饰器模式强调自身功能的扩展。Decorator所做的就是增强ConcreteComponent的功能,主体对象为ConcreteComponent,着重类功能的变化。

代理模式强调对代理过程的控制。Proxy完全掌握对RealSubject的访问控制,因此,Proxy可以决定对RealSubject进行功能扩展,功能缩减甚至功能散失,主体对象为Proxy。、

装饰器模式优缺点

优点:

  1. 装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态地给一个对象扩展功能,即插即用。

  2. 通过使用不同装饰类及这些装饰类的排列组合,可以实现不同效果

  3. 装饰器模式完全遵守开闭原则

缺点:

  1. 会出现更多的代码,更多的类,增加程序的复杂性

  2. 动态装饰在多层装饰时会更复杂

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/419101.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

4月20日专家谈:内网突遭攻击,安全人员一招有效处理

随着网络威胁的愈加频繁,企业面临的安全问题也越来越多,传统的安全能力在面对日益增长的安全问题时显得捉襟见肘。 SOAR借助安全编排和自动化技术,将人工操作和技术集成在一起,自动化完成安全处置,帮助企业更快地响应…

JavaScript【九】JavaScript BOM(浏览器对象模型)

文章目录🌟前言🌟 Bom(浏览器对象模型)🌟window对象:🌟属性:🌟 方法:🌟 获取元素:🌟 添加点击事件:🌟 获取表单…

大数据Flink进阶(二十):Flink细粒度资源管理

文章目录 Flink细粒度资源管理 一、细粒度资源管理介绍 二、细粒度资源适用场景

关于合金电阻

合金电阻是一种具有高精度、高稳定性和高温度特性的电阻器件,广泛应用于各种电子设备中。选型合适的合金电阻并进行合理的设计,可以有效地提高电路的性能和可靠性。本文将从合金电阻的基本原理、选型方法及设计要点等方面进行详细介绍。 一、合金电阻的基…

简单认识下with和上下文管理器

with 对于系统资源如文件、数据库连接、socket,应用程序打开这些资源并执行完业务逻辑之后,必须关闭(断开)该资源。系统允许打开的最大文件数量是有限的,如果我们打开文件后没有及时关闭,极端情况下会出现…

21天学会C++:Day2----命名空间的那些事儿

CSDN的uu们,大家好。这里是C入门的第二讲。 座右铭:前路坎坷,披荆斩棘,扶摇直上。 博客主页: 姬如祎 收录专栏:C专题 目录 1. 为什么要有命名空间 2. 命名空间的定义 3. 访问命名空间域中成员的三种方…

基于Java+SpringBoot+vue的人职匹配推荐系统设计与实现【源码(完整源码请私聊)+论文+演示视频+包运行成功】

博主介绍:专注于Java技术领域和毕业项目实战 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇🏻 不然下次找不到哟 Java项目精品实战案例(300套) 目录 一、效果演示 二、…

virtualbox如何配网

配网搞了一天!!! 百度到的所有教程都是垃圾!! 就没有一个写全的!!写明白怎么配的!!! 我自己来!!!不会配的看我&#xf…

浅析 Queue 和 Deque

终于开始了 LeetCode 的练习,看到 102. 二叉树的层序遍历 有种解法利用到了队列,想着挨个看看基础队列中的方法,便有了这篇文章。 基于 Java 对 Queue 以及 Deque(double ended queue) 实现进行学习介绍,JD…

Vue之代码传送(teleport)

代码传送是啥 在Vue中,代码传送就是将某部分的代码从Vue的template标签下传送到指定的地方,这个地方通常是body标签下。在使用Vue编写界面时,我们都是在html的Body中写一个div,然后指定一个id,然后在Vue的实例中的tem…

2023春招offer收割机,阿里架构师耗时半月写的《Java面试手册》

程序猿在世人眼里已经成为高薪、为人忠诚的代名词。 然而,小编要说的是,不是所有的程序员工资都是一样的。 世人所不知的是同为程序猿,薪资的差别还是很大的。 众所周知,目前互联网行业是众多行业中薪资待遇最好的,包…

Java语法理论和面经杂疑篇《九. 网络编程》

目录 1. 网络编程概述 1.1 软件架构 1.2 网络基础 2. 网络通信要素 2.1 如何实现网络中的主机互相通信 2.2 通信要素一:IP地址和域名 2.2.1 IP地址 2.2.2 域名 2.3 通信要素二:端口号 2.4 通信要素三:网络通信协议 2. 谈传输层协议…

时间序列教程 四、自回归和移动平均模型

一、本节目标 了解自相关函数(ACF)。 了解部分自相关函数(PACF)。 了解自回归和移动平均模型是如何工作的。 使用Python来拟合自相关模型。 二、ACF和PACF 1、自相关函数(ACF) 测量信号与自身延迟数据的相关性。 它用于发现信号中的重复模式,例如周期性信号的存…

Spring相关概念

Spring家族 官网:Spring | Home,从官网我们可以大概了解到: Spring能做什么:用以开发web、微服务以及分布式系统等,光这三块就已经占了JavaEE开发 的九成多。Spring并不是单一的一个技术,而是一个大家族,可以从官网的…

.NET基础加强第七课--文件流

序列化 JSON序列化 例子 using Nancy.Json; Person p1 new Person(); p1.Name “zs”; // json 序列化 JavaScriptSerializer javaScriptSerializer new JavaScriptSerializer(); string json javaScriptSerializer.Serialize(p1); Console.WriteLine(json); Console.…

linux-创建子进程的过程与原理(fork讲解)

我们知道,子进程可以被命令行创建,被fork函数创建,但是子进程创建了什么呢,是完全拷贝父进程函数?还是继承父进程数据呢? 首先我们要知道,进程的构成:进程内核数据结构可运行程序载…

二维差分【算法推导,图文讲解清晰】

798. 差分矩阵 - AcWing题库 算法推导 二维差分相对一维差分会复杂一点,而且还要结合二维前缀和的一些细节处理 A、B数组角色问题 在差分思想中,构造并不是那么重要,而是其中A、B数组的角色。 我们假想存在一个数组B,输入的A…

论文阅读《NeRF-Supervised Deep Stereo》

论文地址:https://arxiv.org/pdf/2303.17603.pdf 源码地址:https://nerfstereo.github.io/ 概述 针对深度估计的标签数据难以获取,自监督方法在病态(遮挡、非朗伯面)区域的表现差,跨域泛化能力弱的问题&…

【jvm系列-06】深入理解对象的实例化、内存布局和访问定位

JVM系列整体栏目 内容链接地址【一】初识虚拟机与java虚拟机https://blog.csdn.net/zhenghuishengq/article/details/129544460【二】jvm的类加载子系统以及jclasslib的基本使用https://blog.csdn.net/zhenghuishengq/article/details/129610963【三】运行时私有区域之虚拟机栈…

Vue——组件基础

目录 定义一个组件​ 使用组件​ 传递 props​ 监听事件​ 通过插槽来分配内容​ 动态组件​ DOM 模板解析注意事项​ 大小写区分​ 闭合标签​ 元素位置限制​ 组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。在实际应…