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

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 isn ' t PHP, я счел оправданным снова спросить его в теге PHP.

Ответ 1

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

Хотя в большинстве случаев вы можете избежать использования констант, реализуя другие шаблоны (стратегию или, возможно, мухи), есть что сказать, потому что вам не понадобится полдюжины других классов для представления концепции. Я думаю, что это сводится к тому, насколько вероятна потребность в других константах. Другими словами, существует ли необходимость в расширении 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 не вводит никакой конкретной связи. С учетом сказанного я бы не вводил интерфейс, чтобы добавить к нему константу.

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

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

Ответ 2

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

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) { ... }
}

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