Sichtbarkeit von Variablen
Uwe
- php
Hallo!
Ich habe 3 Klassen, 2 davon erweitern die erste Klasse. Ich möchte von einer der beiden Klassen auf eine Variable der anderen zugreifen wobei der Inhalt dieser in der parent-Klasse definiert wird.
Hier mal ein Beispiel-Code
// die parent-Klasse
class foo
{
public function bar()
{
return 'hund';
}
}
// die Klasse soll auf eine Variable seines Geschwisters zugreifen
class bla extends foo
{
public function test()
{
global $a;
var_dump($a); // NULL
}
}
//
class start extends foo
{
public function run()
{
$a = $this->bar(); // $a soll in bla->test() verfügbar sein
$o = new bla();
$o->test();
}
}
$start = new start();
$start->run()
Wie bekomme ich also den Inhalt von $a nach foo->test()?
Der Grund ist, dass ich $a in der start-Klasse durch andere Klassen bei bedarf bearbeiten können will.
In etwa wie durch den Aufruf einer Funktion mit einem & vor dem Parameter. Nur, dass ich es ohne übergebenen Parameter an die Funktion möchte.
Mir ist auch nicht ganz klar wieso $a in meinem Beispiel NULL ergibt.
Wenn ich eine Variable ausserhalb aller Klassen definiere und in einer Klasse dann global $Variable;
schreibe, dann habe ich auch Zugriff darauf. Wo liegt hier mein Fehler?
Uwe
wobei der Inhalt dieser in der parent-Klasse definiert wird.
Davon seh ich aber nichts. Was meinst du damit?
// die Klasse soll auf eine Variable seines Geschwisters zugreifen
Dazu sollte sie die Instanz des Geschwisters kennen. Und ich seh immer noch keine Variable der Klasse ;-)
$a = $this->bar(); // $a soll in bla->test() verfügbar sein
Das heißt? Du weißt der Variablen $a (die nur in der Funktion gültig ist) das Ergebnis aus this->bar zu, also $a = 'hund'. Was hat das mit test() zu tun?
Wie bekomme ich also den Inhalt von $a nach foo->test()?
Lass jede Instanz wissen wie die andere Instanz heißt.
Mir ist auch nicht ganz klar wieso $a in meinem Beispiel NULL ergibt.
Weil $a nirgends global definiert ist.
Vielleicht guckst du dir nochmal deinen Code durch, was du da wirklich machst und überlegst nochmal was du wirklich willst. Ich glaub ich hab das immer noch nicht ganz kapiert.
Für mich klingt es als sollte eine Instanz die andere kennen, damit sie auf die Variablen gegenseitig zugreifen können.
wobei der Inhalt dieser in der parent-Klasse definiert wird.
Davon seh ich aber nichts. Was meinst du damit?
Schau genauer hin ;) Der Inhalt, respektive 'Hund', wird in der parent-Klasse, respektive foo, definiert.
// die Klasse soll auf eine Variable seines Geschwisters zugreifen
Dazu sollte sie die Instanz des Geschwisters kennen. Und ich seh immer noch keine Variable der Klasse ;-)
Sehschwäche? In der Geschwister-Klasse, respektive start, wird die Variable, respektive $a, definiert.
$a = $this->bar(); // $a soll in bla->test() verfügbar sein
Das heißt? Du weißt der Variablen $a (die nur in der Funktion gültig ist) das Ergebnis aus this->bar zu, also $a = 'hund'. Was hat das mit test() zu tun?
In test() will ich $a verarbeiten.
Wie bekomme ich also den Inhalt von $a nach foo->test()?
Lass jede Instanz wissen wie die andere Instanz heißt.
Wie mach ich das?
Mir ist auch nicht ganz klar wieso $a in meinem Beispiel NULL ergibt.
Weil $a nirgends global definiert ist.
$a ist dort wo ich es ausgebe, in bla->test(), global definiert.
Für mich klingt es als sollte eine Instanz die andere kennen, damit sie auf die Variablen gegenseitig zugreifen können.
Genau das. Wobei ich auch überlegt habe ob ich in der parent-Klasse (foo) den Wert irgendwie speichern kann so, dass alle abgeleiteten Klassen gemeinsam mit dem selben Wert arbeiten/bearbeiten können.
Uwe
Hi,
Sehschwäche? In der Geschwister-Klasse, respektive start, wird die Variable, respektive $a, definiert.
Mach dir bitte erst mal klar, dass die Klasse nur einen Bauplan liefert.
So lange noch keine konkrete Instanz davon erzeugt wurde, gibt es noch gar kein solches $a.
In test() will ich $a verarbeiten.
Dann beantworte, nachdem du dir obiges klar gemacht hast, bitte die Frage: Welches?
Mir ist auch nicht ganz klar wieso $a in meinem Beispiel NULL ergibt.
Weil $a nirgends global definiert ist.$a ist dort wo ich es ausgebe, in bla->test(), global definiert.
Auch an dieser Stelle fehlt dir offenbar noch das grunglegende Verständnis.
Es gibt in deinem Code kein globales $a – also kann das Schlüsselwort global auch kein solches in den aktuellen Scope hinein ziehen, weil es schlicht und einfach nicht existiert.
Für mich klingt es als sollte eine Instanz die andere kennen, damit sie auf die Variablen gegenseitig zugreifen können.
Genau das.
Und wo genau sind bei dir die *Istanzen*?
Wobei ich auch überlegt habe ob ich in der parent-Klasse (foo) den Wert irgendwie speichern kann so, dass alle abgeleiteten Klassen gemeinsam mit dem selben Wert arbeiten/bearbeiten können.
Vielleicht suchst du statische Properties.
Auch bezüglich dieser gilt aber, bitte wieder klar machen – sie werden gemeinhin auch als schlechter Programmierstil angesehen, und sollten höchstens in Ausnahmefällen Anwendung finden.
MfG ChrisB
Dann drückst du dich aber ziemlich umständlich und irreführend aus.
wobei der Inhalt dieser in der parent-Klasse definiert wird.
Davon seh ich aber nichts. Was meinst du damit?Schau genauer hin ;) Der Inhalt, respektive 'Hund', wird in der parent-Klasse, respektive foo, definiert.
Der Inhalt schon, aber wo ist die Variable dazu? Ich sehe immer noch keine Klassenvariable.
// die Klasse soll auf eine Variable seines Geschwisters zugreifen
Dazu sollte sie die Instanz des Geschwisters kennen. Und ich seh immer noch keine Variable der Klasse ;-)Sehschwäche? In der Geschwister-Klasse, respektive start, wird die Variable, respektive $a, definiert.
Die wird nicht in der Klasse definiert, sondern in der Methode namens run. Außerhalb gibt es sie nicht. Das ist ein ziemlicher Unterschied!
$a ist dort wo ich es ausgebe, in bla->test(), global definiert.
Lies dich mal in Gültigkeitsbereiche von Variablen ein. Wenn du in einer Methode was deklarierst, gibt es das nur da. Wenn du sagst "ein $a von außerhalb soll auch in meiner Methode gelten" (das tut global), dann muss dieses $a auch definiert sein. Du hast aber keins definiert.
Für mich klingt es als sollte eine Instanz die andere kennen, damit sie auf die Variablen gegenseitig zugreifen können.
Genau das. Wobei ich auch überlegt habe ob ich in der parent-Klasse (foo) den Wert irgendwie speichern kann so, dass alle abgeleiteten Klassen gemeinsam mit dem selben Wert arbeiten/bearbeiten können.
Dann müsste es aber ein statischer Wert sein, damit alle auf die selbe Instanz der Variable zugreifen.
Was genau solls denn tun? Erzähl mal was dahinter steckt, wenns geht als richtges Beispiel wo man sich vorstellen kann was du meinst. Dann hat dir sicher jemand einen passenden Tip.
Hallo!
Danke erstmal für die Hilfe! Auf php.net habe ich leider nicht sehr viel zur Sichtbarkeit von Variablen innerhalb von Klassen gefunden. Bzw. nichts was mit global zu tun hat. Oder hab ich nicht gut genug gesucht?
Was genau solls denn tun? Erzähl mal was dahinter steckt, wenns geht als richtges Beispiel wo man sich vorstellen kann was du meinst. Dann hat dir sicher jemand einen passenden Tip.
Also ich schreibe mir ein CMS.
Ich habe 4 "Standard-Klassen": Core, Model, View und Controller.
Model, View und Controller erweitern Core. Model und Controller werden wiederum durch Model- und Controller-Klassen verschiedener Module erweitert.
Jetzt lade ich über den Controller von Modul X Angaben zur Seite wie z.B. Seitentitel. Anschließend kommt Modul Y - z.B. zur Anzeige von Artikeln - und dessen Controller soll den Seitentitel ändern/erweitern können.
Das ist der Plan. Hilft das weiter?
Uwe
Hi!
Auf php.net habe ich leider nicht sehr viel zur Sichtbarkeit von Variablen innerhalb von Klassen gefunden. Bzw. nichts was mit global zu tun hat. Oder hab ich nicht gut genug gesucht?
Alles was zu Variable Scope gesagt wird, gilt grundsätzlich auch für Funktionen innerhalb von Klassen.
Ich habe 4 "Standard-Klassen": Core, Model, View und Controller.
Model, View und Controller erweitern Core. Model und Controller werden wiederum durch Model- und Controller-Klassen verschiedener Module erweitert.
Ich wüsste jetzt nicht, was Model, View und Controller für fachliche Gemeinsamkeiten haben, so dass sie eine Basisklasse rechtfertigen. Das Model beispielsweise ist zum Daten-Handling da. Es weiß üblicherweise gar nichts von irgendwelchen Dingen der Ausgabe. Selbst der Controller hat im Prinzip keine bis wenig Ahnung, wofür die Daten da sind, die er zwischen Model und View vermittelt. Auch ist nicht festgelegt, dass Views immer einen Seitentitel oder ähnliches haben. Die Daten können ja auch via Ajax nur einen Teil der Seite repräsentieren. Ich sehe also keinen Grund, warum der Controller oder irgendeine Basisklasse den Seitentitel kennen muss.
Vielleicht hast du ja deine eigenen Vorstellungen und es ist gar nicht das bekannte MVC-Muster, sondern nur etwas, das dieselben Begriffe verwendet. Schau dir jedenfalls mal jene Sven-Rautenberg-Antwort an, besonders seine Ausführungen zum SOLID-Prinzip.
Jetzt lade ich über den Controller von Modul X Angaben zur Seite wie z.B. Seitentitel. Anschließend kommt Modul Y - z.B. zur Anzeige von Artikeln - und dessen Controller soll den Seitentitel ändern/erweitern können.
Ist es denn nötig, dass Modul X den Seitentitel setzt, obwohl es doch gar nicht das Haupt-Modul ist, sondern Modul Y? Und gibt es denn nicht einen koordinierenden Controller, der die einzelnen Module und deren Controller aufruft und dabei solche Daten für die generelle Seitengerüst-View zusammenträgt?
Lo!
Hallo!
Ich wüsste jetzt nicht, was Model, View und Controller für fachliche Gemeinsamkeiten haben, so dass sie eine Basisklasse rechtfertigen.
Die Core-Klasse stellt z.B. die Datenbankverbindung bereit, die wird dann von den meißten Models und Controllern genutzt.
Und gibt es denn nicht einen koordinierenden Controller, der die einzelnen Module und deren Controller aufruft und dabei solche Daten für die generelle Seitengerüst-View zusammenträgt?
Ha, so einfach kann die Welt sein, natürlich. Ich weiß nicht, ich habe wohl einfach manchmal eine riesige Blockade im Kopf. Schlimm. So, jetzt schäm ich mich mal ebend für meine Frage.
Danke sehr!
Uwe
Moin!
Ich wüsste jetzt nicht, was Model, View und Controller für fachliche Gemeinsamkeiten haben, so dass sie eine Basisklasse rechtfertigen.
Die Core-Klasse stellt z.B. die Datenbankverbindung bereit, die wird dann von den meißten Models und Controllern genutzt.
Schlechte Idee. Damit machst du diese Core-Klasse zu einem Gott-Objekt, welches Kenntnis über alle möglichen Zusammenhänge hat - und sowas ist ganz schlecht erweiterbar.
Datenbankzugriff ist etwas, was keinesfalls zentral gelagert gehört. Es ist ein Aspekt, der unter Umständen sehr flexibel gehandhabt werden muss, denn WELCHE Datenbank wird denn z.B. angesprochen? Und mit welcher Methode? Nur mal als Beispiele: MySQL oder Postgres? Oder MySQL via mysql, mysqli oder PDO-Extension ansprechen? Oder anstelle der relationalen Datenbank was völlig anderes?
Deshalb gehört Datenbank-Zugriff isoliert in eine zugehörige Klassengruppe - die "Single Responsibility" in SOLID.
Ok, und noch ein Grund, warum eine DB-zugreifende Core-Klasse blöd ist: Der Controller hat keinerlei Veranlassung, auf die Datenbank zuzugreifen. Dafür hat er seine Models. Und die Models haben auch keine Veranlassung, die Datenbankzugriffe in sich selbst zu tragen, denn die sind dafür da, die in der DB gespeicherten Daten abzufragen (über eine DB-Klasse) und gegenüber dem Controller bzw. dem View passend zu repräsentieren.
- Sven Rautenberg
Hallo!
Datenbankzugriff ist etwas, was keinesfalls zentral gelagert gehört. Es ist ein Aspekt, der unter Umständen sehr flexibel gehandhabt werden muss, denn WELCHE Datenbank wird denn z.B. angesprochen? Und mit welcher Methode? Nur mal als Beispiele: MySQL oder Postgres? Oder MySQL via mysql, mysqli oder PDO-Extension ansprechen? Oder anstelle der relationalen Datenbank was völlig anderes?
Die Datenbank-Klasse ist abstrakt und die Klasse mit den entsprechenden Funktionen (MySQL, MySQLi etc.) wird geladen. Es ist also egal welche DB angesprochen wird, die Funktionsaufrufe bleiben gleich.
Ok, und noch ein Grund, warum eine DB-zugreifende Core-Klasse blöd ist: Der Controller hat keinerlei Veranlassung, auf die Datenbank zuzugreifen. Dafür hat er seine Models. Und die Models haben auch keine Veranlassung, die Datenbankzugriffe in sich selbst zu tragen, denn die sind dafür da, die in der DB gespeicherten Daten abzufragen (über eine DB-Klasse) und gegenüber dem Controller bzw. dem View passend zu repräsentieren.
Ok, das leuchtet mir ein. Ich schau mal, dass ich die DB-Klasse in meine Model-Klasse bringe und ob die Core-Klasse überhaupt nötig ist.
Gibt es denn auch Gründe _für_ eine Core-Klasse oder ist das allgemein eine schlechte Idee?
Uwe
Hi!
Ok, das leuchtet mir ein. Ich schau mal, dass ich die DB-Klasse in meine Model-Klasse bringe und ob die Core-Klasse überhaupt nötig ist.
Ein Model muss nicht zwangsläufig ein DBMS befragen. Es kann die Daten auch aus einer anderen Quelle holen, sie dort ablegen oder sie einfach so berechnen. Ein Model kann sich einer DB-Klasse bedienen, aber es sollte nicht davon abgeleitet sein.
Gibt es denn auch Gründe _für_ eine Core-Klasse oder ist das allgemein eine schlechte Idee?
Im .NET-Framework gibt es eine Klasse Object, von der ist alle[*] anderen Klassen abgeleitet sind. Die stellt aber keine fachlichen sondern nur mehr oder weniger für das Framework interessante Dienste bereit. Es besteht jedoch keine unbedingte Notwendigkeit, eigene Klassen, die keine Erweiterung des Frameworks darstellen auch davon abzuleiten oder für diese ebenfalls eine Basisklasse zu erstellen.
Gründe lassen sich schon finden, aber sie sollten gut überlegt sein. _Keine_ Gründe wären zum Beispiel:
Klassen sollen sich keine Dienste holen, weil dies Abhängigkeiten schafft, was man besser vermeidet. Besser ist es, ihnen die benötigten Werkzeuge mitzugeben, was auf den Namen Dependency Injection hört. Funktional gesehen sind sie zwar immer noch auf einen externen Dienstleister angewiesen, aber den kann man frei übergeben - mal diesen, mal jenen, mal nur einen Fake, wenn man Test machen möchte (zum Beispiel bei Test Driven Design).
[*] Vielleicht gibt es Ausnahmen, die sind mir aber nicht bekannt/begegnet.
Lo!
Hi!
Gibt es denn auch Gründe _für_ eine Core-Klasse oder ist das allgemein eine schlechte Idee?
Im .NET-Framework gibt es eine Klasse Object, von der alle[*] anderen Klassen abgeleitet sind.
[*] Vielleicht gibt es Ausnahmen, die sind mir aber nicht bekannt/begegnet.
Ich korrigiere mich: Alle Klassen in C# sind von System.Object abgeleitet. Das gilt auch für selbst erstelle, die dann implizit von System.Object erben, wenn man nichts angibt.
Es besteht jedoch keine unbedingte Notwendigkeit, eigene Klassen, die keine Erweiterung des Frameworks darstellen auch davon abzuleiten oder für diese ebenfalls eine Basisklasse zu erstellen.
Dieser Satz ist damit hinfällig.
Die [System.Object-Klasse] stellt aber keine fachlichen sondern nur mehr oder weniger für das Framework interessante Dienste bereit.
Eine Methode von System.Object ist ToString(), die immer eine Stringdarstellung eines Objekts liefert. Sofern sie nicht überschrieben ist, ist das lediglich der Klassenname. Der Klassenname ist vor allem für das Debugging nützlich, um zu sehen, welcher Klasse Kind ein bestimmtes Objekt ist. Ansonsten wird die Stringdarstellung beispielsweise bei Zahlen benötigt. Es gibt ja keine implizite Typkonvertierung wie bei PHP, also muss sich jemand darum kümmern.
Lo!
Moin!
Datenbankzugriff ist etwas, was keinesfalls zentral gelagert gehört. Es ist ein Aspekt, der unter Umständen sehr flexibel gehandhabt werden muss, denn WELCHE Datenbank wird denn z.B. angesprochen? Und mit welcher Methode? Nur mal als Beispiele: MySQL oder Postgres? Oder MySQL via mysql, mysqli oder PDO-Extension ansprechen? Oder anstelle der relationalen Datenbank was völlig anderes?
Die Datenbank-Klasse ist abstrakt und die Klasse mit den entsprechenden Funktionen (MySQL, MySQLi etc.) wird geladen. Es ist also egal welche DB angesprochen wird, die Funktionsaufrufe bleiben gleich.
Du hast also sowas:
abstract class DB {
abstract public function query($sql);
//...
}
class Mysql_DB extends DB {
public function query($sql) {
mysql_query($sql);
}
}
Da würde ich auch erstmal "suboptimal" rufen wollen.
Zum einen: Die sogenannte Datenbankunabhängigkeit ist, wenn man sie wirklich voll ausprogrammieren wollte, ein schöner Schein! Weil Nichtexistent. Datenbanken sind von Natur aus unterschiedlich, nur deswegen eignet sich die eine für manche Dinge besser, als eine andere. Deswegen ist auch das SQL unterschiedlich, was man zur Nutzung der Features braucht. Will man DB-unabhängig sein, kann man nur die Features nutzen, die in allen DBs vorkommen, oder muss fehlende Features irgendwie per Workaround hinbasteln. Insofern: Du brauchst keine DB-Unabhängigkeit, du entscheidest dich am Anfang für eine existierende Datenbank und benutzt die.
Aber andererseits benutzt du diese DB nur solange, wie sie die Features deines CMS supporten kann. Wenn irgendwann der Fall eintritt, dass die bisherige DB nicht mehr ausreicht, dann willst du die Datenbank doch mal austauschen. Und in so einem Fall ist es viel angenehmer, wenn mindestens mal sämtliche SQL-Querys gesammelt an einem Ort (sprich: Leicht auffindbar in einem bestimmten Typ von Klasse) liegen und dort entsprechend modifiziert bzw. durch neue Querys in einer alternativen Klassenerbfolge ersetzt werden können.
Zweiter Punkt: Die oben genannte Abstrakte Klasse enthält ja keinen ausführbaren Code. Und ich hielte es auch für schwierig, dort solchen Code wirklich unterzubringen, denn der kann ja nur allgemeine Dinge tun, aber keine konkreten DB-Funktionen aufrufen. Allgemeine Dinge sind jedoch vermutlich etwas anderes, als DB-Abfragen vorzunehmen, gehören deshalb nicht in eine DB-Klasse. Ein Interface wäre an dieser Stelle als Alternative für eine abstrakte, codelose Klasse eindeutig vorzuziehen. Und (das ist das I von SOLID) eine Klasse kann mehr als ein Interface implementieren, es ist also möglich und angeraten, keine Mega-Interfaces zu schreiben, sondern für die jeweils beabsichtigten Funktionen ein möglichst kleines Interface vorzusehen.
Als gute Ideengeber würde ich mal die Interfaces der SPL nennen: ArrayAccess definiert vier Methoden, Iterator definiert fünf, und Countable sogar nur eine einzige. Die Kombination aller drei Interfaces erlaubt es, dass ein Objekt sich vollkommen wie ein Array verhalten kann, man aber volle Kontrolle über den Zugriff auf die internen Daten behält.
Ok, und noch ein Grund, warum eine DB-zugreifende Core-Klasse blöd ist: Der Controller hat keinerlei Veranlassung, auf die Datenbank zuzugreifen. Dafür hat er seine Models. Und die Models haben auch keine Veranlassung, die Datenbankzugriffe in sich selbst zu tragen, denn die sind dafür da, die in der DB gespeicherten Daten abzufragen (über eine DB-Klasse) und gegenüber dem Controller bzw. dem View passend zu repräsentieren.
Ok, das leuchtet mir ein. Ich schau mal, dass ich die DB-Klasse in meine Model-Klasse bringe und ob die Core-Klasse überhaupt nötig ist.
Gibt es denn auch Gründe _für_ eine Core-Klasse oder ist das allgemein eine schlechte Idee?
Aus Gründen der Testbarkeit mittels Unit-Tests wird man solche Gott-Klassen immer vermeiden wollen.
Das Problem liegt hierbei meist im benötigten Aufwand, um überhaupt einen Test schreiben zu können. Unit-Tests prüfen die Korrektheit einer Methode, indem die Methode in einer Testumgebung mit Testdaten aufgerufen wird, und das reale Ergebnis mit dem erwarteten Ergebnis verglichen wird. Das erwartete Ergebnis entsteht aus der Anforderung des Programmierers, was die Funktion eigentlich tun soll, und das reale Ergebnis ist das Produkt seiner Programmierkunst, welches der Erwartung hoffentlich nahe kommt.
Im Prinzip rufen Unit-Tests die Methoden "nur mal eben kurz auf". Und es ist für so einen Ansatz extrem hinderlich, wenn diese Methoden nur dann funktionieren, wenn die Testumgebung erst nach einer komplizierten Herstellung von Realität korrekt funktionieren. Viel schöner ist, wenn man den Test mit minimalem Aufwand (ideal: Null Aufwand) schreiben kann. Denn so ein Test ist in der Regel der erste Code, der die programmierte Methode aufruft und benutzt, und es ist außerdem viel einfacher, seine Methoden innerhalb eines eingerichteten und funktionsfähigen Testframeworks aufzurufen und zu testen, anstatt sich zum Ausprobieren immer erst ein kleines test.php zu schreiben und manuell im Browser zu gucken, ob alles funktioniert.
Denn das Problem bei diesem Vorgehen: Die test.php wird beim Ausprobiere beliebig umgewandelt, ohne die vorherigen Testaufrufe zu behalten und deren Ergebnisse zu speichern und später immer wieder maschinell zu prüfen. Das heißt: Wenn man was ändert, könnte man Fehler in etwas einbauen, was früher schon mal korrekt funktioniert hatten - aber weil das Testen dieses Verhaltens weggeworfen wurde, hat man das Problem nicht bemerkt. Und bis es dann soweit ist und das Gefühl von "hier kann irgendwas nicht ganz stimmen" aufkommt, ist die fehlerhafte Änderung schon längst vergessen worden, und die Fehlersuche ist umso aufwendiger.
Langer Rede kurzer Sinn: Wenn du nicht auf Unit-Testing stehst und das ganze für übertriebenen Overhead hältst, dann darfst du auch gern eine zentrale Core-Klasse schreiben und dich damit in dein prophezeihtes Unglück stürzen. ;) Aber auch ohne Unit-Tests ist das Abstandhalten von solchen Gott-Klassen eine förderliche Vorgehensweise, die Abhängigkeiten im Code vermeidet und ihn besser macht.
- Sven Rautenberg
Hallo!
Also ich habe die Core-Klasse komplett entfernt und instanziere die Datenbank-Klasse nun in der Model-Klasse von der alle anderen Models abgeleitet werden. Ich habe also nur noch Datenbankzugriff für die Models.
Du hast also sowas: [..]
Ja.
Da würde ich auch erstmal "suboptimal" rufen wollen.
Ich hab es fast geahnt ^^
Insofern: Du brauchst keine DB-Unabhängigkeit, du entscheidest dich am Anfang für eine existierende Datenbank und benutzt die.
Ok, getan.
Ein Interface wäre an dieser Stelle als Alternative für eine abstrakte, codelose Klasse eindeutig vorzuziehen. Und (das ist das I von SOLID) eine Klasse kann mehr als ein Interface implementieren, es ist also möglich und angeraten, keine Mega-Interfaces zu schreiben, sondern für die jeweils beabsichtigten Funktionen ein möglichst kleines Interface vorzusehen.
Du meinst also ich sollte ein Datenbank-Interface schreiben?
Ich muss gestehen, dass mir nicht ganz klar ist wozu Interfaces in PHP gut sind. Für mich sieht es so aus, als ob sie nur Funktionsnamen bereit stellen. Das erscheint mir aber überflüssig weil ich die Funktionen eh schreiben muss, das Interface also auch weglassen könnte.
Als gute Ideengeber würde ich mal die Interfaces der SPL nennen: ArrayAccess definiert vier Methoden, Iterator definiert fünf, und Countable sogar nur eine einzige. Die Kombination aller drei Interfaces erlaubt es, dass ein Objekt sich vollkommen wie ein Array verhalten kann, man aber volle Kontrolle über den Zugriff auf die internen Daten behält.
Siehe oben, mir ist nicht ganz klar wozu man da ein Interface braucht.
Deine Ausführung zu Tests muss ich mir nochmal durchden Kopf gehen lasse. Ich habe nicht ganz verstanden was du mir damit sagen wolltest.
Uwe
Moin!
Ein Interface wäre an dieser Stelle als Alternative für eine abstrakte, codelose Klasse eindeutig vorzuziehen. Und (das ist das I von SOLID) eine Klasse kann mehr als ein Interface implementieren, es ist also möglich und angeraten, keine Mega-Interfaces zu schreiben, sondern für die jeweils beabsichtigten Funktionen ein möglichst kleines Interface vorzusehen.
Du meinst also ich sollte ein Datenbank-Interface schreiben?
Ich muss gestehen, dass mir nicht ganz klar ist wozu Interfaces in PHP gut sind. Für mich sieht es so aus, als ob sie nur Funktionsnamen bereit stellen. Das erscheint mir aber überflüssig weil ich die Funktionen eh schreiben muss, das Interface also auch weglassen könnte.
Ich würde es mal so formulieren (und hole dabei etwas weiter aus):
Angenommen, du schreibst eine Klasse. Und dann schreibst du noch eine zweite Klasse, die fast genau dasselbe tut, nur in manchen Details halt anders. Diese zweite Klasse kopiert den Code der ersten meistens.
Dann ist die logische Konsequenz: Der gemeinsame Code kommt in eine Elternklasse, und die jeweiligen Abweichungen jeder einzelnen Klasse bleiben halt dort. Dabei ist möglichst viel gemeinsamer Code in die Elternklasse zu packen.
Und wenn die Elternklasse für sich allein nicht instanziiert werden kann, weil ihr zur Funktionsfähigkeit eben genau jene Methoden fehlen, die in den Kindklassen die Details regeln, dann macht man die Elternklasse abstrakt, und auch ein paar der jeweiligen Methoden.
Im Typehinting von anderen Methoden kann man jetzt aber diese abstrakte Elternklasse als geforderten Typ des Parameters angeben. Die Konsequenz ist, dass man dort ein Objekt entweder des einen oder anderen Kindes der Elternklasse übergibt, denn die Elternklasse ist ja abstrakt.
Nun kann es aber vorkommen, dass man zwar gern ein Typehinting für Objekte bestimmter "Abstammung" hätte, es allerdings keinen Sinn ergibt, eine abstrakte Klasse zu bauen, weil es keinen gemeinsamen Code gibt. Für solch einen Fall kann man mit einem Interface ebenfalls sicherstellen, dass die Objekte, die das Interface instanziieren, alle damit definierten Methoden implementieren - aber eben ohne gemeinsamen Code. Nur mal als Anregung, warum Klassen, die dasselbe tun, nicht denselben Code verwenden könnten: Die DB-Klassen für MySQL und PostgreSQL benutzen jeweils die nativen Funktionen - da kann man kaum identischen Code finden und auslagern.
Interfaces erlauben also, Klassen gleicher Funktion zu identifizieren, ohne dass ihr Code eine gemeinsame Abstammung haben muss aufgrund vererbten Codes. Man kann Interfaces sogar rein als Kennzeichnung ohne jegliche geforderte Methoden schreiben und implementieren, um nur diesen Identifizierungseffekt zu benutzen, allerdings würde ich persönlich das nur im Ausnahmefall wirklich tun. Ich finde es beispielsweise wenig sinnvoll, dass jede existierende Klasse mit Chance auf Duplizität automatisch gleich so ein Tagging-Interface implementiert, in dem keine Methoden definiert werden. Das ist OOP-Overkill.
Als gute Ideengeber würde ich mal die Interfaces der SPL nennen: ArrayAccess definiert vier Methoden, Iterator definiert fünf, und Countable sogar nur eine einzige. Die Kombination aller drei Interfaces erlaubt es, dass ein Objekt sich vollkommen wie ein Array verhalten kann, man aber volle Kontrolle über den Zugriff auf die internen Daten behält.
Siehe oben, mir ist nicht ganz klar wozu man da ein Interface braucht.
Diese Interfaces haben, im Gegensatz zu denen, die du selbst definieren kannst, noch eine Spezialbedeutung:
Implementierst du das Interface "ArrayAccess", dann kannst du mit der Array-Schreibweise auf die von dir gewünschten Werte deines Objekts zugreifen:
class accessTest implements ArrayAccess {
// Hier die Methoden
}
$einArray = new accessTest();
$einArray['name'] = 'wert';
echo $einArray['name'];
echo get_class($einArray); // ergibt "accessTest"
Diese Zugriffsform wird von PHP intern realisiert, indem die Array-Schreibweise in Aufrufe der vom Interface geforderten Methoden gemappt wird. Gleiches gilt für die Implementierung von "Iterator" - damit kannst du ein Objekt mittels foreach() durchgehen. Und "Countable" erlaubt, die Funktion count() auf das Objekt anzuwenden. Alle drei Interfaces realisieren Teilaspekte von dem, was man mit einem echten Array machen kann, aber wie erwähnt: Es ist ein Objekt, man kann eigenen Code ausführen, um Einfluss zu nehmen.
Deine Ausführung zu Tests muss ich mir nochmal durchden Kopf gehen lasse. Ich habe nicht ganz verstanden was du mir damit sagen wolltest.
Da möchte ich dich einfach mal weiterverweisen: http://www.lastcraft.com/simple_test.php und http://www.lastcraft.com/first_test_tutorial.php sind die nach meiner Meinung sehr gut geschriebenen Seiten, die als Einführung in Unittests gelten dürften.
Einen Nachteil haben diese Seiten: Das dort erwähnte Test-Framework "SimpleTest" ist leider hoffnungslos veraltet, es supportet noch PHP 4 und nutzt deshalb selbst noch keine Features von PHP 5. Der Platzhirsch an dieser Stelle ist eindeutig "PHPUnit". Aber die Prinzipien der beiden Frameworks sind absolut vergleichbar, deshalb ist es keine vertane Zeit, wenn du dir die zwei Links mal zu Gemüte führst.
- Sven Rautenberg
Hallo!
Es tut mir leid gestehen zu müssen, dass ich den Sinn von Interfaces immernoch nicht verstanden habe. Bleiben wir mal bei ArrayAccess. Dort werden 4 abstrakte Methoden definiert, dann wird eine Klasse erstellt welche die 4 Methoden nochmal definiert inkl. auszuführenden Code der Methoden. Wenn ich das Interface jetzt weglasse funktioniert alles genauso gut. Ich versteh einfach nicht wozu das Interface und überhaupt wozu nur "leere" Methoden.
Du sagst es dient der Identifikation von Klassen die grob umrissen gleiche Aufgaben erledigen. Wozu muss ich die identifizieren können? Irgendwie will es nicht in meinen Kopf. Gibt es keine Beispiele wo ich zwingend ein Interface brauch?
Deine Ausführung zu Tests muss ich mir nochmal durchden Kopf gehen lasse. Ich habe nicht ganz verstanden was du mir damit sagen wolltest.
Da möchte ich dich einfach mal weiterverweisen: http://www.lastcraft.com/simple_test.php und http://www.lastcraft.com/first_test_tutorial.php sind die nach meiner Meinung sehr gut geschriebenen Seiten, die als Einführung in Unittests gelten dürften.
Einen Nachteil haben diese Seiten: Das dort erwähnte Test-Framework "SimpleTest" ist leider hoffnungslos veraltet, es supportet noch PHP 4 und nutzt deshalb selbst noch keine Features von PHP 5. Der Platzhirsch an dieser Stelle ist eindeutig "PHPUnit". Aber die Prinzipien der beiden Frameworks sind absolut vergleichbar, deshalb ist es keine vertane Zeit, wenn du dir die zwei Links mal zu Gemüte führst.
Frameworks? :( Ich hasse Frameworks. Ich verbringe aus Erfahrung normalerweise mehr Zeit damit mich in ein Framework einzuarbeiten als es gleich selbst zu erledigen. Ich werde mir die Links natürlich trotzdem erstmal anschauen. Danke!
Uwe
Hi!
Es tut mir leid gestehen zu müssen, dass ich den Sinn von Interfaces immernoch nicht verstanden habe. Bleiben wir mal bei ArrayAccess. Dort werden 4 abstrakte Methoden definiert,
Das "abstract" musst du dir wegdenken, das ist da falsch. Interfaces deklarieren immer abstrakte Methoden (also solche ohne Körper - oder anders gesagt nur die Methodensignatur) und das wird nicht weiter gekennzeichnet.
dann wird eine Klasse erstellt welche die 4 Methoden nochmal definiert inkl. auszuführenden Code der Methoden. Wenn ich das Interface jetzt weglasse funktioniert alles genauso gut. Ich versteh einfach nicht wozu das Interface und überhaupt wozu nur "leere" Methoden.
Ein Interface ist eine Art Versicherung. Es garantiert, dass die im Interface deklarierten Methoden vorhanden sind, wenn jemand das Interface implementiert. Für PHP ist das nicht zwingend notwendig, denn man kann bei einem Funktionsaufruf beispielsweise einen Parameter haben wollen, ohne einen Typ anzugeben.
function foo($bar) {
$bar->qux();
}
Die Funktion will eine Methode qux() aufrufen. Wenn sie im übergebenen Objekt vorhanden ist, ist alles bestens.
function foo(IQux $bar) {
$bar->qux();
}
Diese Funktion hingegen verwendet Type Hinting. Das heißt, sie will unbedingt etwas vom Typ IQux übergeben haben und PHP überwacht das. Alle übergebenen Objekte müssen nun zwingend von einer Klasse sein, die IQux implementiert hat.
IQux muss kein Interface sein, es kann auch eine Klasse sein. Aber wenn es eine Klasse ist, müssen alle zu übergebenden Objekte von dieser Klasse oder von deren Nachfolgern sein. Auch eine Klasse selbst oder die davon abstammenden garantieren das Vorhandensein von Methoden. Es ist aber nicht immer sinnvoll, solche Verwandschaftsbeziehungen aufzubauen. Hier kommen Interfaces ins Spiel, die es erlauben, Garantien ohne Verwandschaft zu geben, denn Klassen, die dasselbe Interface implementieren müssen nicht verwandt sein.
Für PHP ist das alles nicht weiter tragisch, wenn man sich selbst disziplinieren kann und immer nur solche Objekte übergibt, die die dann aufzurufenden Methoden enthalten. Wesentlich wichtiger sind Interfaces bei typsicheren Sprachen, wo also stets Typen angegeben werden müssen. Gott-Klassen sind nicht so toll, also gibt man als geforderten Typ ein Interface an und kann dann alles mögliche übergeben, Hauptsache, es hat das Interface implementiert.
Du sagst es dient der Identifikation von Klassen die grob umrissen gleiche Aufgaben erledigen. Wozu muss ich die identifizieren können? Irgendwie will es nicht in meinen Kopf. Gibt es keine Beispiele wo ich zwingend ein Interface brauch?
Bei PHP nicht, wenn du nicht mit Type Hinting, einer Art Typsicherung, arbeiten willst, oder etwas verwendest, das damit arbeiten will. Sven gab ja schon Beispiele. Wenn du mit foreach über ein Objekt iterieren willst, muss dessen Klasse das Interface Iterator oder IteratorAggregate implementiert haben. Dann weiß foreach, dass die darin enthaltenen Methoden da sind. Es muss nicht blind darauf zugreifen, sondern kann gleich zu Beginn sagen: kein Interface, keine Iteration. ArrayAccess ist ähnlich. Ohne Interface müsste eine Klasse von einer mehr oder weniger abstrakten Array-Klasse abgeleitet sein, damit man einen Array-Zugriff realisieren kann. Mit Interface kann sie selbständig sein und muss nur die vier im Interface vereinbarten Methoden mit Leben gefüllt haben.
Lo!
Moin!
Es tut mir leid gestehen zu müssen, dass ich den Sinn von Interfaces immernoch nicht verstanden habe. Bleiben wir mal bei ArrayAccess. Dort werden 4 abstrakte Methoden definiert, dann wird eine Klasse erstellt welche die 4 Methoden nochmal definiert inkl. auszuführenden Code der Methoden. Wenn ich das Interface jetzt weglasse funktioniert alles genauso gut. Ich versteh einfach nicht wozu das Interface und überhaupt wozu nur "leere" Methoden.
Ok, deine Äußerung lässt mich erkennen, dass ich ein schlechtes Beispiel gewählt habe.
Deshalb in Ergänzung zu dedlfix:
Das konkret vorliegende Interface ArrayAccess wendest du ja lediglich an, d.h. deine Klasse implementiert die dadurch geforderten Funktionen.
WENN du es implementierst, dann kannst du sowas machen:
$dataObject = new KlasseDieArrayAccessImplementiert();
$dataObject[0] = "foo";
$dataObject[1] = "bar";
echo $dataObject[0]; // Schreibt: "Magic: foo", um zu demonstrieren, dass es kein Array ist.
Wenn ArrayAccess nicht implementiert ist, dann funktioniert das so nicht.
PHP prüft bei Array-Zugriffsschreibweise intern, ob die Variable ein Array ist, oder ein Objekt, welches ArrayAccess implementiert hat. In letzterem Fall ruft es die implementierten Methoden auf, um festzustellen, ob der gewünschte Array-Index verfügbar ist, und fragt den Wert ab - Schreibzugriffe analog.
Dieses Beispiel ist schlecht gewählt gewesen, weil es lediglich die nützliche Anwendung eines hartcodierten, mit PHP-Sonderfunktionalität ausgestatteten Interfaces demonstriert, aber nicht den Nutzen von selbstgeschriebenen Interfaces, die leider ohne solche Sonderfunktionen auskommen müssen.
Du sagst es dient der Identifikation von Klassen die grob umrissen gleiche Aufgaben erledigen. Wozu muss ich die identifizieren können? Irgendwie will es nicht in meinen Kopf. Gibt es keine Beispiele wo ich zwingend ein Interface brauch?
Nein, zwingend ist in der Programmierung nichts, weil man alles auch anders lösen könnte. Nur zweckmäßig ist es nicht unbedingt.
Einen Nachteil haben diese Seiten: Das dort erwähnte Test-Framework "SimpleTest" ist leider hoffnungslos veraltet, es supportet noch PHP 4 und nutzt deshalb selbst noch keine Features von PHP 5. Der Platzhirsch an dieser Stelle ist eindeutig "PHPUnit". Aber die Prinzipien der beiden Frameworks sind absolut vergleichbar, deshalb ist es keine vertane Zeit, wenn du dir die zwei Links mal zu Gemüte führst.
Frameworks? :( Ich hasse Frameworks. Ich verbringe aus Erfahrung normalerweise mehr Zeit damit mich in ein Framework einzuarbeiten als es gleich selbst zu erledigen. Ich werde mir die Links natürlich trotzdem erstmal anschauen. Danke!
Mit dieser Argumentation müsstest du den Weizen für dein Brot auch selbst anbauen, weil zum Bäcker fahren und dort Kaufen zu kompliziert wäre. ;)
- Sven Rautenberg
Hi!
Ich habe 3 Klassen, 2 davon erweitern die erste Klasse. Ich möchte von einer der beiden Klassen auf eine Variable der anderen zugreifen wobei der Inhalt dieser in der parent-Klasse definiert wird.
Das sieht mir nach einem schweren Design-Fehler aus. Es gibt sicher eine bessere Lösung für das eigentliche Problem ...
Wenn du auf Klassen-Ebene hin- und hergreifen willst, geht das nur mit Klassenvariablen, also solchen, die in der Klasse als static definiert sind. Dazu muss die eine Klasse die andere kennen, was eine unschöne Abhängigkeit ergibt. Und mehrere Objekte greifen immer auf die selbe Variable zu, weil es eben eine Klassenvariable ist.
Auf Objekt-Ebene hingegen kann ein Objekt nur über eine Instanz eines anderen Objekts auf dessen Mitglieder zugreifen. Dabei kommt es drauf an, ob es besser ist, direkt zuzugreifen oder eine Zugriffsmethode zu verwenden. Mit einer Zugriffsmethode macht man in der Regel nichts verkehrt.
class bla extends foo {
public function test() {
global $a;
Damit wird $a ein Verweis auf den globalen Variablenspeicher.
class start extends foo {
public function run() {
$a = $this->bar(); // $a soll in bla->test() verfügbar sein
Hier ist $a eine lokale Variable, die außerhalb der Funktion/Methode nicht bekannt ist.
Der Grund ist, dass ich $a in der start-Klasse durch andere Klassen bei bedarf bearbeiten können will.
Wenn ich eine Variable ausserhalb aller Klassen definiere und in einer Klasse dann
global $Variable;
schreibe, dann habe ich auch Zugriff darauf. Wo liegt hier mein Fehler?
Das Schlüsselwort global wirkt sich nur auf die Sichtbarkeit der angegebenen Variablen im den aktuellen Scope aus. Die Variable wird dadurch nicht generell superglobal (wie $_GET/$_POST).
Lo!