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

设计模式装饰者模式

在工作中,我正在处理庞大的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及其超类即可了解侦听器的确切功能。 取而代之的是,您仅查看装饰列表,看看会发生什么。

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

反射

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

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

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

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

设计模式装饰者模式

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

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

相关文章

初学者选黑卡还是微单_明日之镜 十年索尼微单和他的镜头王国

2010年索尼微单诞生,与之一同而来的还有全新的E卡口。十年间,E卡口从APS-C走向了全画幅,从静态影像走向了专业摄像。并且准瞬间通过机器与机器、镜头与镜头架起了消费级与专业级的桥梁。想来一个卡口创作无限界大抵就是如此吧。起源一枚扫街用…

C语言 | 字符数组

C语言字符数组的定义字符数组是用来存放字符数据的数组,字符数组中的一个元素存放一个字符,定义字符数组的方法和定义数值型数组的方法类似。//例子:char character[10];C语言字符数组的初始化C语言对字符数组初始化,最容易理解的…

linux暗转旧版java_Java旧版不断发展

linux暗转旧版java最近&#xff0c;我偶然发现了JDK API的一个非常有趣的警告&#xff0c;即Class.getConstructors()方法。 它的方法签名是这样的&#xff1a; Constructor<?>[] getConstructors()有趣的是&#xff0c; Class.getConstructor(Class...)返回一个Constr…

C语言和指针的本质是什么?技术大神给出答案

很多编程语言都以 “没有指针” 作为自己的优势来宣传&#xff0c;然而&#xff0c;对于C语言&#xff0c;指针却是与生俱来的。那么&#xff0c;什么是指针&#xff0c;为什么大家都想避开指针。很简单&#xff0c; 指针就是地址&#xff0c;当一个地址作为一个变量存在时&…

fastdfs的tracker启动之后一直选举_jraft选举策略

一、角色划分Follower&#xff1a;完全被动&#xff0c;不能发送任何请求&#xff0c;只接受并响应来自leader 和 candidate 的 message&#xff0c;每个节点启动后的初始状态一定是follower&#xff1b;Leader&#xff1a;处理所有来自客户端的请求&#xff0c;以及复制 log到…

C语言 | 二维数组

C语言二维数组的定义一般形式 类型说明符 数组名[常量表达式][常量表达式]int a[10][6],b[3][4];用矩阵形式表示二维数组&#xff0c;是逻辑上的概念&#xff0c;能形象地表示出行列关系&#xff0c;而在内存中&#xff0c;各元素是连续存放的&#xff0c;不是二维的&#xff0…

C语言 | 一维数组

C语言一维数组的定义一般形式类型符 数组名[常量表达式]C语言使用一维数组注意事项数组名的命名规则和变量名相同&#xff0c;遵循标识符命名规则。在定义数组时&#xff0c;需要指定数组中元素的个数&#xff0c;方括号中的常量表达式用来表示元素的个数&#xff0c;即数组长度…

怎样删去csv中重复行_4个锦囊,祝你快速删去Excel中的重复数据

快速删去重复数据&#xff0c;雷哥在这里提供4种超简单的解决方法&#xff0c;大家一看就会。为了更加直接明了地说明&#xff0c;雷哥在这里通过具体案例进行讲解。案例&#xff1a;下图B列日期中&#xff0c;橘黄色部分是我们已经标记好颜色的重复项。要求&#xff1a;快速删…

C语言通过printf()设置输出显示方式

在调试程序时多数情况下要输出很多提示信息&#xff0c;通过控制输出字体的颜色的显示方式可以方便我们快速查看有用的信息&#xff0c;而printf()的终端转义就为我们提供了这样的手段。我们大家知道在程序结尾加上\n就可在输出的时候换行&#xff0c;其实这就是个转义字符。而…

如何取消计算机阻止安装程序,Win10电脑安装软件提示“你必须取消阻止该发布者才能运行此软件”怎么办...

&#xfeff;我们在使用win10系统的时候&#xff0c;都会在电脑中安装各种各样的软件&#xff0c;但是有时候在安装软件的还是&#xff0c;会遇到一些提示&#xff0c;比如“你必须取消阻止该发布者才能运行此软件”&#xff0c;导致安装软件失败&#xff0c;这该怎么办呢&…

perl大骆驼和小骆驼_快速的骆驼和云消息传递

perl大骆驼和小骆驼Apache Camel是一个流行的&#xff0c;成熟的开源集成库。 它实现了企业集成模式 &#xff0c;这是在集成分布式系统时经常出现的一组模式。 过去&#xff0c;我写过很多关于Camel的文章&#xff0c; 包括为什么我比Spring Integration更喜欢它 &#xff0c;…

mvc 事务层切换数据源_Mvc 与 Flux 与 Redux的一些思考

MVC模型 解决问题以及不足解决问题为了解决业务逻辑和界面渲染逻混在一起 MVC流程图2. 不足由于 Model 对外直接暴露了 set 和 on 方法&#xff0c;导致 View 层可以随意改变 Model 中的值&#xff0c;也可以随意监听 Model 中值的变化。这样的设定最终会导致一个庞大的 Model …

计算机语言缺省,揭秘物联网必学语言——C语言与C++的区别

C语言虽说经常和C在一起被大家提起&#xff0c;但可千万不要以为它们是一个东西。有很多人会有这样的疑问&#xff1a;C语言和C有什么区别呢&#xff1f;C是在C语言的基础上发展来的&#xff0c;但是并不是C比C语言高级&#xff0c;两者的编程思想不一样&#xff0c;应用的领域…

C/C 语言printf()

1.调用格式为 printf("<格式化字符串>", <参量表>); 其中格式化字符串包括两部分内容: 一部分是正常字符, 这些字符将按原样输出; 另一部分是格式化规定字符, 以"%"开始, 后跟一个或几个规定字符, 用来确定输出内容格式。参量表是需要输出的一…

公路多孔箱涵设计_【公路常识中篇】公路路基边坡滑坡防护设计和预防

【公路常识中篇】公路路基边坡滑坡防护设计和预防来源&#xff1a;网络 公路路基基本知识&#xff0e;内容包括&#xff1a;公路路床&#xff1b;公路路堤&#xff1b;公路路基压实&#xff1b;影响公路路基稳定因素&#xff1b;公路路堑&#xff1b;公路路基沉陷&#xff1b;公…

按照计算机系统结构分类存储器可分为,存储器分类,存储器的分级结构

描述1、存储器概述存储器是计算机系统中的记忆设备&#xff0c;用来存放程序和数据。构成存储器的存储介质&#xff0c;目前主要采用半导体器件和磁性材料。存储器中最小的存储单位就是一个双稳态半导体电路或一个CMOS晶体管或磁性材料的存储元&#xff0c;它可存储一个二进制代…

spark 流式计算_流式传输大数据:Storm,Spark和Samza

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

嵌入式开发C语言中的uint8_t

在嵌入式开发中的C语言代码中&#xff0c;经常可以看到类似uint8_t、uint16_t、uint32_t、uint64_t这种数据类型&#xff0c;在教材中却从来没见过。实际上这些数据类型都是某种数据类型的别名。比如&#xff0c;在定义函数时用到了uint8_t。右键“uint8_t”&#xff0c;单击“…

多模态语义分析_情感分析、多模态NLP、多语言翻译...这场NLP知识盛宴不可错过!...

AI科技评论按&#xff1a;2020年12月20日&#xff0c;由中国计算机学会自然语言处理专业委员会(CCF-NLP)发起&#xff0c;联合AI研习社及各个知名高校开展的“CCF-NLP走进高校”系列高校NLP研究分享报告会第六期——华中师范大学站&#xff0c;通过线上会议直播的方式进行。本期…

电瓶车续航测试软件,重点看续航 测试2020款蔚来ES8 485KM

时间回到2019年12月28日&#xff0c;一场隆重的“NIO DAY 2019”在深圳举行&#xff0c;2020款ES8便是在那场大秀中首次亮相。5个月之后&#xff0c;2020款蔚来ES8已于4月19日正式开始交付&#xff0c;我们也在交付之日对新车进行了试驾体验。所以今天就不多废话了&#xff0c;…