Плюсы и минусы интерфейсных констант
Интерфейсы 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) { ... }
}
Дело не в том, что вы используете только константы, вы используете концепцию перечислимых значений или перечислений, которые представляют собой набор ограниченных значений, которые считаются определенным типом с определенным использованием ("домен"?)