The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:
Во время почти каждой презентации, в которой я объясняю свое представление об объектно-ориентированном программировании, есть кто-то, кто делится комментарием вроде: “Если мы будем следовать вашему совету, у нас будет так много маленьких классов”. И мой ответ всегда одинаковый: “Конечно, будет, и это здорово!” Я искренне верю, что даже если вы не считаете “большое количество классов” достоинством, вы также не можете назвать это недостатком по-настоящему объектно-ориентированного кода. Однако может наступить момент, когда классы станут проблемой; посмотрим, когда, как и что делать в таком случае.
Ранее упоминались несколько “правил”, применение которых, очевидно, приведет к большому количеству классов, включая: а) все публичные методы должны быть объявлены в интерфейсах; б) объекты не должны иметь более четырех атрибутов (раздел 2.1 Elegant Objects); в) статические методы не допускаются; г) конструкторы должны быть без кода; д) объекты должны предоставлять менее пяти публичных методов (раздел 3.1 Elegant Objects).
Самая большая проблема, конечно, - это поддерживаемость: “Если вместо 50 более длинных классов у нас было бы 300 более коротких, то код был бы гораздо менее читабельным”. Это безусловно произойдет, если вы неправильно разработаете их.
Типы (или классы) в ООП составляют вашу лексику, которая объясняет мир вокруг вашего кода - мир, в котором живет ваш код. Чем богаче ваша лексика, тем мощнее ваш код. Чем больше типов у вас есть, тем лучше вы можете понять и объяснить мир.
Если ваша лексика достаточно большая, вы будете говорить что-то вроде:
С гораздо меньшим словарным запасом, та же фраза звучала бы так:
Очевидно, первую фразу легче читать и понимать. То же самое происходит с типами в ООП: чем больше их у вас есть в распоряжении, тем более выразительным, ярким и читабельным становится ваш код.
К сожалению, Java и многие другие языки не разработаны с этой концепцией в виду. Пакеты, модули и пространства имен на самом деле не помогают, и мы обычно получаем имена вроде AbstractCooKyivalueMethodArgumentResolver
(Spring) или CombineFileRecordReaderWrapper
(Hadoop). Мы пытаемся запихнуть в имена классов как можно больше семантики, чтобы их пользователи не сомневались ни на секунду. Затем мы стараемся поместить в один класс как можно больше методов, чтобы облегчить жизнь пользователям; они будут использовать подсказки своей среды разработки, чтобы найти нужный.
Это все, кроме ООП.
Если ваш код ориентирован на объекты, ваши классы должны быть маленькими, их имена должны быть существительными, и имена их методов должны состоять всего из одного слова. Вот что я делаю в своем коде, чтобы это произошло:
Интерфейсы являются существительными. Например, Request
, Directive
или Domain
. Нет исключений. Типы (также известные как интерфейсы в 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 префиксов).
Translated by ChatGPT gpt-3.5-turbo/42 on 2023-11-22 at 09:55