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

Взгляните на это (давайте назовем это жирным, и я объясню почему позже):
|
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 SQLArticle 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, являются их собственными. |