Pro e contro delle costanti dell'interfaccia [chiuso]


105

Le interfacce PHP consentono la definizione di costanti in un'interfaccia, ad es

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

Qualsiasi classe di implementazione avrà automaticamente queste costanti disponibili, ad es

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

La mia opinione su questo è che tutto ciò che è globale è malvagio . Ma mi chiedo se lo stesso vale per le costanti di interfaccia. Dato che la codifica rispetto a un'interfaccia è considerata una buona pratica in generale, l'utilizzo delle costanti dell'interfaccia è le uniche costanti accettabili da utilizzare al di fuori del contesto di una classe?

Mentre sono curioso di sentire la tua opinione personale e se usi o meno le costanti dell'interfaccia, cerco principalmente ragioni oggettive nelle tue risposte. Non voglio che questa sia una domanda di tipo sondaggio. Sono interessato all'effetto che l'uso delle costanti dell'interfaccia ha sulla manutenibilità. Accoppiamento. O unit test. Come si relaziona a SOLID PHP? Viola i principi di codifica considerati buone pratiche in PHP? Hai l'idea ...

Nota: c'è una domanda simile per Java che elenca alcuni buoni motivi per cui sono cattive pratiche, ma poiché Java non è PHP, ho ritenuto giustificato chiederlo di nuovo all'interno del tag PHP.


1
Hmm, non avevo mai incontrato prima la necessità di definire le costanti in un'interfaccia. Vale la pena sapere che le classi che implementano l'interfaccia non possono sovrascrivere le costanti, mentre le classi che semplicemente si estendono a vicenda possono sovrascrivere le costanti.
Charles

1
Credo che le costanti non siano male poiché hanno valori prevedibili anche quando si tratta di testabilità unitaria. Le variabili globali sono malvagie poiché chiunque può cambiarle poiché è una variabile e tutto ha uno scopo ma le costanti non cambieranno mai il suo valore, quindi il termine costante.
mdprotacio

Risposte:


135

Bene, penso che si riduca alla differenza tra buono e abbastanza buono .

Mentre nella maggior parte dei casi è possibile evitare l'uso di costanti implementando altri modelli (strategia o forse peso mosca), c'è qualcosa da dire per non aver bisogno di una mezza dozzina di altre classi per rappresentare un concetto. Penso che ciò a cui si riduce è quanto sia probabile che siano necessarie altre costanti. In altre parole, è necessario estendere l'ENUM fornito dalle costanti sull'interfaccia. Se puoi prevedere la necessità di espanderlo, scegli uno schema più formale. In caso contrario, potrebbe essere sufficiente (sarà abbastanza buono e quindi ci sarà meno codice da scrivere e testare). Ecco un esempio di abbastanza buono e cattivo uso:

Male:

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

Abbastanza buono:

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();
}

Ora, il motivo per cui ho scelto questi esempi è semplice. L' Userinterfaccia definisce un'enumerazione dei tipi di utente. È molto probabile che si espanda nel tempo e sarebbe più adatto a un altro modello. Ma HTTPRequest_1_1è un caso d'uso decente, poiché l'enum è definito da RFC2616 e non cambierà per tutta la durata della classe.

In generale, non vedo il problema con le costanti e le costanti di classe come un problema globale . Lo vedo come un problema di dipendenza. È una distinzione stretta, ma definita. Vedo i problemi globali come in variabili globali che non vengono applicate, e come tali creano una dipendenza globale morbida. Ma una classe hardcoded crea una dipendenza forzata e come tale crea una dipendenza globale hard. Quindi entrambe sono dipendenze. Ma considero il globale di gran lunga peggiore poiché non è applicato ... motivo per cui non mi piace raggruppare le dipendenze di classe con le dipendenze globali sotto lo stesso banner ...

Se scrivi MyClass::FOO, sei hard-coded per i dettagli di implementazione di MyClass. Questo crea un hard-coupling, che rende il codice meno flessibile e come tale dovrebbe essere evitato. Tuttavia, esistono interfacce per consentire esattamente questo tipo di accoppiamento. Pertanto MyInterface::FOOnon presenta alcun accoppiamento concreto. Detto questo, non introdurrei un'interfaccia solo per aggiungere una costante ad essa.

Quindi, se stai usando interfacce e sei molto sicuro che tu (o chiunque altro per quella materia) non avrai bisogno di valori aggiuntivi, allora non vedo davvero un grosso problema con le costanti dell'interfaccia ... i progetti non includevano costanti o condizionali o numeri magici o stringhe magiche o qualcosa di hard-coded. Tuttavia, ciò aggiunge ulteriore tempo allo sviluppo, poiché è necessario considerare gli usi. La mia opinione è che la maggior parte delle volte vale assolutamente la pena dedicare del tempo aggiuntivo per costruire un ottimo design solido. Ma ci sono momenti in cui abbastanza buono è davvero accettabile (e ci vuole uno sviluppatore esperto per capire la differenza), e in quei casi va bene.

Di nuovo, questa è solo la mia opinione al riguardo ...


4
Cosa suggeriresti come modello diverso per l'utente in questo caso?
Jacob il

@ Jacob: lo astrarrei via. A seconda delle tue esigenze, probabilmente creerei una classe Access che otterrebbe i suoi dati da una tabella di database. In questo modo aggiungere un nuovo livello è facile come inserire una nuova riga. Un'altra opzione potrebbe essere quella di creare un set di classi ENUM (in cui hai una classe per ogni ruolo di autorizzazione). Quindi è possibile estendere le classi dove necessario per fornire le autorizzazioni appropriate. Ma ci sono anche altri metodi che funzioneranno
ircmaxell

3
Risposta molto solida e ben articolata! +1
Decent Dabbler

1
la classe con costanti pubbliche non dovrebbe avere alcun metodo. Dovrebbe essere solo la struttura dei dati o solo un oggetto, non entrambi.
OZ_

2
@FrederikKrautwald: puoi evitare i condizionali con il polimorfismo (nella maggior parte dei casi): dai un'occhiata a questa risposta e guarda questo discorso sul Clean Code ...
ircmaxell

10

Penso che di solito sia meglio gestire le costanti, specialmente le costanti enumerate, come un tipo separato ("classe") dalla tua interfaccia:

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

oppure, se desideri utilizzare una classe come spazio dei nomi:

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

Non è che stai usando solo costanti, stai usando il concetto di valori enumerati o enumerazioni, che un insieme di valori limitati, sono considerati un tipo specifico, con un utilizzo specifico ("dominio"?)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.