java模式之装饰器模式

2023-10-29

定义:

装饰器模式也叫作包装器模式,只在不改变原有对象的基础上,动态的给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活,属于结构型设计模式

装饰器模式提供了比继承更有弹性的替代方案将功能附加到对象上。因此,装饰器模式的核心功能是功能扩展,使用装饰器模式可以透明且动态的扩展类的功能

应用场景:

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

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

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

UML类图:

 

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

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

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

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

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

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

通用写法:

package com.design.pattern.decorator;
​
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类

package com.design.pattern.decorator;
​
public class Battercake {
    protected String getMsg(){
        return "煎饼";
    }
    public int getPrice(){
        return 5;
    }
}

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

package com.design.pattern.decorator;
​
public class BattercakeWithEgg extends Battercake{
​
    protected String getMsg(){
        return super.getMsg() + "+ 1个鸡蛋";
    }
    public int getPrice(){
        return super.getPrice() + 1;
    }
}

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

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

最后编写客户端测试代码

package com.design.pattern.decorator;
​
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类。

package com.design.pattern.decorator.base;
​
public abstract class Battercake {
    protected abstract String getMsg();
    protected abstract int getPrice();
}

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

package com.design.pattern.decorator.base;
​
public class BaseBattercake extends Battercake{
    @Override
    protected String getMsg() {
        return "煎饼";
    }
    @Override
    protected int getPrice() {
        return 5;
    }
}

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

package com.design.pattern.decorator.base;
​
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

package com.design.pattern.decorator.base;
​
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类

package com.design.pattern.decorator.base;
​
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;
    }
}

在编写客户端测试代码

package com.design.pattern.decorator.base;
​
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. 动态装饰在多层装饰时会更复杂

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java模式之装饰器模式 的相关文章

  • 如果测试用例失败,Selenium Web 驱动程序无法关闭 Firefox 实例

    我各位 我正在使用 junit 和 selenium web 驱动程序 2 28 问题是 如果我运行成功的测试用例 Web 驱动器能够关闭 Firefox 实例 但是当测试用例失败时 Selenium Web 驱动器无法关闭 Firefox
  • 如何在 JFace 的 TableViewer 中创建复选框?

    我创建了一个包含两列的 tableViewer 我想将其中一列设为复选框 为此 我创建了一个 CheckBoxCellEditor 但我不知道为什么它不起作用 名为 tableName 的列显示其值正常 色谱柱规格如下 String COL
  • 我需要在 Spring 中检查每个控制器中的有效会话吗? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 假设在 Spring Mvc 的 Web 应用程序中 我们是否需要检查每个控制器或 jsps 中的有效会话 我该如何解决 MVC 中的
  • 线程自动利用多个CPU核心?

    假设我的应用程序运行 2 个线程 例如渲染线程和游戏更新线程 如果它在具有多核 CPU 当今典型 的移动设备上运行 我是否可以期望线程在可能的情况下自动分配给不同的核心 我知道底层操作系统内核 Android linux内核 决定调度 我的
  • Android Studio 在编译时未检测到支持库

    由于 Android Studio 将成为 Android 开发的默认 IDE 因此我决定将现有项目迁移到 Android studio 中 项目结构似乎不同 我的项目中的文件夹层次结构如下 Complete Project gt idea
  • ExceptionConverter:java.io.IOException:文档没有页面。我正在使用 iText

    当我执行下面的代码时 File f new File c sample pdf PdfWriter getInstance document new FileOutputStream f document open System out p
  • 如何查找 Android 设备中的所有文件并将它们放入列表中?

    我正在寻求帮助来列出 Android 外部存储设备中的所有文件 我想查找所有文件夹 包括主文件夹的子文件夹 有办法吗 我已经做了一个基本的工作 但我仍然没有得到想要的结果 这不起作用 这是我的代码 File files array file
  • HDFS:使用 Java / Scala API 移动多个文件

    我需要使用 Java Scala 程序移动 HDFS 中对应于给定正则表达式的多个文件 例如 我必须移动所有名称为 xml从文件夹a到文件夹b 使用 shell 命令我可以使用以下命令 bin hdfs dfs mv a xml b 我可以
  • Java 页面爬行和解析之 Crawler4j 与 Jsoup

    我想获取页面的内容并提取其中的特定部分 据我所知 此类任务至少有两种解决方案 爬虫4j https github com yasserg crawler4j and Jsoup http jsoup org 它们都能够检索页面的内容并提取其
  • Microsoft Graph 身份验证 - 委派权限

    我可以使用 Microsoft Graph 访问资源无需用户即可访问 https developer microsoft com en us graph docs concepts auth v2 service 但是 此方法不允许我访问需
  • 如何将文件透明地传输到浏览器?

    受控环境 IE8 IIS 7 ColdFusion 当从 IE 发出指向媒体文件 例如 mp3 mpeg 等 的 GET 请求时 浏览器将启动关联的应用程序 Window Media Player 我猜测 IIS 提供文件的方式允许应用程序
  • Spring Data 与 Spring Data JPA 与 JdbcTemplate

    我有信心Spring Data and Spring Data JPA指的是相同的 但后来我在 youtube 上观看了一个关于他正在使用JdbcTemplate在那篇教程中 所以我在那里感到困惑 我想澄清一下两者之间有什么区别Spring
  • 制作java包

    我的 Java 类组织变得有点混乱 所以我要回顾一下我在 Java 学习中跳过的东西 类路径 我无法安静地将心爱的类编译到我为它们创建的包中 这是我的文件夹层次结构 com david Greet java greeter SayHello
  • 使用 AWS Java SDK 为现有 S3 对象设置 Expires 标头

    我正在更新 Amazon S3 存储桶中的现有对象以设置一些元数据 我想设置 HTTPExpires每个对象的标头以更好地处理 HTTP 1 0 客户端 我们正在使用AWS Java SDK http aws amazon com sdkf
  • 将 JSON 参数从 java 发布到 sinatra 服务

    我有一个 Android 应用程序发布到我的 sinatra 服务 早些时候 我无法读取 sinatra 服务上的参数 但是 在我将内容类型设置为 x www form urlencoded 之后 我能够看到参数 但不完全是我想要的 我在
  • Java - 不要用 bufferedwriter 覆盖

    我有一个程序可以将人员添加到数组列表中 我想做的是将这些人也添加到文本文件中 但程序会覆盖第一行 因此这些人会被删除 如何告诉编译器在下一个空闲行写入 import java io import java util import javax
  • 如何测试 spring-security-oauth2 资源服务器安全性?

    随着 Spring Security 4 的发布改进了对测试的支持 http docs spring io spring security site docs 4 0 x reference htmlsingle test我想更新我当前的
  • 将 JTextArea 内容写入文件

    我在 Java Swing 中有一个 JTextArea 和一个 提交 按钮 需要将textarea的内容写入一个带有换行符的文件中 我得到的输出是这样的 它被写为文件中的一个字符串 try BufferedWriter fileOut n
  • 如何修复“sessionFactory”或“hibernateTemplate”是必需的问题

    我正在使用 Spring Boot JPA WEB 和 MYSQL 创建我的 Web 应用程序 它总是说 sessionFactory or hibernateTemplate是必需的 我该如何修复它 我已经尝试过的东西 删除了本地 Mav
  • KeyPressed 和 KeyTyped 混淆[重复]

    这个问题在这里已经有答案了 我搜索过之间的区别KeyPressedand KeyTyped事件 但我仍然不清楚 我发现的一件事是 Keypressed 比 KeyTyped 首先被触发 请澄清一下这些事件何时被准确触发 哪个适合用于哪个目的

随机推荐

  • JAVA中String及String常用的方法

    String string是表示字符串的字符串类 public class StringDemo public static void main String args 常见面试题 String s new String hello 问 如
  • Assuming drive cache: write through

    我也遇到过 关机重启就可以了
  • linux内存管理(十四)-内存OOM触发分析

    在内存分配路径上 当内存不足的时候会触发kswapd 或者内存规整 极端情况会触发OOM 来获取更多内存 在内存回收失败之后 会进行OOM OOM的入口是 alloc pages may oom 文件位于mm page alloc c中 s
  • 线代——基础解系 vs 特征向量

    基础解系 基础解系的概念是针对方程而言的 齐次线性方程组的解集的最大无关组称为齐次线性方程的基础解系 要求齐次线性方程组的通解 只需求出它的基础解系 例 特征向量 特征向量和特征值满足关系式 A A
  • 物理学家的Python

    特点 从基本的交互式 Python 开始 熟悉该语言 详细讨论了所有程序清单 介绍了 Matplotlib 图形 用于生成表示数据和函数图的图形 例如场线 考虑了动画函数图 有一章专门讨论代数方程和超越方程的数值解 讨论了基本的数学原理 并
  • 之前的一些笔记 py

    cmd清空 cls win e 打开我的电脑 好习惯 文件夹后面写数字表明软件的版本 怎么将2个分开的网页窗口合并成一个 鼠标按住一个窗口中的标签页不放 并向其它窗口的标题栏中拖动 即可把当前标签页合并到其它窗口中 注意 一定要按住标签页拖
  • 学习OpenCV——Surf(特征点篇)&flann

    Surf Speed Up Robust Feature Surf算法的原理 1 构建Hessian矩阵构造高斯金字塔尺度空间 其实surf构造的金字塔图像与sift有很大不同 就是因为这些不同才加快了其检测的速度 Sift采用的是DOG图
  • 我的博客即将同步至腾讯云+社区

    我的博客即将同步至腾讯云 社区 邀请大家一同入驻
  • OpenGL笔记之矩阵变换(Matrix Transformation)

    OpenGL笔记之矩阵变换 Matrix Transformation 分类 OpenGL 2012 08 06 21 21 3968人阅读 评论 1 收藏 举报 transformation matrix math 图形 影视 本文是学习
  • nftqin网站登录参数s算法逆向

    原文转载自http www itfvck com 7488 html 抓包数据 method POST authority api nftqin com scheme https path api app passwordLogin con
  • java 代码静态检查_[原创]Java静态代码检查工具介绍

    原创 Java静态代码检查工具介绍 一 什么是静态代码检查 静态代码分析是指无需运行被测代码 仅通过分析或检查源程序的语法 结构 过程 接口等来检查程序的正确性 找出代码隐藏的错误和缺陷 如参数不匹配 有歧义的嵌套语句 错误的递归 非法计算
  • 把win10系统迁移至ssd后,开机时电脑默认不会启动ssd里面系统的问题解决方法

    把win10系统迁移至ssd后 开机时电脑默认不会启动ssd里面系统的问题解决方法 电脑用了两三年了卡到不行 开机要两三分钟 刚开机后两三分钟内卡到都无法操作 于是乎 买了一块三星的860 evo的ssd 固态硬盘 打算对旧机升级一下 简单
  • elasticsearch之嵌套对象、父子文档

    一 嵌套对象 es并非关系型数据库 它并不擅长关系型数据库的join操作 在存储数据时 用冗余数据替代查询时的关联 如blog和comments 如果用关系型数据库 会将blog存一个表 comments存另一个表 然后将这两个表关联起来
  • 华兴数控g71外圆循环编程_数控车床加工时的复合循环指令G70,G71,G72,G73

    复合循环指令应用在切除非一次加工即能加工到规定尺寸的场合 主要在粗车和多次切螺纹的情况下使用 它主要有以下几种 外径 内径粗车循环指令G71 该指令将工件切削到精加工之前的尺寸 精加工前工件形状及粗加工的刀具路径由系统根据精加工尺寸自动设定
  • DataWorks数据埋点的设计及未来发展的思考

    什么是前端埋点 马总曾经说过现在是DT时代 大数据的时代 数据已经成为一家公司最宝贵的财富 越来越多的互联网公司开始重视数据的应用 数据应用的过程是 数据收集 gt 数据整理 数据同步 gt 数据分析 gt 数据可视化 前端埋点是用户行为数
  • ChatGPT的主要应用场景例子

    ChatGPT是一种基于深度学习技术的大型语言模型 它可以根据用户提供的输入信息 生成自然语言文本或响应 这种技术可以应用于很多领域 下面将详细介绍ChatGPT在以下几个方面的应用 以下是使用过程中的一些应用场景对话记录 欢迎补充更多的应
  • 什么是ioc

    什么叫ioc 1 ioc叫做控制反转 是面向对象的一种设计方式 2 把对象的创建和对象之间的调用过程 交给spring管理 3 目的 为了使耦合度降低 耦合度 我有多个service类 都需要调用一个dao类 当我修改这个dao类的位置时
  • Spark Streaming流式数据处理

    目录 一 Spark Streaming 简介 二 简单的例子 三 Spark Streaming相关核心类 3 1 StreamingContext 3 2 离散流 Discretized Streams DStreams 3 3 Inp
  • LiDAR Camera Calibration

    LiDAR和Camera的联合标定 目前有不少方法 不同方法适合不同的传感器 如果有必要 可以自己写一个联合标定的工具 不少公司会自己再写一个 因为标定之后精度可以更高 如果LiDAR是32 及其以上 适合使用Baidu Apollo的方法
  • java模式之装饰器模式

    定义 装饰器模式也叫作包装器模式 只在不改变原有对象的基础上 动态的给一个对象添加一些额外的职责 就增加功能来说 装饰器模式相比生成子类更为灵活 属于结构型设计模式 装饰器模式提供了比继承更有弹性的替代方案将功能附加到对象上 因此 装饰器模