Composable Decorators vs. Imperative Utility Methods

The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:

装饰器模式是我所了解的所有模式中我最喜欢的一种。它是一种非常简单但非常强大的机制,可以使您的代码具有高内聚性和松散耦合性。然而,我认为装饰器并没有被足够经常地使用。它们应该无处不在,但事实并非如此。我们从装饰器中获得的最大优势是使我们的代码具有可组合性。这就是为什么本文的标题是可组合的装饰器。不幸的是,我们经常使用命令式的实用方法,这使得我们的代码更偏向过程化而不是面向对象化。

首先,一个实际的例子。这是一个用于读取文本并返回文本的对象的接口:

这里是一个从文件中读取文本的实现。

现在让我们来看看修饰器,它是Text的另一种实现,可以将文本中的所有不可打印字符删除。

这是我如何使用它的方式:

如您所见,PrintableText 并不会从文件中读取文本。它并不关心文本的来源。它将文本读取的任务委托给封装的 Text 实例。关于这个封装对象会如何处理文本以及从哪里获取文本,PrintableText 并不关心。

让我们继续并尝试创建一个 Text 的实现,它会将文本中的所有字母都转为大写:

关于一个修剪输入的 Text 如何?

我可以继续使用这些装饰器。我可以创建许多适用于各自独立用例的装饰器。但是让我们看看它们如何协同工作。假设我想从文件中读取文本,将其大写,修剪并删除所有不可打印字符。而且我想要是声明性的。我要做的是:

首先,我创建了一个Text的实例,将多个修饰器组合成一个单一的对象。我通过声明性地定义text的行为,而不是实际执行任何操作。在调用read()方法之前,文件不会被修改,文本的处理也不会开始。text对象只是一组修饰器的组合,而不是可执行的过程。请参阅关于声明性和命令式编程风格的文章:Utility Classes Have Nothing to Do With Functional Programming.

这种设计比传统设计更加灵活和可重用,传统设计中Text对象足够聪明,可以执行所有的操作。例如,Java的String类就是一个糟糕设计的典型例子。它有超过20个实用方法,本应该作为修饰器提供的功能,例如trim()toUpperCase()substring()split()等等。当我想要修剪字符串,将其转换为大写,并将其拆分成多个部分时,我的代码将如下所示:

这是命令式和过程式编程。然而,可组合的装饰器会使这段代码变成面向对象和声明式的。在Java中有类似这样的东西会很好(伪代码):

总之,我建议您在每次向接口/类添加新的实用方法时三思而后行。尽量避免使用实用方法,而是使用装饰器。理想的接口应仅包含绝对不能删除的方法。其他所有操作都应通过可组合的装饰器完成。

Translated by ChatGPT gpt-3.5-turbo/42 on 2023-11-17 at 16:41

sixnines availability badge   GitHub stars