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

python装饰器

python装饰器

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

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

反射

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

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

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

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

python装饰器

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值