MB: Interfaces ohne Inhalt für Logik?

moin,

Interfaces schreiben vor, welche Methoden man zu implementieren hat. Ist es geläufig, ein Interface zu implementiert mit der Absicht eine Merkmal zur Unterscheidung zu erschaffen?

interface PersonInterface {}



interface WorkerInterface extends PersonInterface {}

interface CustomerInterface extends PersonInterface {}

interface EmployerInterface extends PersonInterface {}



class WarehouseCustomer implements CustomerInterface
{
  // code
}

class WarehouseServer implements WorkerInterface
{
  // code
}

class WarehouseOwner implements EmployerInterface
{
  // code
}


function determinePerson ( PersonInterface $person ) : int {
  if ( $person instanceOf CustomerInteface ) {
    return 1;
  } elseif ( $person instanceOf WorkerInteface ) {
    return 2;
  } elseif ( $person instanceOf EmployerInteface ) {
    return 3;
  }
  // no person
  return 0;
}

Meines wissens ist das nicht Sinn der Interfaces. Aber ist das eine Methode herr über die Problestelluhng zu werden?

class lgmb

--
Sprachstörung

akzeptierte Antworten

  1. Hallo MB,

    Interfaces als Marker habe ich auch schon verwendet. Aber eher selten. Sowas tut man meiner Meinung nach, wenn man eine existierende Codebasis hat und es sich nicht erlauben kann, den existierenden Code abzuändern. Was in PHP eher unwahrscheinlich ist.

    Deine determinePerson-Funktion hat aber Smell. Du ermittelst eine Kennzahl basierend darauf, ob ein Objekt bestimmte Subinterfaces "implementiert". Ein solche Switch ruft nach einer Lösung mit Polymorphie.

    Und ich frage mich, wann dein switch-default zuschlägt. Du hast ein PersonInterface und 3 Sub-Interfaces. Du fragst alle 3 ab. Wie kann der default-Fall eintreffen.

    Lösung mit Polymorphie:

    interface PersonInterface {
       function getPersonType();
    }
    
    class WarehouseCustomer implements PersonInterface 
    {
       function getPersonType() { return 1; }
      // code
    }
    
    class WarehouseServer implements PersonInterface 
    {
       function getPersonType() { return 2; }
      // code
    }
    
    class WarehouseOwner implements PersonInterface 
    {
       function getPersonType() { return 3; }
      // code
    }
    
    function determinePerson ( PersonInterface $person ) : int {
       // entfällt
    }
    
    // statt
    $typ = determinePerson($p);
    
    // rufst Du
    $typ = $p->getPersonType();
    

    Bzw. es wäre die Frage zu klären, wofür der Typ gebraucht wird und ob man statt der Typ-Abfrage die Polymorphie nicht noch weiter treiben könnte.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. moin,

      Interfaces als Marker habe ich auch schon verwendet.

      Ok, schön 😀.

      Ein solche Switch ruft nach einer Lösung mit Polymorphie.

      Stimmt! Daran habe ich nicht gedacht. Danke!!!

      aber was ist denn wenn ich verschachtelte Objekte Habe die mehrere merkmale haben???

      function determineEntity ( EntityInterface $entity ) {
        if ( $entity instanceOf PersonInteface ) {
          if ( $entity instanceOf WorkerInteface ) {
            // ...
          }
        }
      }
      

      kann man das auch mit Polymorphie lösen?

      Und ich frage mich, wann dein switch-default zuschlägt. Du hast ein PersonInterface und 3 Sub-Interfaces. Du fragst alle 3 ab. Wie kann der default-Fall eintreffen.

      ist nur ne Deko 😉

      Bzw. es wäre die Frage zu klären, wofür der Typ gebraucht wird und ob man statt der Typ-Abfrage die Polymorphie nicht noch weiter treiben könnte.

      Danke für den Typ. Ware ja wie von mir gewohnt, n Beispiel.

      lgmb

      --
      Sprachstörung
      1. Hallo MB,

        aber was ist denn wenn ich verschachtelte Objekte Habe die mehrere merkmale haben???

        Nein, hast Du nicht. Dein Beispiel ist unzureichend, weil WorkerInterface eine Subklasse von PersonInterface ist, darum ist das nicht wirklich verschachtelt.

        Beantworten kann man deine Frage vermutlich nicht mit einem abstrakten Fall. Da muss man immer konkret schauen. Aber ich versuch's mal.

        Angenommen, du hast

        interface XInterface { }
        interface YInterface { }
        
        class A implements XInterface {...}
        class B implements YInterface {...}
        class C implements XInterface, YInterface {...}
        

        und willst unterschiedliches Verhalten haben, je nach dem, welche Interfaces die Klasse implementiert. Vor allem: XInterface und YInterface stehen in keiner Vererbungsbeziehung zueinander.

        Dann fragt sich vor allem: Wozu? Wenn XInterface und YInterface einander fremd sind, ist die Wahrscheinlichkeit gering, dass es Logiken gibt, die ein XInterface anders behandeln müssen, wenn XInterface auch noch ein YInterface ist.

        Aber wenn doch - dann sollte die Methode, die diese Logik implementiert, in den Klassen implementiert sein. Und in dem Moment hast Du es gelöst, denn du kannst die unterschiedlichen Logiken in A, B und C passend implementieren.

        Wenn es einen ganzen Haufen Klassen gibt, die XInterface, YInterface oder beide implementieren, und die anzuwendende Logik bei allen Klassen jeweils davon abhängt, welche Interfaces vorliegen, nun, dann muss man sich das genau anschauen. Ist die Logik dann wirklich komplett unterschiedlich, oder kann man gruppieren - DAS beantworte ich nicht abstrakt ohne einen konkreten Usecase.

        Rolf

        --
        sumpsi - posui - obstruxi
        1. moin,

          Hallo MB,

          aber was ist denn wenn ich verschachtelte Objekte Habe die mehrere merkmale haben???

          Nein, hast Du nicht. Dein Beispiel ist unzureichend, weil WorkerInterface eine Subklasse von PersonInterface ist, darum ist das nicht wirklich verschachtelt.

          Ok, ich hab vergessen zu erwähnen: Angenommen Interfaces habe eine Vererbungsahierarchie.

          • AInterface
            • A_AInterface
              • A_A_AInterface
              • B_A_AInterface
              • C_A_AInterface
            • B_AInterface
              • A_B_AInterface
              • B_B_AInterface
              • C_B_AInterface

          Wie löst man das Problem??? Das wollte ich mit meinen Code-Beispiel im letzen Beitrag rüberbringen. Fällt das noch unter Abstraktion die du beantworten kannst oder ist das zuuu abstrakt und da muss n UseCase herhalten? Dann wäre es ok, wenn du keinen Senf dazu gibst und ich Melde mich wenns soweit is mit einem UseCase 😉.

          lgmb

          --
          Sprachstörung
          1. Hallo MB,

            ich glaube, das war schon klar. Einen Test wie "ist es Instanz von A_Interface UND Instanz von A_A_Interface" brauchst Du nicht. Da reicht der Test auf A_A_Interface, weil diese zweite Teilbedingung notwendigerweise auch ein TRUE der ersten Teilbedingung beinhaltet.

            Anders ist es, wenn Du den Fall "ist es Instanz von B_A_AInterface UND Instanz von "C_B_Interface" betrachten willst. In dem Fall hast Du ja eine Klasse, die beide Interfaces implementiert, und diese Klasse kann getPersonType mit einem bestimmten Rückgabewert implementieren.

            Wenn getPersonType die richtige Antwort ist. Aber das hängt vom UseCase ab.

            Rolf

            --
            sumpsi - posui - obstruxi
    2. Du hast ein PersonInterface und 3 Sub-Interfaces. Du fragst alle 3 ab. Wie kann der default-Fall eintreffen.

      Indem später ein viertes hinzukommt 😉

      Einen default Fall festzulegen ist meistens eine gute Idee.
      Ebenfalls sinnvoll kann es sein, nicht einfach nur still 0 zurückzugeben, sondern gleich einen unübersehbaren Fehler zu erzeugen. Dann merkt nämlich der Entwickler bei Einführung weiterer Klassen sofort, wo er überall vergessen hat diese neue Klasse mit zu berücksichtigen.

      1. Hallo encoder,

        okay, noch ein Grund mehr für eine abstrakte Superklasse, statt Interfaces, und einer abstrakten Methode getPersonType() in der Superklasse. Wenn man die dann vergisst zu implementieren, rappelt's sofort.

        Rolf

        --
        sumpsi - posui - obstruxi