设计模式装饰者模式

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

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

总览

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

情况

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

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

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

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

一种可配置类的解决方案

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

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

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

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

继承的解决方案

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

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

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

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

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

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

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

改变

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

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

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

救援人员的装饰模式

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

装饰图案

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

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

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

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

行动中

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

然后,我删除了原始类,并用正确的装饰器组合替换了它们的用途。 最终,我了解了新功能并选择了正确的装饰器。 用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);

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

效果

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

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

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

反射

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

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

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

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

设计模式装饰者模式

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

  1. 工厂设计模式解决什么问题_使用工厂模式解决设计问题

    工厂设计模式解决什么问题 工厂设计模式是面向对象环境中最常用的模式之一. 再次来自"创意设计"模式类别,即有关对象创建的所有信息. 在某些情况下,对象的创建很复杂,可能需要某种程度 ...

  2. 工厂方法模式_工厂方法模式

    工厂方法模式是简单工厂模式的升级版,简单工厂模式不符合设计模式的原则(即:单一职责,开闭原则) 优点: 职责明确,扩展方便 缺点:需要创建多个工厂 实现步骤: 1.将工厂通用方法抽取接口 (例如:IF ...

  3. 工程模式和抽象工厂模式_功能工厂模式

    工程模式和抽象工厂模式 您是否需要一种非常快速的方法来制作Factory对象? 然后,您需要lambda或其他函数传递! 它不仅快速,而且非常简单. 我敢打赌,如果您对Lambdas相当满意,那么您只 ...

  4. 哈斯机床进去debug模式_责任链模式

    原文链接: 责任链模式 | 菜鸟教程​www.runoob.com 顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链.这种模式给予请 ...

  5. python的盈利模式_八大盈利模式是什么?一篇文教会你盈利模式分析!

    当今如果说到创业,永远绕不开一个话题:"模式". 非创业者往往最关注的是"产品"或"服务": 初级创业者往往最关注的的是"行业&q ...

  6. shell开启飞行模式_手机飞行模式有什么用 手机飞行模式介绍【详解】

    手机飞行模式功能一直被大家吐槽为最没有用的手机功能,随着智能手机的快速发展,手机很多功能都已经逐渐消失被替代,唯独飞行模式依旧占据着手机设置里的主要地位. 那么问题来了,手机飞行模式到底有什么用? 1 ...

  7. Hadoop 的三种运行模式_本地模式_伪分布式模式

    演示的版本是:2.7.2 官方文档 Hadoop运行模式 Hadoop运行模式包括:本地模式.伪分布式模式以及完全分布式模式. Hadoop官方网站:http://hadoop.apache.org/ ...

  8. java mediator模式_中介者模式(Mediator)

    中介者模式 一. 中介者模式 1.1 定义 用一个中介者对象封装一系列的对象交互,中介者使各对象不需要显示的相互作用,从而使耦合松散,而且可以独立的改变他们之间的交互. 1.2 角色 抽象中介者类(A ...

  9. 设计模式 -结构型模式_ 装饰者模式Decorator Pattern 在开源软件中的应用

    文章目录 定义 结构图 需求 装饰者模式 装饰者模式在MyBatis中的应用 小结 定义 装饰模式 Decorator : 在不改变原有功能的基础上, 动态的给一个对象添加一些额外的职责 ,非常符合开 ...

最新文章

  1. android:layout_gravity 和 android:gravity 的区别
  2. Lancet:支持奋战在疫情一线的中国科研技术工作者
  3. Markdown中常用的转义字符
  4. ERP实施过程中的误区 你知道吗?
  5. YOLOv4重磅发布,五大改进,二十多项技巧实验,堪称最强目标检测万花筒
  6. 1024“代码急救室”活动来袭!机械键盘、背包等程序员装备等你来!
  7. pmtk3怎样离线安装
  8. android 自定义控件的宽高_巧用Handler获取View控件信息
  9. pyqt5 python2.7_python2.7 安装pyqt5
  10. debian软raid
  11. Atitit 人工智能目前的进展与未来 包含的技术 v2 r99.docx
  12. python抽签代码,python:选房抽签小工具
  13. Cameralink光端机
  14. 【数据挖掘】关联规则和Apriori算法
  15. python数据分析师面试题选
  16. wordpress 更改excerpt的长度,设置excerpt后面'[...]'的字样和链接
  17. P3373(线段树)
  18. PC操作系统使用技巧
  19. 阿里云二级域名解析教程
  20. 会写Python代码的人工智能Kite宣布支持Linux,“程序猿”要失业了?

热门文章

  1. P3911-最小公倍数之和【莫比乌斯反演】
  2. nssl1519-背包签到题【数论】
  3. Codeforces Round #684 (Div. 2)
  4. GDKOI2021总结
  5. 12、oracle数据库下的存储过程和函数
  6. Java自动化邮件中发送图表(四)之javafx Chart
  7. ssm使用全注解实现增删改查案例——EmpServiceImpl
  8. System.err: java.net.UnknownServiceException: CLEARTEXT communication to 192.168.43.172 not permitte
  9. 最新版Intellij IDEA视频教程 20170814
  10. 计算机视觉论文doc,嘉炬-计算机视觉论文资料.doc