Fat vs. Skinny Design

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

Кажется, что иерархии типов/классов в ООП могут быть спроектированы двумя крайними способами: либо с полной инкапсуляцией данных в виду; либо с только несколькими интерфейсами, которые делают сырые данные видимыми и позволяют классам работать с ними, разбирать и превращать в более мелкие элементы данных. Возможно, вас удивит, но я предлагаю второй вариант более изящным. Мне кажется, что мы не теряем объектно-ориентированность, а наоборот, получаем больше гибкости, повторного использования, тестируемости и так далее.

Давайте взглянем на это (давайте назовем это «толстым», и я объясню, почему позже):

Для получения имени автора мы делаем:

Визуально этот дизайн может выглядеть так (в UML):

allowmixing Интерфейс Article Интерфейс Head Интерфейс Author Article o-> Head : “head()” Head o-> Author : “author()” Author : String name() Author : String email() Article <– PgArticle Head <– PgHead Author <– PgAuthor PgArticle o-> PgHead PgHead o-> PgAuthor компонент PostgreSQL PgArticle ..> PostgreSQL PgHead ..> PostgreSQL PgAuthor ..> PostgreSQL

Теперь сравним его с альтернативным дизайном (который намного менее толстый по сравнению с предыдущим, я бы даже назвал его стройным):

Здесь, чтобы получить имя автора, мы должны извлечь заголовок в виде String, извлечь автора в виде String, а затем извлечь имя в виде String:

Визуально в UML это выглядит так:

allowmixing Interface Article TxtAuthor *- TxtHead TxtHead *- Article Article : String head() TxtHead : String author() TxtAuthor : String name() TxtAuthor : String email() Article <– SqlArticle component PostgreSQL SqlArticle ..> PostgreSQL

В первом дизайне было три интерфейса, тогда как во втором есть только один интерфейс и два класса. Я называю первый “толстым”, потому что он возвращает интерфейсы, которые уже реализуют функциональность, которую мы ищем, и нам не нужно покрывать их дополнительными декораторами или адаптерами. Его иерархия из трех интерфейсов достаточно богата, чтобы дать нам все, что нам нужно. Вот почему он толстый. Второй, с другой стороны, довольно тощий, здесь есть только один интерфейс, который возвращает нам простые текстовые данные, которые мы должны разобрать сами. Нам нужно его украсить.

Кажется, что тощий дизайн лучше по нескольким причинам:

  • Согласованность. Худой дизайн определенно более согласован, поскольку все, что связано с управлением данными PostgreSQL, остается в одном классе SqlArticle. Напротив, толстый дизайн распределяет функциональность между множеством классов и, благодаря этому, делает всю группу классов более сложной в поддержке.

  • Возможность повторного использования. Класс TxtAuthor безусловно может быть использован в любом другом месте, где требуется разбор информации об авторе, в то время как класс PgAuthor подходит только для одного конкретного случая: получение и разбор данных, связанных с PostgreSQL.

  • Тестирование. Очевидно, что тонкий дизайн значительно проще тестировать, потому что подмена одного интерфейса - это гораздо более простая задача, чем подмена всей иерархии. Чтобы протестировать класс TxtAuthor, мы просто передаем его конструктору фиктивный текст и проверяем, как он работает. Чтобы протестировать класс PgAuthor, нам придется сделать гораздо больше, включая запуск фиктивного экземпляра сервера PostgreSQL.

Все сказанное выше верно как для 1) получения данных из PostgreSQL, так и для 2) манипуляций с данными в PostgreSQL. Конечно, манипуляции могут потребовать множество методов, которые должны существовать в SqlArticle, что может сделать стройный дизайн некрасивым, и станет очевидно, что некоторые из этих методов должны быть перенесены в классы/интерфейсы более низкого уровня. Это только демонстрирует, что не всегда возможно создать стройный дизайн с помощью одного интерфейса, как в приведенном выше примере. Иногда нам просто нужно сделать его более сложным.

Однако, есть одна серьезная проблема, связанная со стройным дизайном: он позволяет необработанным данным выходить из SqlArticle, что противоречит самой идее объектно-ориентированного программирования, как мы знаем. Действительно, если мы позволим TxtHead производить разбор, мы можем потерять некоторый интересный контекст, относящийся к PostgreSQL, доступный только внутри SqlArticle. Мы не хотим, чтобы сложный разбор данных происходил далеко от места их появления. Мы хотим, чтобы все, что связано с данными, происходило там, где они находятся: внутри SqlArticle.

Это обоснованная проблема, но позволить информации, связанной с PostgreSQL (например, настройкам подключения), перемещаться из PgArticle в PgHead, а затем в PgAuthor, является еще более серьезным нарушением принципа инкапсуляции данных.

В реальных ситуациях, конечно, невозможно представить чисто стройные дизайны с одним интерфейсом. Они всегда будут в некоторой степени сложными. Однако мое предложение состоит в том, чтобы попытаться сделать дизайны менее сложными, позволяя пользователям интерфейса наряжать их как им нравится. Это предложение очень близко к тому, что я ранее сказал о “умных” классах, но на этот раз принцип более широкий.

Translated by ChatGPT gpt-3.5-turbo/42 on 2023-11-18 at 05:01

sixnines availability badge   GitHub stars