Hallo zusammen,
zum Einstieg ins Forum habe ich gleich eine Frage zum Umgang mit Klassen in PHP, bei deren Thema ich im Moment keinen Ansatz finde.
Die Ableitungen von "Service" im unten gezeigten Code nutzen dabei immer ein Objekt, welches von "Data" abgeleitet wurde. Eine Klasse kann dabei von verschiedenen Service-Implementierungen genutzt werden. setDataForPerson() befindet sich daher in "Service".
Via Typehint erzwingt setData() ein Objekt vom Typ Data. Bei getData() weiß man also in der Folge eigentlich auch nur, dass ein Objekt diesen Typs (oder null) zurück kommt, entsprechend kennzeichnet dies auch @return.
Folgerichtig beschwert sich dann meine IDE (PhpStorm) darüber, dass in setDataForPerson() das Feld "field_person_name" beim Resultat von "getData()" undefiniert sei. Zwar führt PHP das Programm schmerzfrei so aus, wie es "gedacht" ist, aber es handelt sich hier wohl um keine wirklich saubere Lösung.
Ich habe den Beispiel-Code stark vereinfacht von einem konkreten Fall hergeleitet, hoffe aber, dass man das grundsätzliche Problem erkennen kann.
Hat vielleicht jemand einen Tipp zur Hand, wie man die Klassen günstiger strukturieren kann?
Eine Klasse zwischen Service und Service1/Service2 zu hängen, die setData()/getData() mit dem konkreten Typ implementiert und setDataForPerson() dort unterzubringen, ist im konkreten Fall keine Option, da abseits des Beispiel-Codes mehrere Ableitungen von Data vorhanden sind, bei denen sich Datenfelder überschneiden, sprich: in Service1 würde bspw. setDataForPerson() und setDataForCar() (im Code nicht gezeigt) aufgerufen, während bspw. in Service4 setDataForPerson() und setDataForXY() aufgerufen werden könnten. Es würde jeweils in den genannten Setter-Methoden (die jeweils nicht nur eines, sondern eine Anzahl Felder setzen) ein entsprechend passendes Data-Objekt notwendig sein.
Problematisch ist hierbei, dass die Data-Klassen (bislang) als flache Klassen mit einer handvoll Feldern implementiert sind, was gemacht wurde, damit diese als einfache Datenhaltungs-Klassen an anderer Stelle zur Verarbeitung der enthaltenen Daten übergeben werden können.
Eine Option wäre vielleicht, setDataForPerson() in die entsprechende(n) Implementierung(en) von Data umzuziehen, in Data eine abstrakte Methode der Art setDataForAllFields() vorzugeben, die dann in den konkreten Data-Implementierungen alle notwendigen Methoden wie setDataForPerson() aufruft. Problematisch dabei fände ich allerdings, dass damit die Data-Klassen wiederum zu viel Wissen über die eingelieferten Daten haben müssen. An der Stelle wo derzeit setDataForPerson('test') aufgerufen wird, müsste dann die gesamte Quell-Objektstruktur aus der die Daten (hier nur String 'test') stammen an setDataForAllFields() übergeben werden und in der Data-Implementierung die Logik vorhanden sein, um die Daten zu erhalten.
Vielen Dank im Voraus
mdo
zum Einstieg ins Forum habe ich gleich eine Frage zum Umgang mit Klassen in PHP, bei deren Thema ich im Moment keinen Ansatz finde.
Die Ableitungen von "Service" im unten gezeigten Code nutzen dabei immer ein Objekt, welches von "Data" abgeleitet wurde. Eine Klasse kann dabei von verschiedenen Service-Implementierungen genutzt werden. setDataForPerson() befindet sich daher in "Service".
Via Typehint erzwingt setData() ein Objekt vom Typ Data. Bei getData() weiß man also in der Folge eigentlich auch nur, dass ein Objekt diesen Typs (oder null) zurück kommt, entsprechend kennzeichnet dies auch @return.
Folgerichtig beschwert sich dann meine IDE (PhpStorm) darüber, dass in setDataForPerson() das Feld "field_person_name" beim Resultat von "getData()" undefiniert sei. Zwar führt PHP das Programm schmerzfrei so aus, wie es "gedacht" ist, aber es handelt sich hier wohl um keine wirklich saubere Lösung.
Ich habe den Beispiel-Code stark vereinfacht von einem konkreten Fall hergeleitet, hoffe aber, dass man das grundsätzliche Problem erkennen kann.
Hat vielleicht jemand einen Tipp zur Hand, wie man die Klassen günstiger strukturieren kann?
Eine Klasse zwischen Service und Service1/Service2 zu hängen, die setData()/getData() mit dem konkreten Typ implementiert und setDataForPerson() dort unterzubringen, ist im konkreten Fall keine Option, da abseits des Beispiel-Codes mehrere Ableitungen von Data vorhanden sind, bei denen sich Datenfelder überschneiden, sprich: in Service1 würde bspw. setDataForPerson() und setDataForCar() (im Code nicht gezeigt) aufgerufen, während bspw. in Service4 setDataForPerson() und setDataForXY() aufgerufen werden könnten. Es würde jeweils in den genannten Setter-Methoden (die jeweils nicht nur eines, sondern eine Anzahl Felder setzen) ein entsprechend passendes Data-Objekt notwendig sein.
Problematisch ist hierbei, dass die Data-Klassen (bislang) als flache Klassen mit einer handvoll Feldern implementiert sind, was gemacht wurde, damit diese als einfache Datenhaltungs-Klassen an anderer Stelle zur Verarbeitung der enthaltenen Daten übergeben werden können.
Eine Option wäre vielleicht, setDataForPerson() in die entsprechende(n) Implementierung(en) von Data umzuziehen, in Data eine abstrakte Methode der Art setDataForAllFields() vorzugeben, die dann in den konkreten Data-Implementierungen alle notwendigen Methoden wie setDataForPerson() aufruft. Problematisch dabei fände ich allerdings, dass damit die Data-Klassen wiederum zu viel Wissen über die eingelieferten Daten haben müssen. An der Stelle wo derzeit setDataForPerson('test') aufgerufen wird, müsste dann die gesamte Quell-Objektstruktur aus der die Daten (hier nur String 'test') stammen an setDataForAllFields() übergeben werden und in der Data-Implementierung die Logik vorhanden sein, um die Daten zu erhalten.
Vielen Dank im Voraus
mdo
PHP-Code:
<?php
error_reporting(E_STRICT);
abstract class Service
{
private $data;
/**
* @param Data $data
* @return void
*/
public function setData(Data $data)
{
$this->data = $data;
}
/**
* @return Data
*/
public function getData()
{
return $this->data;
}
/**
* @param string $value
* @return void
*/
public function setDataForPerson($value)
{
$this->getData()->field_person_name = $value;
}
}
class Service1 extends Service
{
public function __construct()
{
$this->setData(new Data1());
}
public function doit()
{
$this->setDataForPerson('test');
}
}
class Service2 extends Service
{
public function __construct()
{
$this->setData(new Data1());
}
public function doit()
{
$this->setDataForPerson('test2');
}
}
class Service3 extends Service
{
public function __construct()
{
$this->setData(new Data2());
}
public function doit()
{
// do some other stuff
}
}
abstract class Data
{
public $field_data;
}
class Data1 extends Data
{
public $field_person_name;
}
class Data1a extends Data1
{
public $field_person_birth;
}
class Data2 extends Data
{
public $field_data2;
}
$x = new Service1();
$x->doit();
var_dump($x);
Kommentar