среда, 17 мая 2017 г.

Слишком много маленьких классов

Почти во всех презентациях, в которых я объясняю свой взгляд на объектно-ориентированное программирование, есть кто-то, кто разделяет такой комментарий: «Если мы последуем вашему совету, у нас будет так много маленьких классов». И мой ответ всегда один и тот же: «Конечно, будем, и это здорово!» Я искренне верю, что даже если вы не можете считать «много классов» добродетелью, вы не можете назвать это недостатком действительно объектно-ориентированного кода. Однако может наступить момент, когда классы становятся проблемой; Давайте посмотрим, когда, как и что с этим делать.


Ранее упоминалось несколько «правил», которые, если они применяются, очевидно, приведут к большому числу классов, в том числе: 
а) все публичные методы должны быть объявлены в интерфейсах; 
б) объекты не должны иметь более четырех атрибутов (раздел 2.1 Элегантных объектов); 
с) статические методы не допускаются; 
d) конструкторы должны быть без кода; 
e) объекты должны содержать менее пяти открытых методов 


Типы в ООП составляют ваш словарный запас, который объясняет мир вокруг вашего кода.

Самой большой проблемой, конечно, является поддержка кода: «Если вместо 50 более длинных классов у нас будет 300 более коротких, то код станет менее читаемым». Это, безусловно так, если вы задумаете их неправильно.

Типы (или классы) в ООП составляют ваш словарный запас, который объясняет мир вокруг вашего кода - мир, в котором живет ваш код. Чем богаче лексика, тем мощнее ваш код. Чем больше типов вы имеете, тем лучше вы сможете понять и объяснить мир.

Если ваш словарный запас достаточно большой, вы скажете что-то вроде:

    Прочитайте книгу, которая находится на столе.

С гораздо меньшей лексикой, такая же фраза будет звучать так:

    Сделай это с тем, что на этой штуке.

Очевидно, что легче читать и понимать первую фразу. То же самое происходит с типами в ООП: чем больше их в вашем распоряжении, тем более выразительным, ярким и читаемым является ваш код.

К сожалению, Java и многие другие языки не разработаны с учетом этой концепции. Пакеты, модули и пространства имен не помогают, и обычно мы получаем имена типа:
AbstractCookieValueMethodArgumentResolver (Spring) 
CombineFileRecordReaderWrapper (Hadoop). 

Мы стараемся как можно больше упаковать семантику в имена классов, чтобы их пользователи не сомневались ни секунды. Затем мы пытаемся поместить как можно больше методов в один класс, чтобы облегчить жизнь пользователям; Они будут использовать подсказки IDE, чтобы найти правильный.

Это совсем не ООП.

Если ваш код объектно-ориентирован, ваши классы должны быть маленькими, их имена должны быть существительными, а имена их методов должны быть только одним словом. Вот что я делаю в своем коде, чтобы это произошло:

Интерфейсы - это существительные. Например, запрос, директива или домен. Исключения отсутствуют. Типы (также известные как интерфейсы в Java) являются ключевой частью моего словаря; Они должны быть существительными.

Классы имеют префикс. Мои классы всегда реализуют интерфейсы. Благодаря этому я могу сказать, что они всегда являются запросами, директивами или доменами. И я всегда хочу, чтобы их пользователи помнили об этом. Префиксы помогают. Например, RqBuffered является буферизованным запросом, RqSimple - это простой запрос, RqLive - это запрос, который представляет «живое» HTTP-соединение, а RqWithHeader - это запрос с дополнительным заголовком.

Альтернативный подход - использовать имя типа как центральную часть имени класса и добавить префикс, который объясняет детали реализации. Например, DyDomain - это домен, который сохраняет свои данные в DynamoDB. Когда вы знаете, для чего предназначен этот префикс Dy, вы можете легко понять, что такое DyUser и DyBase.

В приложении среднего размера или в библиотеке будет 10-15 префиксов, которые вам нужно запомнить, не более. Например, в Takes Framework имеется 24 000 строк кода, 410 файлов Java и 10 префиксов: Bc, Cc, Tk, Rq, Rs, Fb, Fk, Hm, Ps и Xe. Не так сложно запомнить, что они имеют в виду, верно?

Из всех 240 классов самым длинным именем является RqWithDefaultHeader.

Я нахожу такой подход к наименованию классов довольно удобным. Я использовал его в этих проектах с открытым исходным кодом (в GitHub): yegor256 / takes (10 префиксов), yegor256 / jare (5 префиксов), yegor256 / rultor (6 префиксов) и yegor256 / wring (5 префиксов).

Комментариев нет:

Отправить комментарий