OOP: Assoziation zwischen zwei Klassen

Einklappen
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • OOP: Assoziation zwischen zwei Klassen

    Hallo,

    ich habe mal wieder eine Verständnisfrage zu OOP [FONT=Wingdings][FONT=Wingdings][/FONT][/FONT]

    Diesmal geht es um die konkrete Implementierung einer Assoziation zwischen zwei Klassen Kunde und Kundenliste.

    Kundenliste -> Kunde (Kundenliste enthält Kunde)


    Mal angenommen ich habe eine Klasse Kunde:

    PHP-Code:
    class Kunde {
          protected 
    $kundennummer null;
          protected 
    $name null;
          protected 
    $alter null;
          protected 
    $gesundheit null;
          
          public function 
    __construct($kundennummer) {
              
    // Kundendaten aus DB Laden und Eigenschaften setzen
              // SELECT .... FROM Kunde WHERE id=$kundenummer
              
              
    $this->kundenummer $db_row["kundenummer"];
              
    $this->name $db_row["name"];
              
    // usw...
          
    }
          
          public function 
    getLebenserwartung() {
              
    //Algorithms zur Berechnung der Lebenserwartung
              // Lebenserwartung wird nicht in der DB gespeichert (dient nur der Veranschaulichung)
              
    return 100-($this->alter*$this->gesundheit);
          }
      } 
    (die Beispiele sind bewusst sehr einfach gehalten und dienen wirklich nur der Veranschaulichung des Problems)

    Nun kann ich bequem ein neues Kundenobjekt erzeugen und mir z.B. die Lebenserwartung anzeigen lassen:

    PHP-Code:
    $ulli = new Kunde(10);
      echo 
    $ulli->getLebenserwartung(); 
    Jetzt möchte ich noch eine weitere Klasse erstellen mit der sich bestimmte Daten mehrerer Kunden in einer Tabelle/Liste darstellen lassen.
    Lösung 1

    PHP-Code:
    class Kundenliste {
          private 
    $sqlResult null;
      
          
    punblic function __construct($kdnr_von,$kdnr_bis) {
              
    //select-abfrage auf die Kundendaten z.B;
              // SELECT * FROM Kunde WHERE id>=$kdnr_von && id<=$kdnr_bis
              
    $this->sqlResult $ergebnisskennung_der_abfrage;
          }
      
          public function 
    display() {
              foreach(
    $this->sqlResult as $row) {
                  
    $kunde = new Kunde($row["kundenummer"]);
                  echo 
    $kunde->name.", Lebenserwartung: ".$kunde->getLebenserwartung();
              }
          }
      } 
    Bei dieser Lösung wird in der Methode display() jedes mal ein neues Kundenobjekt erzeugt.

    Vorteil:
    ich arbeite mit einem Kundenobjekt und kann direkt auf seine Eigenschaften und Methoden zugreifen. Das ist sehr bequem und ich muss mich um die Implementierung der Kunden-Methoden nicht kümmern

    Nachteil:
    Jedes mal wenn ich ein neues Kundenobjekt erzeuge greife ich auf die DB zu um seine Daten zu holen. Das ist in diesem Fall sehr ineffizient und verursacht einen unnötigen DB-Overhead.


    Lösung 2:

    PHP-Code:
    class Kundenliste {
          private 
    $sqlResult null;
          
          
    punblic function __construct($kdnr_von,$kdnr_bis) {
              
    //select-abfrage auf die Kundendaten z.B;
              // SELECT .... FROM Kunde WHERE id>=$kdnr_von && id<=$kdnr_bis
              
    $this->sqlResult $ergebnisskennung_der_abfrage;
          }
          
          public function 
    display() {
              foreach(
    $this->sqlResult as $row) {
                  echo 
    $row["name"].", Lebenserwartung: ".$this->getLebenserwartung($row["alter"],$row["gesundheit"]);
              }
          }
          
          protected function  
    getLebenserwartung($alter,$gesundheit) {
              return 
    100-($alter*$gesundheit);
          }
      } 
    Vorteil:
    Der DB-Overhead ist weg. Die Methode display() ist somit effizienter als bei Lösung 1

    Nachteil:
    Ich kann nicht mehr auf die Methoden und Eigenschaften des Kunden bequem über ein Kundenobjekt zugreifen. Schlimmer noch, ich muss die Methoden des Kundenobjektes die ich für die Darstellung in der Liste benötige neu implementieren. Jede Änderungen in der Klasse Kunde hat somit auch eine entsprechende Anpassung in der Klasse Kundenliste zur Folge.


    Beide Lösungsansätze haben meiner Meinung nach zu große Nachteile und sind nicht wirklich zufriedenstellend. Wie macht man es in OOP aber richtig? Wie aggregiert oder injiziert man ein Kundenobjekt in die Klasse Kundenliste ohne dabei zusätzlich den DB-Overhead zu verursachen und ist es überhaupt der richtige Ansatz das Problem so zu lösen, oder würde man es mit OOP ganz anders angehen?


    Eine Idee hätte ich da noch, wenn der Konstruktor von Kunde wie folgt ergänz wird:

    PHP-Code:
        public function __construct($kundennummer) {
              if(
    $kundennummer) {
                  
    // Kundendaten aus DB Laden und Eigenschaften setzen
                  // SELECT .... FROM Kunde WHERE id=$kundenummer
              
                  
    $this->kundenummer $DB["kundenummer"];
                  
    // usw...            
              
    }
          } 
    dann ließe sich auch ein „leeres“ Kundenobjekt so eine Art Prototyp erstellen, der mit entsprechenden setter-Methoden nachträglich in der Klasse Kundenliste mit Daten gefüllt werden könnte:


    PHP-Code:
        public function display() {
              foreach(
    $this->sqlResult as $row) {
                  
    $kunde = new Kunde(false);
                  
    $kunde->setName($row["name"]);
                  
    $kunde->setAlter($row["alter"]);
                  
    // usw 
                  
    echo $kunde->name.", Lebenserwartung: ".$kunde->getLebenserwartung();
              }
          } 
    Ok, damit könnte man den DB-Overhead komplett vermeiden und hätte gleichzeitig den Vorteil, daß man bequem mit dem Kundenobjekt arbeiten könnte, allerdings müsste man dann sicherstellen, daß man dem Kundenobjekt auch wirklich alle Daten übergibt, das wiederum würde bei Umfangreichen Objekten mit vielen Eigenschaften schnell relativ aufwendig werden und könnte leicht zu inkonsistenten Objekten führen.

    Was wäre bei dieser Problemstellung nun die richtige Vorgehensweise im Sinne des objektorientierten Designs?

    jack
    Zuletzt geändert von jack88; 02.04.2012, 09:26.

  • #2
    Hallo,

    schau dir mal das MVC Pattern an, ich weiß zwar nicht wie weit du bist, aber nimmt dir einges ab und vor allem du schaffst dir dadurch eine bessere Übersicht.

    So bekommst über dein Model und / oder einen Mapper dein Datenmodel für die Darstellung. Weitere Logik übernimmt der Controller und am Ende folgt die Ausgabe über die View.

    Deine beiden Lösungsvarianten gefallen mir persönlich nicht, weil in beiden zuviel zusammengebracht wird was nicht zusammengehört.

    Wenn du für jeden User schon ein eigenes Objekt erzeugen willst, dann mache das nicht in der UserList Klasse fest, sondern von außen in die Klasse injizieren. Stichwort Dependency Injection.

    Gruß Litter
    Aus dem Dynamo Lande kommen wir. Trinken immer reichlich kühles Bier. Und dann sind wir alle voll, die Stimmung ist so toll. Aus dem Dynamo Lande kommen wir.
    http://www.lit-web.de

    Kommentar


    • #3
      Hej, das ging ja wirklich schnell :-)

      Wie gesagt, die Beispiele dienen nur der Veranschaulichung der eigentlichen Problematik.

      Mit MVC Pattern werde ich mich noch beschäftigen, aber da geht es in erster Line um die Trennung von Datenschicht, Präsentationsschicht und Business-Logik.

      Mir geht es hier sozusagen nur um die Basics des objektorientierten Designs.

      Wenn du für jeden User schon ein eigenes Objekt erzeugen willst, dann mache das nicht in der UserList Klasse fest, sondern von außen in die Klasse injizieren. Stichwort Dependency Injection.
      [FONT=&quot]
      [/FONT][FONT=Arial]Dependency Injection ist mir bekannt, aber wie würdest Du das bei der Klasse Kundenliste einsetzen und welchen Vorteil sollte das haben? Vielleicht könntest Du das an einem kurzen Beispiel veranschaulichen?[/FONT]

      Kommentar


      • #4
        Du möchtest Objekte (hier im OOP Kontext gesprochen) in einer relationalen Datenbank abbilden. Das ganze nennt sich ORM ( https://de.wikipedia.org/wiki/Objekt...nale_Abbildung ). Wenn du eine eigene Lösung entwickeln möchtest wirst du unter dem Begriff sicherlich einige Anhaltspunkte finden. Wenn du auf eine sehr gute und meines Erachtens quasi PHP Standard (im Bezug auf ORM in PHP) Lösung zurückgreifen möchtest sehe dir Doctrine 2 ( Welcome to the Doctrine Project &mdash; Doctrine-Project ) an.

        Gruß Danny
        hostbar - Full Service Internet Agentur

        Kommentar


        • #5
          Du möchtest Objekte (hier im OOP Kontext gesprochen) in einer relationalen Datenbank abbilden.
          Nein, das möchte ich nicht, auch wenn ich das Thema sehr spannend finde ;-)

          Wenn ich das Problem prozedural, also ohne Objekte und Klassen lösen sollte, dann würde ich in etwa wie folgt vorgehen:

          PHP-Code:
          // KUNDE
          function getKundeById($id) {
              
          // SELECT .... FROM Kunde WHERE id=$id
              // Array mit allen Kundendaten zurückliefern
              
          return $db_row;


          // weitere Kundenfunktionen wie z.B. Berechnung der Lebenserwartung
          function getLebenserwartung($alter,$gesundheit) {
                    return 
          100-($alter*$gesundheit);
                }     


          // KUNDENLISTE      
          function displayKundenliste($id_von,$id_bis) {
              
          // SELECT .... FROM Kunde WHERE id>=$id_von && id<=$id_bis
              
          foreach($sqlResult as $row) {
                  echo 
          $row["name"].", Lebenserwartung: ".getLebenserwartung($row["alter"],$row["gesundheit"]);
              }

          Nun möchte ich es wie gesagt anstatt prozedural objektorientiert abbilden und frage mich, wie dieses einfache Beispiel mit Klassen und Objekten vernünftig umgesetzt werden kann?

          gruß jack

          Kommentar


          • #6
            Leider komme ich bei dem beschriebenen Problem nicht weiter. Bei meinen Recherchen bin ich in diesem Zusammenhang über die Begriffe Datenklasse und Modell-Klasse gestolpert.

            Finde dazu allerdings keine brauchbaren Informationen. Vielleicht kennt sich ja jemand mit dem Thema aus und wäre so nett es hier kurz zu erklären was es damit auf sich hat?

            Vielen Dank im Voraus.
            jack

            Kommentar


            • #7
              Naja eine Model Klasse ist denke ich einfach ein Model im MVC.

              Aber eine solche Klasse oder Klassen kannst du auch unabhängig des Patterns schreiben. Daten kannst du eben aus der Persistens Schicht oder auch der Präsentationsschicht holen und einer Model Klasse (oder Datenklasse) übergeben.

              Wichtig dabei ist das die Datenklasse nichts von seiner Datenquelle wissen sollte, weil du dich da in der Flexibilität einschränkst. Du kannst deine Modelklassen von verschiedenen Datenlayern erben lassen. So zum Beipiel kann ein Model mit XML, Json oder einer Datenbank kommunizieren bzw. da seine Daten her holen oder dahin speichern (um mal auf der Webebene zu bleiben).

              Man kann das ganze so ausbauen das Models eben nur die reine Datenstruktur beinhalten oder darstellen und man die handelnden Methoden zum speichern etc. eben in einen Mapper packt.

              Es gibt viele Ansätze in der Richtung, wenn man aus dem Web ausbricht und Serverapplikationen schreibt die dann auf Hardware etc. zugreifen oder die Master File Table bei Windows kommen ja wieder andere Datenlayer zustande.

              Also kurz zusammen gefasst, schreibe dir Datenlayer die dafür geschaffen sind mit gewissen speziellen Datenquellen zu komminuzieren und lasse deine Modelklassen je nach bedarf von diesen Layern erben. Brauchst du in einem Model mehrere Layer dann schaffe dir Methoden die deine Layerklassen zur Benuzung dem Model hinzufügen.

              Gruß der Litter
              Aus dem Dynamo Lande kommen wir. Trinken immer reichlich kühles Bier. Und dann sind wir alle voll, die Stimmung ist so toll. Aus dem Dynamo Lande kommen wir.
              http://www.lit-web.de

              Kommentar


              • #8
                Klassen sollen laut SOLID nur exakt eine Verantwortung haben.

                In deinem Fall ist die Verantwortung der Klasse "UserList", eine Liste von Usern zu verwalten. Das heißt, dass diese Klasse ausschließlich zur Verwaltung von Usern da ist, und nicht zur ERSTELLUNG von User-Objekten.

                Wie litterauspirna schon gesagt hat, musst du für "richtiges" (Anführungszeichen!) OOP die User-Objekte von außen in deine Userlist geben.

                Das sähe dann ugf. so aus:

                PHP-Code:
                $user = new Kunde(10);
                $userList $new Kundenliste();
                $userList->addUser($user); 
                Intern verwaltet die Kundenliste ein Array, in dem alle User-Objekte gespeichert werden. Auf diesem Array kannst du dann alle Aktionen ausführen. Z.b.:

                PHP-Code:
                foreach ($userList->getUsers() as $user) {
                    echo 
                $user->getLebenserwartung();

                Weiterhin sehe ich in deinem Code ein weiteres Problem, das auch auf das Prinzip der "single responsibility" zurückzuführen ist. Deine User-Klasse baut eine Datenbankverbindung auf.

                Das gehört nicht in die Verantwortung einer User-Klasse. Diese sollte ausschließlich den Rahmen repräsentieren, die einen user darstellt.

                Um User aus der Datenbank zu erstellen, solltest du besser eine neue Klasse erstellen, die ausschließlich dazu da ist, User zu erstellen - eine UserFactory.

                Diese Klasse könnte dann z.B. so aussehen:

                PHP-Code:
                class UserFactory {
                    public function 
                createUserFromDatabase($userId) {
                        
                $user = new User();
                        
                // Daten aus der Datenbank suchen und das Userobjekt damit füllen
                        
                return $user;
                    }

                Mit diesem Aufbau schaffst du eine perfekte Trennung der Zuständigkeiten der Klassen. Die UserList macht nur, was der Name sagt: Liste von Usern verwalten. Der User ist nur eine leere Hülle, die mit Daten gefüllt werden kann und die UserFactory macht nichts anderes außer user aus der Datenbank zu generieren.

                Das erleichtert die Wartung und Entwicklung deiner Klassen erheblich, da du genau weißt, welche Klasse was macht. Und in der Verwendung ist es auch wesentlich eindeutiger, was passiert:

                PHP-Code:
                $userFactory = new UserFactory();
                $user $userFactory->createUserFromDatabase(1);
                $userList = new UserList();
                $userList->addUser($user);

                foreach (
                $userList->getUsers() as $user) {
                    echo 
                $user->getLebenserwartung();

                Zuletzt geändert von ApoY2k; 04.04.2012, 10:02.
                This is what happens when an unstoppable force meets an immovable object.

                Kommentar


                • #9
                  Vielen Dank nochmals für alle Antworten.

                  Wenn ich Die Userliste wie von ApoY2k beschrieben mit einer UserFactory generiere, dann banötige ich ich für jeden User genau eine Sql-Abfrage, ist das nicht sehr ineffizient?

                  Um eine Liste zur erstellen würde ich normalerweise wie folgt vorgehen:

                  Code:
                  // SELECT .... FROM Kunde WHERE id<100
                  foreach($sqlResult as $row) {
                      // erstelle Userliste   
                   }
                  Die UserFactory-Lösung entspricht imho vielmehr diesem Ansatz:

                  Code:
                  for($i=0;$i<100;$i++) {
                      // SELECT .... FROM Kunde WHERE id=$i
                      // erstelle UserListe
                  }
                  oder mache ich hier irgendwo einen Denkfehler?
                  gruß
                  jack

                  Kommentar


                  • #10
                    So wie er die UserFactory geschrieben hat, ja.

                    Aber es spricht ja nichts dagegen, eine entsprechende Methode zum erstellen ALLER user zu schreiben, oder eine Methode die den User durch übergeben eines mysql-result-sets erstellt.

                    z.B.:
                    PHP-Code:
                    public function createUserList() {
                        
                    // MySQL-Query für alle user
                        // Schleife über alle Ergebnisse und user erstellen

                    Zuletzt geändert von PicdumpR; 21.04.2012, 08:13.
                    PicdumpR - The only source of funny pictures you will ever need

                    Kommentar

                    Lädt...
                    X