How to Handle the Problem of Too Many Classes

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

sixnines availability badge   GitHub stars