装饰者模式如何拯救了我的一天

在工作中,我正在处理庞大的Java代码库,该代码库是由许多不同的开发人员在15年的时间里开发的。 并不是所有的事情都由书来完成,但是同时我通常没有机会重构遇到的每一个奇怪之处。

尽管如此,仍可以每天采取提高代码质量的措施。 今天就像那样……

总览

由于已经 存在 大量 教程 ,因此本文的目的不是教授装饰器模式。 相反,它提供了一个现实生活中的示例,说明它如何派上用场并节省了一天的时间。

情况

我们的UI包含Swing的JEdi​​torPanes ,用于显示HTML。 与各种链接的交互(例如,悬停和单击)触发以下一个或多个响应:

  1. 记录事件
  2. 更改光标(JEditorPane已经自行完成;似乎从2000年5月开始-…?!)
  3. 使用链接的内容更新窗格
  4. 打开外部浏览器
  5. 打开外部应用程序
  6. 处理内部服务请求

对于所有窗格,这些响应都不相同。 其中有一些需求部分不同。 (如果您知道装饰器模式,那么您会看到它的去向。)

所以问题是:您如何实施这些响应?

一种可配置类的解决方案

您可以将所有这些都放在一个类中,该类实现HyperlinkListener并使用标志来(取消激活)不同的响应。

vaskas_complex_allinone

这个课真是地狱! 是的,地狱 就这么简单。

首先,它将是巨大的。 而且,其本质上不相关的职责之间可能以某种方式有些奇怪的依赖。这种规模和这些关系将使编写和测试变得更加困难,甚至使理解和修改变得更加困难。

(顺便说一句,造成混乱的根本原因是AllInOneHyperlinkListener违反了单一职责原则 。由于这篇文章已经足够长了,因此我将不做详细介绍。)

继承的解决方案

无论如何,我很幸运没有发现自己正在面对一个庞然大物的听众。 取而代之的是,我发现了一个小的类层次结构,这些类将这些职责划分为它们( HLHyperlinkListener的缩写):

  1. CursorSettingHL implements HL :记录事件并设置光标
  2. UrlProcessingHL extends CursorSettingHL
    通过更新窗格的内容或打开外部浏览器/应用程序来处理URL
  3. ServiceRequestHandlingHL extends UrlProcessingHL :如果是服务请求,则处理URL; 否则委托给它的超类

vaskas_complex_inheritance

这看起来更好,不是吗? 好…

首先,某些班级仍然要承担几项责任。 没有真正的理由解释为什么日志和更改游标应该由同一类完成。 (我只能猜测,这种结构会随着时间的推移而有机地增长,而没有任何更深层次的设计。)因此,问题较小,但尚未消失。

它也显示在班级名称中。 以上内容已经过改进,以提高可读性。 原始文档中充满了DefaultSimple和其他非信息。 这个名字甚至是误导性的名字都不是简单的疏忽。 它们是缺乏凝聚力的自然结果。

但是通过更深层次的管理,这些问题本来可以得到缓解。 六个类可以各自实现一件事。 但这也不会帮助我。

不,此解决方案的真正问题是模拟的灵活性。 看起来您可以选择,但实际上您不能。 看看当事情改变时会发生什么。

改变

我们慢慢地从摇摆移动到JavaFX的,我想以取代FX JEdi​​torPane中” 的WebView 。 (实际上,将HyperlinkListeners放入WebView有点麻烦,但我将在另一篇文章中再讨论。)WebView已经完成了上述某些操作,因此这是新侦听器具有的更新响应列表。触发:

  1. 记录事件
  2. 改变光标
  3. 用新内容更新窗格
  4. 打开外部浏览器
  5. 打开外部应用程序
  6. 处理内部服务请求

在这里,整个类系统变得毫无用处。 (至少因为我不愿意让监听者对隐身控件进行2.和3.。)在这一点上,很明显,职责混在一起了。 我仍然需要其中一些,但不是全部,而且由于它们之间没有阶级界限,所以我处于全有或全无的情况。

装饰图案的救援

因此,当我在考虑要混合和匹配现有功能的程度时,它最终使我受了折磨(并且比预期的要晚得多):这正是装饰器模式的目的!

装饰图案

就像我说的,我不会详细解释这种模式,但是基本思想是:

当有一个接口,不同的实现可以提供不同的功能时,请让每个实现独立运行。 但是要实现它们,以便在工作中的某个时刻,他们将控制权移交给同一接口的另一个实例。

如果一个这样的实现调用另一个,并使用该结果来计算自己的实现,则两者都可以做自己的事情,但是效果会重叠。 第二个实例的结果仍然存在,但是第一个实例有些改变。 因此,据说第一个装饰第二个。

这可以在更多实例中进行,每个实例都装饰前者。 应该将其视为分层系统,其中每个装饰器向整体添加另一层行为。

行动中

现在方法很清楚:我将上述功能重构为不同的装饰器,例如LoggingHyperlinkListenerDecoratorServiceRequestHandlingHyperlinkListenerDecorator

vaskas_complex_decorator

然后,我删除了原始类,并用正确的装饰器组合替换了它们的用途。 最终,我了解了新功能并选择了正确的装饰器。 用Java 8可以做到这一点,但是为了简单起见,让我们在这里使用构造函数:

将装饰器放在一起

// use a lambda expression to create the initial listener
// which does nothing
HyperlinkListener listener = event -> {}; 
// these decorators first do their own thing and then call the
// decorated listener (the one handed over during construction);
// in the end, the last added decorator will act first
listener = new ExternalApplicationOpeningHyperlinkListenerDecorator(listener);
listener =new BrowserOpeningHyperlinkListenerDecorator(listener);
listener =new ServiceRequestHandlingHyperlinkListenerDecorator(listener);
listener =new LoggingHyperlinkListenerDecorator(listener);

除了样板外,很明显这里发生了什么。 首先,在我们确定服务请求并处理它们之前,将进行日志记录。 如果可能,将在浏览器中打开其他任何内容; 否则,我们会将其交给一些外部应用程序。

vaskas_complex_decorated

效果

您马上就可以看到对代码的积极影响。 首先,每个班级都有一个非常简单的责任。 这导致了简短易懂的课程。 他们的名字通常是当场就正确地告诉您他们在做什么。 另外,由于每个单元中发生的事情更少,因此可测试性也提高了。

此外,将装饰器放在一起的地方更能揭示其意图。 您不必检查实例化的ServiceRequestHandlingHyperlinkListener及其超类即可了解侦听器的确切功能。 取而代之的是,您仅查看装饰列表,看看会发生什么。

最后但并非最不重要的一点是,它使代码为将来的更改做好了准备。 现在很明显如何实现新的侦听器功能。 对于继承的类,您必须想知道在何处放置新功能,以及新功能如何影响该类的现有用法。 现在,您只需要实现第um装饰器并在需要的地方添加它即可。

反射

这个真实的例子展示了装饰器模式的应用如何使代码更易于阅读,测试和更改。

当然,这不是自动的。 该模式只能在确实使代码更整洁的地方使用。 但是要决定这一点,您必须了解它并且必须能够推理其影响。 我希望这篇文章对此有所帮助。

非常感谢Wikipedia的Benjah ,他创造了 Vaska复杂建筑 的美丽形象并将其发布到公共领域。

翻译自: https://www.javacodegeeks.com/2015/01/how-the-decorator-pattern-saved-my-day.html

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

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

相关文章

快速的骆驼和云消息传递

Apache Camel是一个流行的,成熟的开源集成库。 它实现了企业集成模式 ,这是在集成分布式系统时经常出现的一组模式。 过去,我写过很多关于Camel的文章, 包括为什么我比Spring Integration更喜欢它 , 路由引擎 如何 工作…

三角形类1

/* 程序的版权和版本声明部分 Copyright (c)2012, 烟台大学计算机学院学生 All rightsreserved. 文件名称: object.cpp 作者:刘清远 完成日期: 2013年3月29日 版本号: v1.0 输入描述:无 问题描述:设计求三…

android 自定义xml属性

Android 自定义组件 Android 提供了非常精致的和非常强大的组件化模型,能够更加方便的构建UI,这些UI组件都是基于基本的layout类:View 和 ViewGroup。 部分能够用的widgets包括:Button,TextView,EditText,ListView,CheckBox,Radio…

LeetCode: Longest Common Prefix

string.erase没掌握好&#xff0c;悲了个剧&#xff0c;2次过 1 class Solution {2 public:3 string longestCommonPrefix(vector<string> &strs) {4 // Start typing your C/C solution below5 // DO NOT write int main() function6 s…

流式传输大数据:Storm,Spark和Samza

有许多分布式计算系统可以实时或近实时处理大数据。 本文将从对三个Apache框架的简短描述开始&#xff0c;并试图对它们之间的某些相似之处和不同之处提供一个快速的高级概述。 阿帕奇风暴 在风暴 &#xff0c;你设计要求的T opology实时计算的图&#xff0c;然后喂到集群&…

uniapp使用阿里云多色图标

下载&#xff0c;然后解压 输入cmd&#xff0c;然后enter 输入 npm install -g iconfont-tools 再输入 iconfont-tools&#xff0c;然后一直enter&#xff0c;直到结束 目录会多了个iconfont-weapp文件&#xff0c;点击去找到 iconfont-weapp-icon.css 导入和使用 t-icon开头 接…

针对Java中的XSD验证XML

有许多工具可用于根据XSD 验证XML文档 。 其中包括操作系统脚本和工具&#xff0c;例如xmllint &#xff0c;XML编辑器和IDE&#xff0c;甚至是在线验证器。 由于前面提到的方法的局限性或问题&#xff0c;我发现拥有自己的易于使用的XML验证工具很有用。 Java使编写这样的工具…

uniapp uni.request GET方式请求,不能直接传数组解决方法

这里写目录标题目录遇到的问题 GET请求方法传数组解决方案目录 遇到的问题 GET请求方法传数组 想传一个数组&#xff0c;但是后台接受到的数据与浏览器中显示的数据和前台代码传的不一样&#xff1b; 前台代码打印 浏览器显示数据 其中HerbalNameList &#xff0c;变成了字…

休眠CascadeType.LOCK陷阱

介绍 引入了Hibernate 显式锁定支持以及Cascade Types之后 &#xff0c;就该分析CascadeType.LOCK行为了。 休眠锁定请求触发内部LockEvent 。 关联的DefaultLockEventListener可以将锁定请求级联到锁定实体子级。 由于CascadeType.ALL也包括CascadeType.LOCK &#xff0c;因…

c++中在堆和栈中申请空间的差别

堆中和栈中申请的空间的比较, 我找到了下面的比较: 栈的情况&#xff1a;栈上分配空间的好处是快&#xff0c;而且对象生存期是自动的&#xff0c;离开当前域之后就自动析构回收。坏处就是栈空间有限&#xff0c;而且不能人为控制对象的生存期&#xff0c;比如你无法将一个函数…

推销自己的海盗猫王运营商

因此&#xff0c;Java没有Elvis运算符&#xff08;或者更正式的名称是null合并运算符或null安全成员选择&#xff09;……虽然我个人不太在意它&#xff0c;但有些人似乎很喜欢它。 当一位同事需要几天后&#xff0c;我坐下来探讨了我们的选择。 而且你知道什么&#xff01; 您…

使用CDI简化JAX-RS缓存

这篇文章&#xff08;通过一个简单的示例&#xff09;说明了如何使用CDI Producers使其在RESTful服务中利用缓存控制语义更加容易 与HTTP 1.0中可用的Expires标头相比&#xff0c; HTTP 1.1中添加了Cache-Control标头&#xff0c;这是急需的改进。 RESTful Web服务可以利用此标…

transform限制position:fixed的跟随效果

我们应该都知道&#xff0c;position:fixed可以让元素不跟随浏览器的滚动条滚动&#xff0c;而且这种跟随效果连它的兄弟们position:relative/absolute都限制不了。但是&#xff0c;真是一物降一物&#xff0c;position:fixed固定效果却被小小的transform给干掉了&#xff0c;直…

Java 8 Lambda表达式教程

问候&#xff01; :) 离开几个月后&#xff0c;我决定恢复风格:)。 我注意到我以前有关新的Date / Time API的一篇文章非常受欢迎&#xff0c;因此这次我将把本篇文章专门介绍Java 8的另一个新功能&#xff1a; Lambda Expressions 。 功能编程 Lambda表达式是Java编程语言最…

[HDU] 2553 N皇后问题-简单深搜

题目链接&#xff1a; http://acm.hdu.edu.cn/showproblem.php?pid2553 方法&#xff1a; 1.可以用对称的思想&#xff0c;即&#xff1a;如果N是偶数&#xff0c;则只计算第一个皇后分别放在第一行的位置1到N/2这N/2个情况的结果和&#xff0c;最后再乘以2。如果是奇数&#…

您真的了解@WebService吗?

SOAP Web服务无论如何都不是最先进的技术-尽管它仍然存在&#xff0c;但是基于REST的Web服务却提供了激烈的竞争。 无论如何–这绝对不是REST vs SOAP帖子&#xff01; 我观察到了一些实例&#xff0c;至少可以说&#xff0c;使用基于Java的SOAP Web服务的方式不太理想。 我认…

Error: Module “xxx“ does not exist in container. / antd pro v5启用qiankun报错 / 同时使用mfsu和qiankun报错

一、问题描述 我们用antd pro v5搭建前端项目&#xff0c;启用qiankun微前端模式&#xff0c;终端报错如下&#xff1a; Uncaught (in promise) Error: Module “xxx” does not exist in container. while loading “xxx” from webpack/container/reference/mf 经过排查&…

双链表

问题&#xff1a;在分配空间时&#xff0c;遇到问题 定义一个结构体&#xff1a; typedef struct dLinkListNode{ int data; struct dLinkListNode *prior; struct dLinkListNode *next;}*dLinkList,dListNode; dList(dLinkList)malloc(sizeof(dListNode));与dList(dLinkList)m…

JVMTI标记如何影响GC暂停

这篇文章分析了为什么Plumbr Agents在某些情况下以及如何延长GC暂停的时间。 对基本问题进行故障诊断揭示了有关在GC暂停期间如何处理JVMTI标记的有趣见解。 发现问题 我们的一位客户抱怨说&#xff0c;附加了Plumbr代理后&#xff0c;应用程序的响应速度明显降低。 通过分析G…

使用Apache Hadoop计算PageRanks

目前&#xff0c;我正在接受Coursera的培训“ 挖掘海量数据集 ”。 我对MapReduce和Apache Hadoop感兴趣已有一段时间了&#xff0c;通过本课程&#xff0c;我希望对何时以及如何MapReduce可以帮助解决一些现实世界中的业务问题有更多的了解&#xff08;我在这里介绍了另一种解…