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, не выполняя ничего на самом деле. Пока не вызван метод read(), файл не будет изменен, и обработка текста не начнется. Объект text является просто составным объектом декораторов, а не исполняемой процедурой. Посмотрите эту статью о декларативном и императивном стилях программирования: Утилитарные классы не имеют ничего общего с функциональным программированием.

Этот дизайн намного более гибкий и повторно используемый, чем более традиционный, где объект Text достаточно умный, чтобы выполнять все указанные операции. Например, класс String в Java является хорошим примером плохого дизайна. Он имеет более 20 утилитарных методов, которые вместо этого должны были быть предоставлены в виде декораторов: trim(), toUpperCase(), substring(), split() и многие другие, например. Когда я хочу обрезать строку, привести ее к верхнему регистру и затем разделить на части, мой код будет выглядеть так:

Это императивное и процедурное программирование. С другой стороны, композируемые декораторы сделали бы этот код объектно-ориентированным и декларативным. Было бы здорово иметь что-то подобное в Java (псевдокод):

В заключение, я рекомендую вам хорошенько подумать каждый раз, когда вы добавляете новый вспомогательный метод в интерфейс/класс. Старайтесь избегать использования вспомогательных методов насколько это возможно и вместо этого используйте декораторы. Идеальный интерфейс должен содержать только методы, которые вы абсолютно не можете удалить. Все остальное должно быть реализовано с помощью комбинируемых декораторов.

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

sixnines availability badge   GitHub stars