Плюсы и минусы интерфейсных констант

Интерфейсы PHP позволяют определять константы в интерфейсе, например

interface FooBar
{
    const FOO = 1;
    const BAR = 2;
}
echo FooBar::FOO; // 1

Любой реализующий класс будет автоматически иметь эти константы доступными, например

class MyFooBar implement FooBar
{
}
echo MyFooBar::FOO; // 1

Мое собственное мнение состоит в том, что все, что является Глобальным, является Злом Но мне интересно, относится ли то же самое к интерфейсным константам. Учитывая, что кодирование на основе интерфейса в целом считается хорошей практикой, является ли использование констант интерфейса единственными константами, которые допустимо использовать вне контекста класса?

Хотя мне любопытно услышать ваше личное мнение и то, используете ли вы константы интерфейса или нет, я в основном ищу объективные причины в ваших ответах. Я не хочу, чтобы это был вопрос типа опроса. Мне интересно, как использование констант интерфейса влияет на ремонтопригодность. Связь. Или модульное тестирование. Как это относится к SOLID PHP? Нарушает ли это какие-либо принципы кодирования, которые считаются передовой практикой в ​​PHP? Вы поняли идею...

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

2 ответа

Решение

Ну, я думаю, что это сводится к разнице между хорошим и достаточно хорошим.

Хотя в большинстве случаев вы можете избежать использования констант путем реализации других шаблонов (стратегии или, возможно, наименьшего веса), есть кое-что, что можно сказать, что не нужно полдюжины других классов для представления концепции. Я думаю, что это сводится к тому, насколько вероятно, что есть потребность в других константах. Другими словами, есть ли необходимость расширять ENUM, предоставляемый константами интерфейса. Если вы можете предвидеть необходимость его расширения, переходите к более формальной схеме. Если нет, то этого может быть достаточно (это будет достаточно хорошо и, следовательно, будет меньше кода для написания и тестирования). Вот пример достаточно хорошего и плохого использования:

Плохой:

interface User {
    const TYPE_ADMINISTRATOR = 1;
    const TYPE_USER          = 2;
    const TYPE_GUEST         = 3;
}

Достаточно хорошо:

interface HTTPRequest_1_1 {
    const TYPE_CONNECT = 'connect';
    const TYPE_DELETE  = 'delete';
    const TYPE_GET     = 'get';
    const TYPE_HEAD    = 'head';
    const TYPE_OPTIONS = 'options';
    const TYPE_POST    = 'post';
    const TYPE_PUT     = 'put';

    public function getType();
}

Причина, по которой я выбрал эти примеры, проста. User Интерфейс определяет перечисление типов пользователей. Это очень вероятно, будет расширяться со временем и будет лучше соответствовать другой модели. Но HTTPRequest_1_1 это достойный вариант использования, так как перечисление определено RFC2616 и не изменится в течение срока жизни класса.

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

Если ты пишешь MyClass::FOOВы жестко запрограммированы на детали реализации MyClass, Это создает жесткую связь, которая делает ваш код менее гибким, и поэтому его следует избегать. Однако существуют интерфейсы, разрешающие именно этот тип связи. Следовательно MyInterface::FOO не вводит никакой конкретной связи. С учетом сказанного, я бы не стал вводить интерфейс, просто чтобы добавить к нему константу.

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

Опять же, это только мой взгляд на это...

Я думаю, что обычно лучше обрабатывать константы, особенно перечисляемые константы, как отдельный тип ("класс") из вашего интерфейса:

define(TYPE_CONNECT, 'connect');
define(TYPE_DELETE , 'delete');
define(TYPE_GET    , 'get');
define(TYPE_HEAD   , 'head');
define(TYPE_OPTIONS, 'options');
define(TYPE_POST   , 'post');
define(TYPE_PUT    , 'put');

interface IFoo
{
  function /* int */ readSomething();
  function /* void */ ExecuteSomething(/* int */ param);
}

class CBar implements IFoo
{
  function /* int */ readSomething() { ...}
  function /* void */ ExecuteSomething(/* int */ param) { ... }
}

или, если вы хотите использовать класс в качестве пространства имен:

class TypeHTTP_Enums
{
  const TYPE_CONNECT = 'connect';
  const TYPE_DELETE  = 'delete';
  const TYPE_GET     = 'get';
  const TYPE_HEAD    = 'head';
  const TYPE_OPTIONS = 'options';
  const TYPE_POST    = 'post';
  const TYPE_PUT     = 'put';
}

interface IFoo
{
  function /* int */ readSomething();
  function /* void */ ExecuteSomething(/* int */ param);
}

class CBar implements IFoo
{
  function /* int */ readSomething() { ...}
  function /* void */ ExecuteSomething(/* int */ param) { ... }
}

Дело не в том, что вы используете только константы, вы используете концепцию перечислимых значений или перечислений, которые представляют собой набор ограниченных значений, которые считаются определенным типом с определенным использованием ("домен"?)

Другие вопросы по тегам