Avantages et inconvénients des constantes d'interface [fermé]

105

Les interfaces PHP permettent la définition de constantes dans une interface, par exemple

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

Toute classe d'implémentation aura automatiquement ces constantes disponibles, par exemple

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

Ma propre opinion à ce sujet est que tout ce qui est mondial est mauvais . Mais je me demande si la même chose s'applique aux constantes d'interface. Étant donné que le codage par rapport à une interface est considéré comme une bonne pratique en général, l'utilisation des constantes d'interface est-elle les seules constantes acceptables à utiliser en dehors d'un contexte de classe?

Bien que je sois curieux de connaître votre opinion personnelle et de savoir si vous utilisez ou non des constantes d'interface, je recherche principalement des raisons objectives dans vos réponses. Je ne veux pas que ce soit une question de type sondage. Je suis intéressé par l'effet de l'utilisation des constantes d'interface sur la maintenabilité. Couplage. Ou des tests unitaires. Quel est le lien avec SOLID PHP? Viole-t-il des principes de codage considérés comme des bonnes pratiques en PHP? Vous avez eu l'idée …

Remarque: il y a une question similaire pour Java qui énumère de très bonnes raisons pour lesquelles elles sont de mauvaises pratiques, mais comme Java n'est pas PHP, j'ai estimé qu'il était justifié de la poser à nouveau dans la balise PHP.

Gordon
la source
1
Hmm, je n'avais jamais rencontré le besoin de définir des constantes dans une interface auparavant. Cela vaut la peine de savoir que les classes implémentant l'interface ne peuvent pas remplacer les constantes, tandis que les classes qui s'étendent simplement les unes les autres peuvent remplacer les constantes.
Charles
1
Je pense que les constantes ne sont pas mauvaises car elles ont des valeurs prévisibles même lorsque nous sommes concernés par la testabilité unitaire. Les variables globales sont mauvaises puisque n'importe qui peut la changer car c'est une variable et tout a une portée dessus mais les constantes ne changeront jamais sa valeur donc le terme constant.
mdprotacio

Réponses:

135

Eh bien, je pense que cela se résume à la différence entre bien et assez bien .

Alors que dans la plupart des cas, vous pouvez éviter l'utilisation de constantes en implémentant d'autres modèles (stratégie ou peut-être poids mouche), il y a quelque chose à dire pour ne pas avoir besoin d'une demi-douzaine d'autres classes pour représenter un concept. Je pense que cela se résume à la probabilité que d'autres constantes soient nécessaires. En d'autres termes, est-il nécessaire d'étendre l'ENUM fourni par les constantes sur l'interface. Si vous pouvez prévoir le besoin de l'étendre, optez pour un modèle plus formel. Sinon, cela peut suffire (ce sera assez bon, et donc moins de code à écrire et à tester). Voici un exemple d'utilisation assez bonne et mauvaise:

Mauvais:

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

Assez bien:

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

Maintenant, la raison pour laquelle j'ai choisi ces exemples est simple. L' Userinterface définit une énumération de types d'utilisateurs. Ceci est très susceptible de s'étendre avec le temps et serait mieux adapté par un autre modèle. Mais HTTPRequest_1_1c'est un cas d'utilisation décent, puisque l'énumération est définie par RFC2616 et ne changera pas pendant la durée de vie de la classe.

En général, je ne vois pas le problème des constantes et des constantes de classe comme un problème global . Je le vois comme un problème de dépendance. C'est une distinction étroite, mais définitive. Je vois les problèmes globaux comme des variables globales qui ne sont pas appliquées et, en tant que telles, créent une dépendance globale douce. Mais une classe codée en dur crée une dépendance imposée et, en tant que telle, crée une dépendance globale dure. Les deux sont donc des dépendances. Mais je considère que le global est bien pire car il n'est pas appliqué ... C'est pourquoi je n'aime pas regrouper les dépendances de classe avec des dépendances globales sous la même bannière ...

Si vous écrivez MyClass::FOO, vous êtes codé en dur pour les détails d'implémentation de MyClass. Cela crée un couplage dur, ce qui rend votre code moins flexible et doit donc être évité. Cependant, des interfaces existent pour permettre exactement ce type de couplage. N'introduit MyInterface::FOOdonc aucun couplage en béton. Cela dit, je n'introduirais pas une interface simplement pour y ajouter une constante.

Donc, si vous utilisez des interfaces et que vous êtes très sûr que vous (ou n'importe qui d'autre d'ailleurs) n'aurez pas besoin de valeurs supplémentaires, alors je ne vois pas vraiment de gros problème avec les constantes d'interface ... Le meilleur les conceptions n'incluraient pas de constantes ou de conditions ou de nombres magiques ou de chaînes magiques ou quoi que ce soit codé en dur. Cependant, cela ajoute du temps supplémentaire au développement, car vous devez tenir compte des utilisations. À mon avis, la plupart du temps, il vaut vraiment la peine de prendre le temps supplémentaire de créer un excellent design solide. Mais il y a des moments où assez bien est vraiment acceptable (et il faut un développeur expérimenté pour comprendre la différence), et dans ces cas, tout va bien.

Encore une fois, c'est juste mon point de vue là-dessus ...

ircmaxell
la source
4
Que suggéreriez-vous comme modèle différent pour l'utilisateur dans ce cas?
Jacob
@Jacob: Je voudrais l'abstraire. En fonction de vos besoins, je créerais probablement une classe Access qui obtiendrait ses données à partir d'une table de base de données. De cette façon, ajouter un nouveau niveau est aussi simple que d'insérer une nouvelle ligne. Une autre option serait de créer un ensemble de classes ENUM (où vous avez une classe pour chaque rôle d'autorisation). Ensuite, vous pouvez étendre les classes si nécessaire pour fournir les autorisations appropriées. Mais il existe d'autres méthodes qui fonctionneront aussi
ircmaxell
3
Réponse très solide et bien articulée! +1
Decent Dabbler
1
La classe avec des constantes publiques ne devrait avoir aucune méthode. Il ne doit s'agir que d'une structure de données ou d'un objet - pas des deux.
OZ_
2
@FrederikKrautwald: vous pouvez éviter les conditions avec polymorphisme (dans la plupart des cas): Vérifiez cette réponse et regardez cette discussion sur le Clean Code ...
ircmaxell
10

Je pense qu'il est généralement préférable de gérer les constantes, des constantes spécialement énumérées, comme un type distinct ("classe") de votre interface:

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

ou, si vous souhaitez utiliser une classe comme espace de noms:

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

Ce n'est pas que vous n'utilisez que des constantes, vous utilisez le concept de valeurs énumérées ou d'énumérations, dont un ensemble de valeurs restreintes est considéré comme un type spécifique, avec un usage spécifique ("domaine"?)

umlcat
la source