php.de

Zurück   php.de > Webentwicklung > Software-Design

Software-Design Diskussionen auf Profi-Niveau: PHP Lösungen auf konzeptioneller Ebene

Antwort
 
LinkBack Themen-Optionen Bewertung: Bewertung: 1 Stimmen, 5,00 durchschnittlich.
Alt 25.05.2011, 09:41  
mdo
Neuer Benutzer
 
Registriert seit: 25.05.2011
Beiträge: 5
PHP-Kenntnisse:
Fortgeschritten
mdo befindet sich auf einem aufstrebenden Ast
Standard Klassen-Design

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


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);
mdo ist offline   Mit Zitat antworten
Sponsor Mitteilung
PHP Code Flüsterer

Registriert seit: 21.08.2005
Beiträge: 4682
PHP-Kenntnisse:
Fortgeschritten

Alt 25.05.2011, 09:54  
Erfahrener Benutzer
 
Registriert seit: 11.04.2011
Beiträge: 260
PHP-Kenntnisse:
Fortgeschritten
lcrash wird schon bald berühmt werden
Standard

Service, Service2, Service3 scheint mir schon grober Quatsch zu sein. Warum nicht eine Klasse Service?
lcrash ist offline   Mit Zitat antworten
Alt 25.05.2011, 10:00  
mdo
Neuer Benutzer
 
Registriert seit: 25.05.2011
Beiträge: 5
PHP-Kenntnisse:
Fortgeschritten
mdo befindet sich auf einem aufstrebenden Ast
Standard

Zitat:
Zitat von lcrash Beitrag anzeigen
Service, Service2, Service3 scheint mir schon grober Quatsch zu sein. Warum nicht eine Klasse Service?
Weil die Implementierungen von doit() die umfangreiche Funktionalität einer Art Webservice umsetzen und dort nicht nur wie in dem vereinfachten Beispiel gezeigt ein Setter aufgerufen wird.
mdo ist offline   Mit Zitat antworten
Alt 25.05.2011, 10:19  
Erfahrener Benutzer
 
Registriert seit: 23.08.2010
Beiträge: 495
PHP-Kenntnisse:
Fortgeschritten
mimomamu sorgt für eine eindrucksvolle Atmosphäremimomamu sorgt für eine eindrucksvolle Atmosphäre
Standard

Die Anweisung $this->getData()->field_person_name = $value gehört in eine Klasse, deren getData-Methode ein Objekt vom Typ Data1 liefert.
__________________
Meinungen, die ich geäußert habe, sind nicht notwendigerweise meine eigenen. Abweichungen von der deutschen Rechtschreibung unterliegen dem Urheberrecht, dürfen aber unter den Bedingungen von verwendet werden
mimomamu ist offline   Mit Zitat antworten
Alt 25.05.2011, 10:23  
fab
Erfahrener Benutzer
 
Benutzerbild von fab
 
Registriert seit: 28.07.2010
Beiträge: 2.308
PHP-Kenntnisse:
Fortgeschritten
fab ist ein Lichtblickfab ist ein Lichtblickfab ist ein Lichtblickfab ist ein Lichtblickfab ist ein Lichtblick
Standard

Ich bin mir sicher dass das besser geht aber für Tipps zum grundlegenden Klassendesign ist mir das zu abstrakt und du scheinst ja auch schon recht festgelegt zu sein.

Deshalb ist folgender Vorschlag auch eher Symptombehandlung, löst aber oberflächlich dein Problem
PHP-Code:
    /**
     * @param string $value
     * @return void
     */
    
public function setDataForPerson($value)
    {
        
$data $this->getData();
        if (
$data instanceof Data1))
        {
                
// hier sollte dann auch deine IDE Bescheid wissen
                
$data->field_person_name $value;
        }
        else
        {
                throw 
BadMethodCallException('Data is no instance of Person (Data1)');
        }
    } 
fab ist offline   Mit Zitat antworten
Alt 25.05.2011, 14:06  
mdo
Neuer Benutzer
 
Registriert seit: 25.05.2011
Beiträge: 5
PHP-Kenntnisse:
Fortgeschritten
mdo befindet sich auf einem aufstrebenden Ast
Standard

Zitat:
Zitat von mimomamu Beitrag anzeigen
Die Anweisung $this->getData()->field_person_name = $value gehört in eine Klasse, deren getData-Methode ein Objekt vom Typ Data1 liefert.
Eigentlich war mir das natürlich vorher schon klar und so wie ich schrieb, suchte ich nach einem Weg das sauber aufzulösen. Aber Du hast natürlich Recht, man sollte es ganz simpel so herum anpacken, die Anweisung entsprechend verschieben und dann schauen, wie man die Dinge zusammen bringt ...


Zitat:
Zitat von fab Beitrag anzeigen
Ich bin mir sicher dass das besser geht aber für Tipps zum grundlegenden Klassendesign ist mir das zu abstrakt und du scheinst ja auch schon recht festgelegt zu sein.

Deshalb ist folgender Vorschlag auch eher Symptombehandlung, löst aber oberflächlich dein Problem
PHP-Code:
    /**
     * @param string $value
     * @return void
     */
    
public function setDataForPerson($value)
    {
        
$data $this->getData();
        if (
$data instanceof Data1))
        {
                
// hier sollte dann auch deine IDE Bescheid wissen
                
$data->field_person_name $value;
        }
        else
        {
                throw 
BadMethodCallException('Data is no instance of Person (Data1)');
        }
    } 

Wie Du schon schriebst eher eine Symptombehandlung und die wollte ich tatsächlich vermeiden.

Ich habe es nun vorerst so gelöst, wie ich schon angedeutet hatte. Übertragen auf mein Code-Beispiel in Stichpunkten:

* setDataForPerson() und Konsorten werden aus den Service-Klassen in die Data-Klassen geschoben, wo auch die zugehörigen Daten-Felder bearbeitet werden (das verringert dann auch den Data-Class-Smell an dieser Stelle)
* in Service gibt es eine Methode setData() die eine Delegierung an setDataForAllFields(MyClass $myClass) in den Data-Implementierungen vornimmt und Exceptions handhabt
* im Ganzen sorgt das noch für weitere Entkopplung, da bislang das Exception-Handling, das Einfluss auf den Status des Webservice-Aufrufs nimmt, unmittelbar bei setDataForPerson() untergebracht war
* dafür wurde noch eine passende Exception-Klasse nötig, um entsprechende Fehler aus Data an Service zurück zu kommunizieren
* einziger Nachteil ist soweit jetzt, dass setDataForAllFields() Kenntnisse über die Klasse MyClass haben muss, um die Daten zu extrahieren

So bleibt zwar vorerst eine unschöne Stelle, aber zumindest ist die Unsicherheit bezüglich des Objekt-Typs eliminiert.
mdo ist offline   Mit Zitat antworten
Alt 25.05.2011, 14:16  
moderatives Dielektrikum
 
Benutzerbild von nikosch
 
Registriert seit: 21.05.2008
Beiträge: 35.994
PHP-Kenntnisse:
Fortgeschritten
nikosch hat eine strahlende Zukunftnikosch hat eine strahlende Zukunftnikosch hat eine strahlende Zukunftnikosch hat eine strahlende Zukunftnikosch hat eine strahlende Zukunftnikosch hat eine strahlende Zukunftnikosch hat eine strahlende Zukunftnikosch hat eine strahlende Zukunftnikosch hat eine strahlende Zukunftnikosch hat eine strahlende Zukunftnikosch hat eine strahlende Zukunft
Standard

Schau Dir mal das Strategie-Pattern an, vielleicht bringt Dich das weiter. Kann Dein Problem gerade nicht nachvollziehen.
__________________
--
One pixel is still too big. Please make it smaller. ASAP.

Initiative Mittelstand.
Die wichtigste Gestaltungsregel im Screendesign ist Pi mal Daumen des Arbeitgebers.
--
nikosch ist offline   Mit Zitat antworten
Alt 25.05.2011, 17:25  
Erfahrener Benutzer
 
Registriert seit: 02.09.2009
Beiträge: 1.020
PHP-Kenntnisse:
Fortgeschritten
mquadrat befindet sich auf einem aufstrebenden Ast
Standard

Prinzipiell ist es so, dass du auf die Eigenschaft an dieser Stelle gar nicht zugreifen dürftest. Wenn du dort ein Data-Objekt erwartest, darfst du auch nur auf Eigenschaften und Methoden von Data zugreifen. Data1 ist tabu. Sonst fliegt dir nämlich dein Programm um die Ohren, wenn du die Methode auf einer Data2 Instanz aufrufst (was ja legitim wäre).

Eine Lösungsmöglichkeit zu nennen fällt schwer ohne die konkrete Umgebung und den Sinn und Zweck zu kennen.

Die simpelste Lösung wäre wohl etwas wie
PHP-Code:
$data->setField('person_name') = $value 
Davon ist aber im Allgemeinen abzuraten.
__________________
Wir suchen PHP Entwickler (Vollzeit) im Raum Darmstadt / Rhein-Main. Infos via E-Mail mueller@new-frontiers.de
mquadrat ist offline   Mit Zitat antworten
Alt 25.05.2011, 21:44  
Moderator und Wett-König
 
Benutzerbild von dr.e.
 
Registriert seit: 21.05.2008
Beiträge: 3.657
PHP-Kenntnisse:
Fortgeschritten
dr.e. ist ein Lichtblickdr.e. ist ein Lichtblickdr.e. ist ein Lichtblickdr.e. ist ein Lichtblickdr.e. ist ein Lichtblickdr.e. ist ein Lichtblick
dr.e. eine Nachricht über Skype™ schicken
Standard

Kannst du mir mal kurz einen Abriss geben, was du genau mit dem Klassendesign bezweckst? Ich würde gerne vermeiden über if's und instanceof's diskutieren zu müssen, wenn es einfach durch Inversion der Abhängigkeiten oder Null-Pattern zu lösen ist - vielleicht auch durch Strategy. Wobei ich vor der Anwendung von letzterem eher noch über DI auf Basis eines Service-Interfaces nachdenken würde.
__________________
Viele Grüße,
Dr.E.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1. Think about software design before you start to write code!
2. Discuss and review it together with experts!
3. Choose good tools (-> Adventure PHP Framework (APF))!
4. Write clean and reusable software only!
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
dr.e. ist offline   Mit Zitat antworten
Alt 25.05.2011, 23:36  
mdo
Neuer Benutzer
 
Registriert seit: 25.05.2011
Beiträge: 5
PHP-Kenntnisse:
Fortgeschritten
mdo befindet sich auf einem aufstrebenden Ast
Standard

Zitat:
Zitat von dr.e. Beitrag anzeigen
Kannst du mir mal kurz einen Abriss geben, was du genau mit dem Klassendesign bezweckst? Ich würde gerne vermeiden über if's und instanceof's diskutieren zu müssen, wenn es einfach durch Inversion der Abhängigkeiten oder Null-Pattern zu lösen ist - vielleicht auch durch Strategy. Wobei ich vor der Anwendung von letzterem eher noch über DI auf Basis eines Service-Interfaces nachdenken würde.

Im Grunde geht es um folgendes Szenario. Ich habe einen Webservice, der einen Proxy zu einem Backend-Dienst darstellt. Der WebService implementiert verschiedene Services (Service1, Service2, ...). Diese rufen beim Backend-Dienst wiederum Services auf, die mit sich ähnelden XML-Nachrichten angesprochen werden.

Die Data-Klassen dienen dazu, die Daten aufzunehmen, die der Webservice erhält, damit eine XML-Factory-Klasse den entsprechenden Request bauen kann. Die Data-Implementierungen spiegeln also die XML-Stuktur der Backend-Requests als Objekt wieder. "Data" an sich enthält dabei gemeinsame Elemente, die allen erbenden Data-Klassen gemein sind. In der tatsächlichen Implementierung handelt es sich nicht um primitive Felder, sondern die einzelnen Data-Klassen-Felder sind wiederum Objekte, die auch Ihre Inhalte validieren können.

Ein Übergeben der Eingabedaten ohne den Umweg der Data-Klassen halten ich für wenig sinnvoll, da zum einen halt die Data-Klassen die Request-Formate beim Backend widerspiegeln und zum anderen spätestens in der XML-Factory zu viel Logik wäre, die sich um die Beschaffung der Daten kümmern müsste.

Die XML-Factory orientiert sich beim Zusammenstellen des XML-Requests an der vorhandenen Struktur des jeweiligen Data-Objekts.

Übertragen auf mein Code-Beispiel bedeutet dies (in meiner bereits geänderten Code-Fassung) nun, dass eine Service-Klasse in doit() zuerst via setDataForAllFields() anstößt, dass das konkrete Data-Objekt "sich selbst" mit Daten füllt. Weiter übergibt doit() (bzw. eine weitere Methode) dann das gefüllte Data-Objekt an die XML-Factory um mit dem Ergebnis den Backend-Dienst anzusprechen.
mdo ist offline   Mit Zitat antworten
Antwort


Themen-Optionen
Thema bewerten
Thema bewerten:

Forumregeln
Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are an
Gehe zu

Ähnliche Themen
Thema Autor Forum Antworten Letzter Beitrag
[Erledigt] OOP - Klassen richtig Designen, aber wie? Yamo PHP Einsteiger 8 01.01.2011 12:03
Wozu brauche ich eigentlich eine abstrakte Klasse? echo PHP Tipps 2010 28 24.11.2010 17:10
[Erledigt] Design von Klassen für Datenbankabfragen Sirke Software-Design 10 17.06.2010 12:40
Mehrere Klassen verknüpfen BlackJack01090 Software-Design 9 26.05.2009 20:43
Modulare Programmierung - Klassen nur in Klassen Gültig iXtends Software-Design 6 26.05.2009 15:37
Design und Code Trennen TeazY PHP Tipps 2008 29 21.05.2008 12:08
Fragen zu Klassen Kein Genie PHP Tipps 2006 3 08.05.2006 11:25
Sessions an includete Klassen übergeben Dr_Enquinox PHP Tipps 2006 4 29.04.2006 10:25
mit html klassen definieren und in php ausgeben PHP-Fortgeschrittene 4 16.01.2006 10:31
Design Beitragsarchiv 26 04.06.2005 20:56
klassen und methoden anderer klassen in methoden.... PHP Tipps 2005 17 25.05.2005 09:46
Klassen PHP Tipps 2005 5 09.04.2005 18:51
Klassen in Klassen verwenden PHP Tipps 2005 5 23.02.2005 14:08

Besucher kamen über folgende Suchanfragen bei Google auf diese Seite
php klassendesign, klassendesign, php oop klassendesign, php objekt datensätze, software design klassenbeschreibung in oo, klassen n zu m, softwaredesign service klassen, klassendesign php, phpstorm

Alle Zeitangaben in WEZ +2. Es ist jetzt 01:35 Uhr.




Powered by vBulletin® Version 3.7.2 (Deutsch)
Copyright ©2000 - 2012, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO 3.2.0
Aprilia-Forum, Aquaristik-Forum, Liebeskummer-Forum, Zierfisch-Forum, Geizkragen-Forum