Статьи

Жир против Тощий Дизайн

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

Владение Mahowny (2003) Ричардом Квитниовски

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

01
02
03
04
05
06
07
08
09
10
11
interface Article {
  Head head();
}
interface Head {
  Author author();
  String title();
}
interface Author {
  String name();
  String email();
}

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

1
2
3
4
// It is stored in PostgreSQL (that's why the Pg
// prefix) and retrieves everything using SQL
Article a = new PgArticle();
String name = a.head().author().name();

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

Невозможно отобразить диаграмму PlantUML.

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

01
02
03
04
05
06
07
08
09
10
11
12
13
interface Article {
  String head();
}
class TxtHead {
  private final Article article;
  String author();
  String title();
}
class TxtAuthor {
  private final Head head;
  String name();
  String email();
}

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

1
2
3
4
Article a = new PgArticle();
String head = a.head();
String author = new TxtHead(head).author();
String name = new TxtAuthor(author).name();

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

Невозможно отобразить диаграмму PlantUML.

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

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

  • Расширяемость Тощий дизайн определенно легче расширить. Чтобы извлечь новую информацию от автора, нам просто нужно добавить новый метод в класс TxtAuthor . Нам не нужно переделывать всю иерархию интерфейсов и модифицировать все их реализации. Мы имеем дело с чистыми данными, которые обрабатываются и анализируются позже, в декораторах, адаптерах и других дополнительных интеллектуальных классах.
  • Сплоченность Тощий дизайн определенно более сплоченный, поскольку все, что связано с управлением данными PostgreSQL, остается в одном классе SqlArticle . Напротив, толстый дизайн распространяет функциональность среди многих классов и, благодаря этому, делает весь набор классов более сложным в обслуживании.
  • Возможность повторного использования Класс TxtAuthor определенно может использоваться в любом другом месте, где требуется анализ информации об авторе, а класс PgAuthor подходит только для одного конкретного случая: выборки и анализа данных, связанных с PostgreSQL.
  • Тестируемость Очевидно, что тощий дизайн гораздо проще тестировать, потому что макетирование одного интерфейса — намного более простая задача, чем насмешка всей иерархии. Чтобы протестировать класс TxtAuthor мы просто передаем некоторый фальшивый текст его конструктору и проверим, как он работает. Чтобы протестировать класс PgAuthor нам нужно было бы сделать гораздо больше, включая запуск поддельного экземпляра сервера PostgreSQL.

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

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

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

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

Опубликовано на Java Code Geeks с разрешения Егора Бугаенко, партнера нашей программы JCG . Смотреть оригинальную статью здесь: Fat vs. Skinny Design

Мнения, высказанные участниками Java Code Geeks, являются их собственными.