Statischer Schiffsverleih - OOP Beispiele

  • Hey zusammen,


    ich habe jetzt einen statischen Schiffsverleih programmiert und möchte nun eure Meinung zu meinem OOP-Code wissen. Kritik und Verbesserungsvorschläge sind gerne gesehen.


    Der Code:



    Grüße,

    Stef

  • Habe jetzt nur mal flüchtig drüber geguckt, aber einiges ist mir aufgefallen:


    Eine Funktion die mit get anfängt, sollte einen Return-Wert haben oder anders heißen, wenn sie ja nichts liefert.


    Warum hier Zwischenvariablen:

    $bookedhours = $hour;

    $this->bookedTime = $bookedhours;


    $price = self::Preis * $bookedhours;

    $this->kosten = $price;



    Die Funktion showAllData zeigt nichts an (ist auch richtig) aber auch hier ist der Name falsch.


    Du solltest deutlich mehr auf die Namensgebung achten, denn mal liefert eine get-Funktion etwas und mal nicht. Das scheint im ersten Moment nicht dramatisch, aber wenn man nach längerer Zeit wieder mal an den Quellcode muss, kann man sich damit selbst verwirren.

  • Ein Konstruktor dient dazu eine Instanz zu initiieren. Darin sollte aber nicht die komplette Logik bereits ablaufen.

    Aufgaben hast Du bereits in Methoden ausgelagert, das ist gut, aber im Konstruktor alle Methoden aufzurufen ist keine gute Lösung. Du nimmst Dir damit die Möglichkeit, spätere Prozesse nachträglich einzubauen.


    Anstatt showAllData() würde ich als Getter setzen, bspw. getData(). In der Methode sollte dann die Berechnung angestoßen werden. Noch besser wäre das in zwei Methoden aufzubauen, bspw. so:

    PHP
    $schiffsverleih->calculate();
    $data = $schiffsverleih->getData();

    Das kannst Du dann wenn die Methode Calculate() bspw. eine Referenz auf sich selbst zurückgibt auch zusammenfassen:

    PHP
    $data = $schiffsverleih->calculate()->getData();
  • Hey,


    Vielen Dank für euer Feedback. Ich werde es mir heute bzw. die nächsten Tage dies genauer anschauen und diese Klasse dann optimieren und erweitern.


    Arne Drews :


    Wie meinst du : „Das kannst Du dann wenn die Methode Calculate() bspw. eine Referenz auf sich selbst zurückgibt“ ?


    Eine Referenz ist eine zusätzliche Variable welchen eine Kopie der anderen Variable ist. Und dies erstellt man dann wenn man & vor die Variable schreibt oder eben einer variable den gleichen Wert wie in der anderen Variable zuweist.


    Wie kann man es denn in OOP umsetzen?


    Ich suche dann dazu auch mal.


    Grüße Stef

  • Eine Referenz ist eine zusätzliche Variable welchen eine Kopie der anderen Variable ist.

    Das stimmt so nicht. Bei einer Referenz ist es keine Kopie!

    Eine Referenz an sich ist auch keine Variable. Mit einer Referenz verweist man auf den Adressspeicher einer vorhandenen Variablen oder eines Objektes.


    Wie kann man es denn in OOP umsetzen?

    Eine Methode kann eine Referenz auf seine übergeordnete Instanz zurückgeben. Ein Beispiel:

    Jetzt habe ich halt die Möglichkeit des MethodChaining:

    PHP
    $foobar = new FooBar( "Hello " );
    echo $foobar->addText( "World" )->getText();
  • Ich habe mir mal ein paar Details Deiner Klasse angesehen. Warum nutzt Du für Methoden die statische Klassifizierung, für Variablen aber den Instanz-Operator $this?

    PHP
    $this->buchername = $buchername;
    $this->anzahlBesucher = $anzahlBesucher;
    $this->buchungVon = $buchungVon;
    $this->buchungBis = $buchungBis;
                
    Schiffsverleih::sumBuchungszeit();
    Schiffsverleih::getTimeParts();
    Schiffsverleih::getBootNummer();
    Schiffsverleih::getAnzahlSchiffe();
    Schiffsverleih::getPrice();

    Mit $schiffsverleih = new Schiffsverleih(...)erstellst Du eine Instanz der Klasse. Darin solltest Du dann auch $this verwenden:

    PHP
    $this->sumBuchungszeit();
    $this->getTimeParts();
    $this->getBootNummer();
    $this->getAnzahlSchiffe();
    $this->getPrice();

    Die statische Schreibweise macht nur Sinn, wenn die Klasse auch statisch ist.

  • Hey,


    vielen Dank Arne Drews. Ich habe es jetzt so eingermaßen verstanden.


    Wenn man $this zurückgibt dann gibt man wenn man die Function ausführt die ganze Klasse zurück und somit kann man auf dieser die andere Function ausführen weil diese dort verfügbar ist. Wie ich das jedenfalls mit mehr als 2 Functionen machen soll weiß ich noch nicht. Werde es aber aufjedenfall austesten.


    Ich habe den Code nochmal komplett neu erstellt und möchte euch diesen jetzt zeigen:


    Grüße,

    Stef

  • Die Richtung ist besser.

    Wenn Du empfänglich bist, darf ich Dir vielleicht als Anregung mal einen Ansatz mit geben, den ich anhand Deines Beispiels verfolgen würde.


    Da OOP von Objekten ausgeht, würde ich im Ganzen betrachten, was ich alles als Objekt definieren könnte. Das wären für mich an dieser Stelle folgende:


    • Company ( der Schiffsverleih )
      Hier werden Eigenschaften/Methoden des Unternehmen, bspw. Anzahl der Schiffe, verfügbare Schiffe
    • Ship ( das/ein Schiff )
      Hier werden die Eigenschaften/Methoden eines Schiff verwaltet, bspw. die Verfügbarkeit
    • Booking ( die Buchung )
      Hier werden Eigenschaften/Methoden einer Buchung verwaltet, bspw. Zeitraum
    • Planner ( der Buchungsplaner )
      Quasi ein Kalender, der Informationen hat oder erhält, wann bspw. welches Schiff gebucht ist
    • PriceList ( das Preismodell )
      Da unterschiedliche Schiffe unterschiedliche Eigenschaften, wie etwa Preise haben können, sollte es unterschiedliche Preismodelle geben
    • Customer ( der Kunde/Buchende )
      Ist denke ich mal selbsterklärend


    Warum ( auf den ersten Blick ) so umständlich?

    Du hast mit diesen Objekten Deine Prozesse sinnvoll aufgeteilt. Ein Objekt im OOP sollte immer nur eine Aufgabe haben und das ist bspw. in Bezug auf die Buchung nur der Vorgang selber. Die Buchung weiß weder persistent welches Schiff, noch welcher Buchungszeitraum vorliegt. Die Buchung fungiert als eine Art Controller, die einfach nur Daten steuert und weiter gibt, in diesem Fall sinnvoller Weise an den Planner.

    Der terminiert den Buchungsvorgang auf den gewählten Zeitraum und das gewählte Schiff und sperrt diese Kombination für weitere Buchungsanfragen.


    Die PriceList hätte man auch mit Ship kombinieren können, aber ich versuche immer an Flexibilität zu denken und könnte mir vorstellen, dass es irgendwann mal zu Rabattaktionen kommt. Diese lassen sich im PriceList-Model sehr gut unterbringen. Man kann also die Klasse PriceList einfach gegen eine andere austauschen, um neue Preismodelle einzufügen. Alles andere im Code bleibt wie es ist, da die Eigenschaften und Methoden der unterschiedlichen PriceList-Klassen immer gleich sind. Nur die Logik unterscheidet sich.


    Das im Detail jetzt alles durchzugehen wird ziemlich lang und für viele auch sicher langweilig, daher belasse ich das hier dabei.

    Wenn Du Interesse hast, kann ich Dir gern mal eine kleine funktionierende Beispiel-Buchung auf so einer Basis aufbauen, dann kannst Du damit ja mal rumspielen. Wird allerdings erst am WE was, weil ich morgen für drei Tage beruflich unterwegs bin.

  • Hey,


    Wow. Vielen Dank für die sehr ausführliche Beschreibung und Erklärung sowie deiner Sichtweise zu dem Aufbau dieser Applikation.

    Daraus kann man sehr viel lernen und aufnehmen.


    Daran habe ich nicht mehr gedacht und alles in eine Klasse gepackt.


    Eigentlich ist die Klasse ja das Objekt. Nehmen wir z.b. ein Auto. Die Eigenschaften sind dann z.b. der Motortyp, Benzintyp, usw. Und die Methoden sind dann z.b. Aktionen wie Tanken, Fahren oder Auto putzen.


    Wenn Du Interesse hast, kann ich Dir gern mal eine kleine funktionierende Beispiel-Buchung auf so einer Basis aufbauen, dann kannst Du damit ja mal rumspielen. Wird allerdings erst am WE was, weil ich morgen für drei Tage beruflich unterwegs bin.


    Sehr gerne. Ich werde auch dann mal von meiner Seite es so erstellen. Dann kan man ja beides vergleichen. Aber das dauert bestimmt bis Sonntag.


    Grüße,

    Stef

  • Ich habe mal folgenden Workflow erstellt, den ich einschlagen würde. Hier und da mag es sicher bessere Möglichkeiten geben, aber ich wollte nun auch nicht ein ganzes Projekt erstellen. Daher soll das nur die Richtung zeigen, die aus meiner Sicht OOP nahe kommt. Datenbank-Abfragen usw. habe ich ausgelassen, was in den Kommentaren auch zu lesen ist.


    Sowohl den Workflow, als auch die Klassen habe ich nur auf das Minimum begrenzt, das für dieses Beispiel nötig war. Ich denke aber, dass relativ gut erkennbar ist, wo die Vorteile liegen.


    Hier mal der Workflow:

    Die Klassen dazu:

    Gruß Arne

  • Hey Arne,


    vielen Dank. Ich war die letzten Tage leider so beschäftigt, dass ich garnicht dazu kam meine Version zu erstellen. Ich teste es aber an einem anderen Beispiel und poste dazu dann nochmal hier.


    Man führt erst alle Prozesse aus und erst wenn es klappt gibt man die Erfolgsmeldung aus und erstellt z.B. eine PDF mit der Rechnung und den Buchungsdaten.

    Für die PDF braucht man dann noch interne retrun Funktionen welche die Daten die ich brauche zurückgeben. Denn ich kann ja nicht bei return $this auf Eigenschaften zugreifen welche protected oder private sind.


    Ein paar Fragen habe ich noch zu deinem Code:

    • Man kann auch dem Constructor die Klasse mit der Variable, aus dieser, als Parameter übergeben ?
      • Beispiel: public function __construct(Ship $shipId){}
    • Warum benutzt du static bei der Eigenschaft sowie den Funktionen vom Planner ?


    Ich habe mir schon zahlreiche Tutorials angeschaut zu static verstehe aber den Sinn davon nicht. Normale Functionen und Eigenschaften haben eigentlich fast die selbe Eigenschaft wie static. Man muss eben zuerst die Klasse instanzieren und man kann diese nur mit dem Pfeil ( -> ) zugreifen. Mit static braucht man nicht die Klasse zu instanzieren und kann mit 2 Doppelpunkten aus die zugreifen. Aber das ist für mich kein Grund static zu verwenden.

    • Warum benutzt du dabei keine protected bzw private Funktionen?

    Ich habe es so gelernt, dass Angreifer wenn alles public ist auf die ganzen wichtigen Funktionen Zugriff haben kann und man daher nur die Funktionen welche für den User sichtbar sein sollten man mit public machen soll. Alle anderen als protected oder private.


    Ich freue mich auf deine Antwort.


    Grüße,

    Stef

  • Hi,


    Man kann auch dem Constructor die Klasse mit der Variable, aus dieser, als Parameter übergeben ?

    Ja, ist aber versionsabhängig! ;)

    Ich habe hier ja nur einen funktionalen minimalen Ansatz zur Verfügung gestellt und solche Dinge bewusst weg gelassen.


    Warum benutzt du static bei der Eigenschaft sowie den Funktionen vom Planner ?

    Der Planner ist für mich kein instanziierbares Objekt. Er muss nur bestimmte Methoden ausführen können.

    Ich hätte aus meiner Sicht auf die Aufgaben des Planner bezogen keinen Vorteil, ein Objekt instanziieren zu müssen.

    Daher habe ich den Planner statisch gemacht.


    Mit static braucht man nicht die Klasse zu instanzieren und kann mit 2 Doppelpunkten aus die zugreifen. Aber das ist für mich kein Grund static zu verwenden.

    Für mich aber :)

    Mit Doppelpunkt oder nicht hat das allerdings wenig zu tun. Mir reicht das Argument, dass ich kein Objekt instanziieren muss, denn das benötige ich dafür nicht.


    Ein Beispiel:

    Jedes Schiff hat seine Eigenschaften, die nicht mit denen anderer Schiffe übereinstimmen müssen. Daher definiere ich ein Schiff als ein eigenständiges Objekt, was einer Instanz der Klasse Ship entspricht.

    Den Planner gibt es nur einmal, ganz egal welches Schiff, welcher Zeitraum und welche Person betroffen ist.


    Mann kann den Planner sicherlich instanziierbar umschreiben, aber ich würde das an der Stelle nicht machen.


    Warum benutzt du dabei keine protected bzw private Funktionen?

    Das tue ich. In dem Beispiel sind ja nur die elementaren Methoden drin. Die müssen aus dem Script aufgerufen werden können und sind daher public.


    Du darfst nicht vergessen, dass das hier nur ein kurzes Beispiel darstellt, wie ich den Workflow interpretieren würde.

    Es fehlt noch ein ganze Menge zum fertigen Projekt, aber zumindest funktioniert das Beispiel und mit Planner::debug() erhältst Du ein Array mit den Daten, die der Planner im Produktivmodus als bestätigte Buchung eintragen würde.


    Das ist am Ende auch nur meine Sicht auf die Dinge, was nicht zwingend als einzig richtiger Weg zu betrachten ist!

  • Hey,


    okay. Alles klar. Ein sehr großes Dankeschön an dich :)


    Ich habe vorhin geschrieben, dass ich noch ein Beispiel erstelle wo ich es austeste.


    Als Beispielanwendung habe ich mir ein Login und Logout mit Datenbankanbindung vorgestellt.


    Da stehe ich vor einem Rätsel mit den Cookies. Ich rufe beim Click auf den Ausloggbutton eine Function auf welche den Cookie löschen soll. Aber er wird garnicht gelöscht. Ich habe bereits viel gegoogelt aber auch trotzdessen konnte ich das Problem nich lösen. Der Cookie ist trotzdem weiterhin da. Die Funktion set_cookie gibt bei der Ausführung aber true zurück. Trotzdem wird der Cookie nicht gelöscht. Ich weiß nicht mehr weiter.


    Was mache ich falsch?


    So sieht meine Beispielanwendung aus:

    Meine Klassen:


    Grüße,

    Stef

Jetzt mitmachen!

Sie haben noch kein Benutzerkonto auf unserer Seite? Registrieren Sie sich kostenlos und nehmen Sie an unserer Community teil!