Vor- und Nachteile von Schnittstellenkonstanten [geschlossen]

105

PHP-Schnittstellen ermöglichen die Definition von Konstanten in einer Schnittstelle, z

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

Für jede implementierende Klasse stehen diese Konstanten automatisch zur Verfügung, z

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

Ich gehe davon aus, dass alles, was global ist, böse ist . Aber ich frage mich, ob das auch für Schnittstellenkonstanten gilt. Ist die Verwendung von Schnittstellenkonstanten angesichts der Tatsache, dass das Codieren gegen eine Schnittstelle im Allgemeinen als bewährte Methode angesehen wird, die einzige Konstante, die außerhalb eines Klassenkontexts verwendet werden kann?

Ich bin zwar neugierig auf Ihre persönliche Meinung und darauf, ob Sie Schnittstellenkonstanten verwenden oder nicht, aber ich suche in Ihren Antworten hauptsächlich nach objektiven Gründen. Ich möchte nicht, dass dies eine Frage vom Typ Umfrage ist. Ich bin daran interessiert, welche Auswirkungen die Verwendung von Schnittstellenkonstanten auf die Wartbarkeit hat. Kupplung. Oder Unit Testing. Wie hängt es mit SOLID PHP zusammen? Verstößt es gegen Kodierungsprinzipien, die in PHP als bewährte Verfahren gelten? Du hast die Idee …

Hinweis: Es gibt eine ähnliche Frage für Java , in der einige gute Gründe aufgeführt sind, warum es sich um schlechte Praktiken handelt. Da Java jedoch kein PHP ist, hielt ich es für gerechtfertigt, sie erneut innerhalb des PHP-Tags zu stellen.

Gordon
quelle
1
Hmm, ich hatte noch nie zuvor die Notwendigkeit, Konstanten in einer Schnittstelle zu definieren. Es ist wichtig zu wissen, dass Klassen, die die Schnittstelle implementieren, die Konstanten nicht überschreiben können, während Klassen, die sich lediglich gegenseitig erweitern, Konstanten überschreiben können .
Charles
1
Ich glaube, Konstanten sind nicht schlecht, da sie vorhersehbare Werte haben, selbst wenn es um die Testbarkeit von Einheiten geht. Die globalen Variablen sind böse, da jeder sie ändern kann, da es sich um eine Variable handelt und alles einen Gültigkeitsbereich hat, aber Konstanten werden ihren Wert niemals ändern, daher der Begriff Konstante.
Mdprotacio

Antworten:

135

Nun, ich denke, dass es auf den Unterschied zwischen gut und gut genug hinausläuft .

Während Sie in den meisten Fällen die Verwendung von Konstanten vermeiden können, indem Sie andere Muster (Strategie oder möglicherweise Fliegengewicht) implementieren, gibt es etwas zu sagen, wenn Sie nicht ein halbes Dutzend anderer Klassen benötigen, um ein Konzept darzustellen. Ich denke, es läuft darauf hinaus, wie wahrscheinlich es ist, dass andere Konstanten benötigt werden. Mit anderen Worten, muss die durch die Konstanten auf der Schnittstelle bereitgestellte ENUM erweitert werden. Wenn Sie vorhersehen können, dass es erweitert werden muss, wählen Sie ein formelleres Muster. Wenn nicht, kann es ausreichen (es ist gut genug und daher weniger Code zum Schreiben und Testen). Hier ist ein Beispiel für eine gute und eine schlechte Verwendung:

Schlecht:

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

Gut genug:

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

Der Grund, warum ich diese Beispiele gewählt habe, ist einfach. Die UserSchnittstelle definiert eine Aufzählung von Benutzertypen. Dies wird sich mit der Zeit sehr wahrscheinlich ausdehnen und wäre durch ein anderes Muster besser geeignet. Dies HTTPRequest_1_1ist jedoch ein anständiger Anwendungsfall, da die Aufzählung durch RFC2616 definiert wird und sich während der Lebensdauer der Klasse nicht ändert.

Im Allgemeinen sehe ich das Problem mit Konstanten und Klassenkonstanten nicht als globales Problem. Ich sehe es als Abhängigkeitsproblem. Es ist eine enge Unterscheidung, aber eine bestimmte. Ich sehe globale Probleme als globale Variablen, die nicht erzwungen werden und als solche eine weiche globale Abhängigkeit erzeugen. Eine fest codierte Klasse erzeugt jedoch eine erzwungene Abhängigkeit und als solche eine harte globale Abhängigkeit. Beides sind also Abhängigkeiten. Aber ich halte das Globale für weitaus schlimmer, da es nicht erzwungen wird ... Deshalb möchte ich Klassenabhängigkeiten nicht gerne mit globalen Abhängigkeiten unter demselben Banner zusammenfassen ...

Wenn Sie schreiben MyClass::FOO, sind Sie fest mit den Implementierungsdetails von codiert MyClass. Dies führt zu einer harten Kopplung, die Ihren Code weniger flexibel macht und daher vermieden werden sollte. Es gibt jedoch Schnittstellen, um genau diese Art der Kopplung zu ermöglichen. Führt MyInterface::FOOdaher keine konkrete Kupplung ein. Vor diesem Hintergrund würde ich keine Schnittstelle einführen, nur um eine Konstante hinzuzufügen.

Wenn Sie also Schnittstellen verwenden und sehr sicher sind, dass Sie (oder sonst jemand) keine zusätzlichen Werte benötigen, sehe ich kein großes Problem mit den Schnittstellenkonstanten ... Das Beste Entwürfe würden keine Konstanten oder Bedingungen oder magische Zahlen oder magische Zeichenketten oder fest codierte Elemente enthalten. Dies verlängert jedoch die Entwicklungszeit zusätzlich, da Sie die Verwendungszwecke berücksichtigen müssen. Meiner Ansicht nach lohnt es sich meistens, sich die zusätzliche Zeit zu nehmen, um ein großartiges, solides Design zu erstellen. Aber es gibt Zeiten, in denen gut genug wirklich akzeptabel ist (und es braucht einen erfahrenen Entwickler, um den Unterschied zu verstehen), und in diesen Fällen ist es in Ordnung.

Auch das ist nur meine Ansicht dazu ...

ircmaxell
quelle
4
Was würden Sie in diesem Fall als anderes Muster für den Benutzer vorschlagen?
Jacob
@ Jacob: Ich würde es weg abstrahieren. Abhängig von Ihren Anforderungen würde ich wahrscheinlich eine Access-Klasse erstellen, deren Daten aus einer Datenbanktabelle stammen. Auf diese Weise ist das Hinzufügen einer neuen Ebene so einfach wie das Einfügen einer neuen Zeile. Eine andere Option wäre das Erstellen eines ENUM-Klassensatzes (wobei Sie für jede Berechtigungsrolle eine Klasse haben). Anschließend können Sie die Klassen bei Bedarf erweitern, um die entsprechenden Berechtigungen bereitzustellen. Aber es gibt auch andere Methoden, die funktionieren werden
ircmaxell
3
Sehr solide und gut artikulierte Antwort! +1
Decent Dabbler
1
Klasse mit öffentlichen Konstanten sollte keine Methoden haben. Es sollte nur eine Datenstruktur oder nur ein Objekt sein - nicht beides.
OZ_
2
@FrederikKrautwald: Sie können Bedingungen mit Polymorphismus vermeiden (in den meisten Fällen): Überprüfen Sie diese Antwort und sehen Sie sich diesen Clean Code Talk an ...
ircmaxell
10

Ich denke, dass es normalerweise besser ist, Konstanten, speziell aufgezählte Konstanten, als separaten Typ ("Klasse") von Ihrer Schnittstelle zu behandeln:

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

oder, wenn Sie eine Klasse als Namespace verwenden möchten:

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

Es ist nicht so, dass Sie nur Konstanten verwenden, Sie verwenden das Konzept von Aufzählungswerten oder Aufzählungen, bei denen eine Reihe von eingeschränkten Werten als ein bestimmter Typ mit einer bestimmten Verwendung ("Domäne"?) Betrachtet wird.

umlcat
quelle