Плюси і мінуси констант інтерфейсу [закрито]


105

Інтерфейси 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.


1
Хм, я ніколи раніше не стикався з необхідністю визначати константи в інтерфейсі. Варто знати, що класи, що реалізують інтерфейс, не можуть змінювати константи, тоді як класи, що лише розширюють один одного, можуть змінювати константи.
Чарльз

1
Я вважаю, що константи непогані, оскільки вони мають передбачувані значення навіть тоді, коли ми переймаємося одиничною перевіряючістю. Глобальні змінні є злими, оскільки його може змінити кожен, оскільки він є змінною, і все має на ній сферу застосування, але константи ніколи не змінять свого значення, таким чином, термін "константа".
mdprotacio

Відповіді:


135

Ну, я вважаю, що це зводиться до різниці між добром і досить добрим .

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

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

Знову ж таки, це лише мій погляд на це ...


4
Що б ви запропонували як інший зразок для Користувача в цьому випадку?
Яків

@Jacob: Я б відмовив від цього. Залежно від ваших потреб, я, швидше за все, будую клас Access, який отримає його дані з таблиці баз даних. Таким чином додати новий рівень так само просто, як і вставити новий рядок. Іншим варіантом буде скласти набір класів ENUM (де у вас є один клас для кожної ролі дозволу). Тоді ви можете розширити класи, де потрібно, щоб надати відповідні дозволи. Але є й інші методи, які також будуть працювати
ircmaxell

3
Дуже тверда і добре сформульована відповідь! +1
гідний даблер

1
клас з громадськими константами не повинен мати жодних методів. Це має бути лише структура даних або лише об'єкт - не те й інше.
OZ_

2
@FrederikKrautwald: ви можете уникнути умов з поліморфізмом (у більшості випадків): Перевірте цю відповідь , а також дивіться цю розмову в чистому коді ...
ircmaxell

10

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

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

Справа не в тому, що ви використовуєте лише константи, ви використовуєте концепцію перелічених значень чи перерахувань, які набір обмежених значень вважаються певним типом із конкретним використанням ("домен"?)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.