Интерфейсный интерфейс, или три плохих совета для именования

Во все времена самым сложным делом в программировании было наименование, — пишет сайт proglib.io в своем переводе статьи Better naming convention. Подбирать имена для классов за вас мы, конечно, не будем, но кое-что посоветуем.

Выбирать имена - сложная задача

Сколько бессонных ночей мы провели, подбирая идеальное наименование для переменных, функций и файлов – увы, не всегда успешно. Сколько тысяч вариантов перебрали, сколько нервов потратили! А все ради чего? Чтобы наш код выглядел логичным и единообразным.

Чтобы облегчить муки подбора, великие умы придумали соглашения о наименованиях. Но теперь появилась новая проблема – какому соглашению следовать?

Чтобы понять, что действительно хорошо, пойдем от противного и попробуем разобраться, что плохо, вдохновляясь Doctrine Coding Standard и демо-проектом Proophessor Do.

Антипаттерн #1. Тавтология интерфейсов

Начнем с классической тавтологии – добавлении префикса/суффикса Interface к – чему бы вы думали! – названиям интерфейсов.

Тавтология – это структура речи, в которой одна и та же идея выражается с помощью очень похожих морфем, слов или фраз. Иначе говоря, повторяется дважды без значимого обоснования.

В повседневном языке тавтологии обычно считаются недостатком стиля. «Круглый круг», «сухая пустыня», «новые инновации» – все это не самые изящные словосочетания. Та же проблема постоянно встречается при использовании аббревиатур, например, «система GPS» или «номер ISBN».

А теперь прочтите вслух определение этого PHP-класса:

interface TodoRepositoryInterface
{
}

Снова тавтология!

Не все разработчики обращают внимание на подобные вещи. Однако если задуматься, это соглашение об именовании кажется довольно ew rolex datejust rolex calibre 2836 2813 mens m126300 0015 white dial silver tone глупым. Вы ведь не используете суффикс Class для имен ваших классов: TodoClass вместо простого Todo. Чем интерфейсы хуже?

В определении интерфейсов уже присутствует ключевое слово Interface. Нет никакой необходимости добавлять к их именам суффикс Interface или префикс I. Это тавтология, которая не несет никакого дополнительного значения, а только размывает основную цель структуры и нарушает принцип DRY.

UML-диаграммы классов предоставляют достаточные возможности для представления и различения интерфейсов и абстрактных классов. Перед именем первых ставится ключевое слово interface, а вторые выделяются курсивом.

Интерфейсы

Современные IDE также прекрасно отличают интерфейсы:

IDE также прекрасно отличают интерфейсы

Те же самые аргументы можно применить к абстрактным классам и трейтам с соответствующими префиксами/суффиксами Abstract  и Trait. Однако это может быть приемлемым исключением из правила, ведь абстрактные классы не должны быть частью какого-либо публичного интерфейса. Вы также можете использовать альтернативный префикс, например, Base, но не стоит злоупотреблять этим подходом. Практически в любой ситуации можно придумать хорошее имя, просто описав цель и область действия абстрактного класса. К примеру, можно поместить логику, общую для всех PDO-репозиториев в абстрактный класс PdoRepository, а MySqlTodoRepository станет конкретной реализацией.

Еще одна тавтология, распространенная в мире программирования, но, к счастью, почти обошедшая стороной PHP – это практика использования суффикса Impl для конкретных реализаций. Так как обычно все, что не является интерфейсом, и есть конкретная реализация, это создает очень много лишнего шума и даже выглядит абсурдно.

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

Антипаттерн #2. Тавтология доменных классов

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

С сущностями и Value Objects проблем обычно не возникает, ведь мы обычно тщательно выбираем для них хорошие имена: UserEmailAddressTodoTodoText. Но дальше начинаются сложности:

namespace My\Todo\Exception;
final class CannotReopenTodoException extends \Exception
{
}

Суффикс Exception особенно не бросается в глаза, как это было с интерфейсами. Но давайте посмотрим на FQCN \My\Todo\Exception\CannotReopenTodoException.

Снова тавтология, ведь Exception уже встречается в пространстве имен.

Кроме того, формулировка и Founs 6000 puffs контекст использования класса CannotReopenTodoException (throwcatch, имя переменной $ex) однозначно указывают на то, что мы имеем дело с исключением:

try {
    throw CannotReopenTodoException::notDone($todo);
} catch (CannotReopenTodoException $ex) {
}

Очевидно, что суффикс Exception тут лишний.

Еще одна неотъемлемая часть архитектуры – это события, которые часто именуются по такому же принципу. Действительно, в имени FooEvent суффикс необходим, иначе мы не поймем, что речь идет о событии. Однако было бы лучше использовать точное описание действия в прошедшем времени – TodoWasMarkedAsDone.

В этом паттерне большое значение имеет понятность имен. Если название класса достаточно хорошо описывает его смысл, суффикс можно (и нужно) удалить. Плохое имя чаще всего – дефект дизайна. Если вы не можете изменить его, значит, что-то в вашем коде пошло не так. 

Антипаттерн #3. Тавтология гет-методов

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

Геттеры и сеттеры – основная форма взаимодействия со свойствами объекта через публичный интерфейс. Учитывая, что объекты-значения иммутабельны, а сущности поощряют инкапсуляцию, у них не должно быть классических сеттеров. В такой ситуации геттеры берут на себя роль простых методов доступа к свойствам.

final class Todo
{
    public function getId(): TodoId
    {
    }
    public function getDescription(): string
    {
    }
    public function getStatus(): Status
    {
    }
}

И здесь префикс get становится лишним. Он не несет никакой полезной информации – разве что группирует все методы доступа.

Альтернативное «strip naked» соглашение об именах для методов доступа к свойствам – это важный шаг к общему стандарту оформления сущностей и объектов-значений. В PHP 7.4 стали доступны типизированные свойства и, может быть, скоро появятся readonly свойства, так что в ближайшем будущем мы, возможно, сможем писать наши классы вот так:

final class Todo
{
    public TodoId readonly $id;
    public string readonly $description;
    public Status readonly $status;
}

Нет тавтологии!

Множество существующих PHP-проектов страдают от тавтологического синдрома и большая часть из них никогда не будет вылечена. Но в наших силах создать новое поколение здоровых и красивых приложений! 

Шпаргалка на память - как выбирать имена
Шпаргалка на память

[customscript]techrocks_custom_after_post_html[/customscript]

[customscript]techrocks_custom_script[/customscript]

2 комментария к “Интерфейсный интерфейс, или три плохих совета для именования”

  1. Я вот не согласен с последним пунктом про Get.
    Префикс get или set удобны именно для сортировки и определения четкого назначения и в этом не никакой тавтологии.
    id это переменная с которой можно работать? или это переменная, которую вынесли в public для debug и забыли убрать? при изменении id нужны ли дополнительные действия? Ответить на эти вопросы может только сам разработчик класса.
    И вот при работе с чужим классов, начинаешь понимать все удобство GetID и SetID, потому что знаешь, что разработчик этого класса уже обо все позаботился (но это не точно!).

  2. Вообще не согласен. get определенно нужен, хотя бы getWalk(), как понимать этот метод без get? I перед интерфейсом тоже полезен, во первых не всегда реализация для интерфейса подразумевает какую-то особую специфику. IButton — реализация Button. Во вторых из кода ясно интерфейс это или класс. И вобще такое ощущение, что статью писал человек, который о программировании по наслышке знает. Exception так то может использоваться вне контекста try throw. Каково это читать код, где вместо List будет List???

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Прокрутить вверх