Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
3-Schichten-Design?? [harte vs. weiche (Daten-)Objekte / OOP vs. ActiveRecords]
TripleX
myCSharp.de - Member

Avatar #avatar-3071.jpg


Dabei seit:
Beiträge: 338
Herkunft: Nürtingen

Themenstarter:

3-Schichten-Design?? [harte vs. weiche (Daten-)Objekte / OOP vs. ActiveRecords]

beantworten | zitieren | melden

Hallo liebe Gemeinde,

ich lese mich jetzt schon seit kurzer Zeit in das Thema des 3-Schichten-Designs ein, verstehe es auch einigermaßen aber es hapert bei mir an der Umsetzung. Also mein Ziel ist es ein kleines Kalenderprogramm zu basteln.

Folgende Komponenten werden aufjedenfall gebraucht:
Ein DataSet (dsCalender) mit einer DataTable (dtTasks) drin. Erstmal soll die Datenbank als XML-File gespeichert werden. Fürs gute Aussehen sollen noch ein paar Forms und UserControls verwendet werden.

Mein Ziel ist es jetzt, eine "Fundamentalsystem" zu bauen, welches möglichst skalierbar ist und es soll mir einen guten komfort und (wichtig) Übersichtlich sein.
Mein Ansatz bisher ist folgendermaßen: Siehe Anhang

Mein Problem liegt jetzt in der weiteren Vorgehensweise.
Was kommt alles in den BusinessLayer?
Was kommt alles in den DataAccessLayer?

Ich habe mir mal so Gedanken über das Design gemacht und ich habe folgende Fragen:
- Der PresentationLayer kennt das DataSet ja nicht, also ist auch kein DataBinding möglich?
- Ich hätte gerne eine Klasse Task, doch wo bringe ich sie unter und dann ist da noch das Problem dass die Klasse fast identisch ist mit der Klasse dtTasks aus dem DataSet. Ist die Klasse Task also überflüssig?
- Wenn ich schon dabei bin: Ich hatte bisher wenig mit INterfaces zu tun. Wo könnte man diese hier einsetzen?

Vielleicht kann mir jemand anhand eines einfachen beispiels (eine datenbank mit einer tabelle) einen sinnvollen Aufbau zeigen?

Ich hoffe ihr könnt mir helfen.
MFG TripleX
Dieser Beitrag wurde 5 mal editiert, zum letzten Mal von TripleX am .
Attachments
Träume nicht dein Leben sondern lebe deinen Traum.
Viele Grüße, David Teck
private Nachricht | Beiträge des Benutzers
JuyJuka
myCSharp.de - Experte

Avatar #avatar-2316.jpg


Dabei seit:
Beiträge: 2282
Herkunft: Deutschland

beantworten | zitieren | melden

Hallo TrippleX,

Die blöden Antworten
Zitat von TrippleX
Was kommt alles in den BusinessLayer?
Die Funktionslogik deiner Software. Validierungen, Logische prüfungen, etc.
Zitat von TrippleX
Was kommt alles in den DataAccessLayer?
Ausschließlich der Zugriff auf die Datenbasis.

Die guten Antworgen
Zitat von TrippleX
- Der PresentationLayer kennt das DataSet ja nicht, also ist auch kein DataBinding möglich?
DataBinding geht auch mit Objekten, im Dialog "neue Datenquelle" einfach mal Objekt stadt Datenbank auswählen. Tipp: UNBEDINGT INotifyPropertyChanged mit allen Klassen aus der Funktionsschicht implementieren (weiter hin zu Empfehlen: IDataErrorInfo, IEditable, ...).
Zitat von TrippleX
- Wenn ich schon dabei bin: Ich hatte bisher wenig mit INterfaces zu tun. Wo könnte man diese hier einsetzen?
Für jede deiner Klassen ein Interface aus der Funktions- und der Datenzugriffsschicht. :) Das ist Ernst gemeint! Tipp: FactoryPattern oder gleich einen Microkernel besorgen (z.B.: |Aisys| O/R Mapper , da ist ein Microkernel mit drin).
Zitat von TrippleX
- Ich hätte gerne eine Klasse Task, doch wo bringe ich sie unter und dann ist da noch das Problem dass die Klasse fast identisch ist mit der Klasse dtTasks aus dem DataSet. Ist die Klasse Task also überflüssig?
Ist Sie nicht, da Sie die Logik enhält, z.B. Validierungen, z.B. AnfangsDatum < EndDatum. (*g* Beispiel zum Beispiel *g*).
Aber deine Überlegung hatte ich auch und habe mir daher eine generische Datenzugriffschicht gebaut wo man nicht für jede Funktionsklasse eine Datenzugriffsklasse braucht (siehe |Aisys| O/R Mapper). Aber das ist ein bischen Weiterführend und du solltest erst mal bei deinem Projekt bleiben und nur einen Microkernel und jede menge Interfaces benutzen.

Gruß
Juy Juka
private Nachricht | Beiträge des Benutzers
TripleX
myCSharp.de - Member

Avatar #avatar-3071.jpg


Dabei seit:
Beiträge: 338
Herkunft: Nürtingen

Themenstarter:

beantworten | zitieren | melden

danke für deine Antwort,

also ich habe mal versucht mit Interfaces zu arbeiten, und mein Aufbau ist jetzt folgendermaßen: Siehe Anhang.

Eigentlich bin ich soweit recht zufrieden, doch beim testen hats nich ganz geklappt.
Also ich habe mal versucht von meiner PresentationLayer die Funktion AddTask aufzurufen. Dann bringt mir der Compiler folgende Fehlermeldung:
Zitat
Der Typ "DataAccessLayer.ITasks" ist in einer nicht referenzierten Assembly definiert. Fügen Sie einen Verweis auf die Assembly "DataAccessLayer, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" hinzu.

Was die Fehlermeldung heißt, kann ich mir denken. Ich müsste im PL ein Verweis zum DAL einfügen. Aber das verstößt dann doch gehen die "Richtlinien", welche besagen dass ein Layer nur den Layer unter ihn kennt.

MFG TripleX
Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von TripleX am .
Attachments
Träume nicht dein Leben sondern lebe deinen Traum.
Viele Grüße, David Teck
private Nachricht | Beiträge des Benutzers
JuyJuka
myCSharp.de - Experte

Avatar #avatar-2316.jpg


Dabei seit:
Beiträge: 2282
Herkunft: Deutschland

beantworten | zitieren | melden

Hallo TrippleX,

Datenzugriffsschicht und Funktionsschicht haben natürlich zwei verschiedene Schnittstellen. Du musst in beiden je ein interface ITask definieren (jap, gleiche spielchen wie mit den beiden Klassen *g*), schließlich muss ITask aus der Datenzugriffsschicht ja etwas anderes leisten als ITask aus der Funktionsschicht.
Ach ja: Microsoft empfiehlt Typen nicht gleich zu benennen, auch wenn Sie durch Namespaces getrennt sind (Datenzugrifsschicht.ITask und Funktionsschicht.ITask). Des weiteren soll man auch keine Postfixe verwendne sondern den "Basisnamen" hinten anhängen (also nicht "dsCalendar" sondern "CalendarDataSet"). (Richtlinien zur Benennung)

Namens- und Strucktuvorschlag folgen als UML-Diagramm.
1. Ich habe zwischen Funktions- und Datenschicht auf Schnittstellen verzichtet, da mit Rainbird (glaube ich) mal gesagt hat, das Interfaces mit DataSet und Co. ziemlich schwer umzusetzen ist. (Falls ich mich irre, sollte man dort natürlich sofort Schnittstellen einfügen.)
2. Das "<<Fasade>>" ist ein weiteres Pattern. Ist für so ein Kalender-Projekt glaube ich sehr gut.

Gruß
Juy Juka
Attachments
private Nachricht | Beiträge des Benutzers
Peter Bucher
myCSharp.de - Experte

Avatar #jVxXe7MDBPAimxdX3em3.jpg


Dabei seit:
Beiträge: 6141
Herkunft: Zentralschweiz

beantworten | zitieren | melden

Hallo TippleX

Schau mal hier:
- HowTo: 3-Tier / 3-Schichten Architektur


Gruss Peter
--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

- https://peterbucher.ch/ - Meine persönliche Seite
- https://fpvspots.net/ - Spots für FPV Dronenflüge
private Nachricht | Beiträge des Benutzers
TripleX
myCSharp.de - Member

Avatar #avatar-3071.jpg


Dabei seit:
Beiträge: 338
Herkunft: Nürtingen

Themenstarter:

beantworten | zitieren | melden

Hallo,

danke für die Antworten (auch wenn ihr keiner meinen namen korrekt schreiben konnte ;) ).

Also irgendwie stehe ich immernoch aufm Schlauch. Mal schauen ob ich das richtig verstanden habe bisher:

Im DataAccessLayer kommt mein DataSet hinein. Für jede Tabelle inner DB gibt es:
- 2x Interface (einmal für die Tabelle an sich und einmal für die Einträge)
- 1x Klasse (um die Zugriffe auf diese Tabelle zu Managen)
(ITask, ITaskManager, TaskManager)

Im BusinessLayer kommt rein (für jede Tabelle inner DB):
- 1x Interface (für die Einträge)
- 2x Klassen (für die Einträge und um die Klasse zu managen)
(ITask, Task, TaskManager)

stimmt das bisher so?

MFG TripleX
Träume nicht dein Leben sondern lebe deinen Traum.
Viele Grüße, David Teck
private Nachricht | Beiträge des Benutzers
Peter Bucher
myCSharp.de - Experte

Avatar #jVxXe7MDBPAimxdX3em3.jpg


Dabei seit:
Beiträge: 6141
Herkunft: Zentralschweiz

beantworten | zitieren | melden

Hallo TripleX

Ich habe "TrippleX" gelesen aber dann falsch geschrieben,
aber auch damit war ich falsch...
...jetzt stimmts ;-)

Wo hakts jetzt noch?
Hast du dir das Video / Tutorial und die Beispielanwendung auf Code-Inside angesehen?

Ich finde Robert bringt es dort ziemlich auf den Punkt.
Zwei gleichbenannte Interfaces halte ich für Unfug.
Die ganze Architektur sollte schlussendlich auch einen Sinn ergeben.

Mal in Kürze:

DataLayer:
Abstraktion deines Datenzugriffs.
Dafür benutzt du im optimalen Fall ein Interface für den Zugriff, so sprichst du dann immer mit IUserProvider aber im Hintergrund speist du bspw. ein XmlUserProvider / SqlUserProvider ein, am Businesslayer ist das aber egal.

Dieser Layer sollte nur sehr generische Datenzugriffsarbeiten behinhalten, keine Logik und keine Filterund (Nur die Möglichkeit dazu).

BusinessLayer:
Hier benutzt du den DataLayer, aber nur das Interface davon und delegierst alle Daten im einfachsten Fall weiter.

Im anderen Fall (So dass die Schicht auch Sinn ergibt) machst du alles was im DataLayer noch fehlt, aber generelll anwendbar ist.
D.h. Filterung / Sortierung, spezielle Abfragemethoden, Caching, Logik in der Abfrage.

Ein gutes Beispiel hierbei finde ich wiederum das von Code-Inside mit dem IUserService, dort enthält der Service die Logik für den LogIn und LogOut.
Das hätte nichts im DataLayer zu suchen, so als Beispiel.

Und von deiner Benutzeroberfläche sprichst du nur mit dem Businesslayer, den DataLayer kennst du gar nicht.
Und alle Logik die du noch in der Benutzeroberfläche platzieren möchtest / automatisch machst, gehört in den meisten Fällen in die Businesssicht.
Natürlich alles was geht :-)

Jetzt fragst du dich vielleicht wo deine Task Klasse hingehört.
Nun ich halte es so, dass ich eine eigene Bibliothek mit allen Entities / Data Objects / Businessobjects anlege und diese in jeder Schicht referenziere.

Also ist deine schlussendliche Datenquelle (DataSet, Xml, SqlServer, ....) nur im DataLayer bekannt und sonst nur überall deine Businessobjekte + Interfaces.

Ich hoffe das hilft dir weiter.


Gruss Peter
--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

- https://peterbucher.ch/ - Meine persönliche Seite
- https://fpvspots.net/ - Spots für FPV Dronenflüge
private Nachricht | Beiträge des Benutzers
JuyJuka
myCSharp.de - Experte

Avatar #avatar-2316.jpg


Dabei seit:
Beiträge: 2282
Herkunft: Deutschland

beantworten | zitieren | melden

Hallo TripleX, [offtopic]Haha! Richtig geschrieben![/offtopic]

Könnte man so machen. Aber wozu brauchst du Zugriff auf die Tabelle selber?
Folgendes müsste doch ausreichend sein:
+ das DataSet (mit allem drum und dran)
+ eine Schnittstelle für die Einträge (Row)
+ eine Klasse für den Zugriff
(+ eine Schnittstelle für den Zugriff, auch der kann per Schnittstelle abstrahiert werden )

Auch hier vielleicht eine Schnittstelle für TaskManager. Aber anstelle eines TaskManagers würde ich gleich eine Klasse Calendar machen ;).

Summa sumarum:


namespace TripleX.Calendar.DataAcces
{
  internal class CalendarDataSet;
  internal class ...; // rest vom DataSet halt
  public interface ITaskData; // oder so ähnlich
  public class TaskDataManage; 
  public interface ITaskDataManage; // optional, erfordert Microkernel
}

namespace TripleX.Calendar.Business
{
  public interface ITask;
  internal class Task;
  public class Calendar; // sprich: TaskManager
  public interface ICalendar; // wäre schon besser das zu haben. Dann kann Calendar auch internal werden.
}

Gruß
Juy Juka
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von JuyJuka am .
private Nachricht | Beiträge des Benutzers
Peter Bucher
myCSharp.de - Experte

Avatar #jVxXe7MDBPAimxdX3em3.jpg


Dabei seit:
Beiträge: 6141
Herkunft: Zentralschweiz

beantworten | zitieren | melden

Hallo JuyJuka

Was hältst du vom Artikel von Robert und von meinem Text?
Bei deiner Aufzeigung hat es mir zuviel drin, das geht ja auch mit weniger genau so gut.


Gruss Peter
--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

- https://peterbucher.ch/ - Meine persönliche Seite
- https://fpvspots.net/ - Spots für FPV Dronenflüge
private Nachricht | Beiträge des Benutzers
JuyJuka
myCSharp.de - Experte

Avatar #avatar-2316.jpg


Dabei seit:
Beiträge: 2282
Herkunft: Deutschland

beantworten | zitieren | melden

Hallo Peter Bucher,

Den Artikel von Robert hab ich nur überflogen, da ich dachte, dass er sich an Einsteiger richtet und vor allem, weil ich im Büro auch noch Sachen für die Kunden erledigen muss - Nervig so was :D .
Zitat von Peter Bucher
Bei deiner Aufzeigung hat es mir zuviel drin, das geht ja auch mit weniger genau so gut.
Es enthält nur alle nötigen Klassen und je eine Schnittstelle dazu. Worauf soll man da noch verzichten können?

Im Detail:
  • CalendarDataSet + Co. = Das braucht man halt alles, wenn man sich von VS die Datenzugriffsschicht erstellen lässt. Pech gehabt.
  • ITaskData = Nur das interface zur DataRow-Ableitung für Tasks. Schließlich wird das nach Außen gegeben und da soll ja im besten Fall kein konkreter Typ auftauchen.
  • TaskDataManager = Fasade für die Datenzugriffsschicht, nich mehr und nicht weniger. Irgend wo braucht der Entwickler von außen halt zugriff.
  • ITaskDataManager = Optional und nur ein interface für TaskDataManager. Aber nach Außen wollen wir ja keine konkreten Typen.
  • Task = Funktionsklasse für Tasks (Anfangsdatum vor Enddatum prüfen, etc.)
  • ITask = Wieder nur ein interface, diesmal für Task.
  • Calendar = Funktionsklasse zum Verwalten von Tasks (z.B. Verschieben, Verketten, ... was weiß ich den, ist TripleX's Projekt :) )
  • ICalendar = Das obligatorische interface.

Aber ich glaube hier prallen mal wieder unsere Welten auf einander ("Daten- und Funkionsklassen" VS "Daten und Funktion bilden eine Einheit").
Für mich ist es 100%ig undenkbar irgend eine "Säule" mit den Daten-Klassen zu haben, paralell zu meinen Schichten. Das ist aus meiner Sicht falsch, da die Daten ja nicht öffentlich sein sollen und durch eine Klasse geschütz werden müssen und dass natürlich nur die Klasse kann, die auch die Funktion der Daten kennt.
Warst nicht du das, der sagte "Ich programmiere am liebsten Konkret."? Dann wären wir auch da wieder "Gegenpole" bei mir verwendet eine Klasse im Normallfall keine andere Klasse direkt sondern ausschließlich Schnittstellen (wo es geht auch bei den Framework-Klassen).

Gruß
Juy Juka

[EDIT]
Jetzt hab ich den Artikel von Robert doch mal gelesen - war ja garnicht so lang und ausschweifen wie ich dachte.
Guter Überblick und einfach geschrieben - sehr lobenswert.
Das beispiel ist gut gewählt, jedoch bräuchte man dringend etwas weiterführendes, da man nach ca. 1-2h am Ende mit den gelieferten Infromationen ist und dann verloren rum steht (hab ich irgend welche Link's übersehen?).
Das Beispiel ist ganz gut, jedoch geht es auch wieder richtung "Daten- und Funktionsklasse" (erkennt man nicht sofort), das ist zwar Nachvollziehbar aber für mich halt falsch/ein Fehler.
[EDIT]
Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von JuyJuka am .
private Nachricht | Beiträge des Benutzers
Peter Bucher
myCSharp.de - Experte

Avatar #jVxXe7MDBPAimxdX3em3.jpg


Dabei seit:
Beiträge: 6141
Herkunft: Zentralschweiz

beantworten | zitieren | melden

Hallo JuyJuka
Zitat von JuyJuka
Für mich ist es 100%ig undenkbar irgend eine "Säule" mit den Daten-Klassen zu haben, paralell zu meinen Schichten. Das ist aus meiner Sicht falsch, da die Daten ja nicht öffentlich sein sollen und durch eine Klasse geschütz werden müssen und dass natürlich nur die Klasse kann, die auch die Funktion der Daten kennt.
Verstehe ich nicht wirklich.
Was soll den öffentlich sein und warum nicht die Datenklassen?
Zitat von JuyJuka
Warst nicht du das, der sagte "Ich programmiere am liebsten Konkret."?
Hmm, ich wüsste nicht wo und in welchem Kontext.
Zitat von JuyJuka
bei mir verwendet eine Klasse im Normallfall keine andere Klasse direkt sondern ausschließlich Schnittstellen (wo es geht auch bei den Framework-Klassen).
Das ist eine gute Praxis ja, ich wüsste aber nicht wo ich dagegen verstosse.
Zitat von JuyJuka
Das Beispiel ist ganz gut, jedoch geht es auch wieder richtung "Daten- und Funktionsklasse" (erkennt man nicht sofort), das ist zwar Nachvollziehbar aber für mich halt falsch/ein Fehler.
Was hier falsch / Fehler ist, würde mich interessieren,
ich kann deine Aussage nicht nachvollziehen.


Gruss Peter
--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

- https://peterbucher.ch/ - Meine persönliche Seite
- https://fpvspots.net/ - Spots für FPV Dronenflüge
private Nachricht | Beiträge des Benutzers
JuyJuka
myCSharp.de - Experte

Avatar #avatar-2316.jpg


Dabei seit:
Beiträge: 2282
Herkunft: Deutschland

beantworten | zitieren | melden

Hallo Peter Bucher,

Datenklassen an sich stellen für mich schon Fehler da. Daten und Funktion müssen eine Einheit bilden.
Zitat von Peter Bucher
Zitat von JuyJuka
Warst nicht du das, der sagte "Ich programmiere am liebsten Konkret."?
Hmm, ich wüsste nicht wo und in welchem Kontext.
Kann mich auch vetuen.
Zitat von Peter Bucher
Zitat von JuyJuka
bei mir verwendet eine Klasse im Normallfall keine andere Klasse direkt sondern ausschließlich Schnittstellen (wo es geht auch bei den Framework-Klassen).
Das ist eine gute Praxis ja, ich wüsste aber nicht wo ich dagegen verstosse.
Hab ich nie behauptet. Falls es so rüber gekommen ist: Sorry.
Zitat von Peter Bucher
Zitat von JuyJuka
Das Beispiel ist ganz gut, jedoch geht es auch wieder richtung "Daten- und Funktionsklasse" (erkennt man nicht sofort), das ist zwar Nachvollziehbar aber für mich halt falsch/ein Fehler.
Was hier falsch / Fehler ist, würde mich interessieren,
ich kann deine Aussage nicht nachvollziehen.
Das gleiche, wie ich im allgemeinen an deinem Programmierstiel als Fehler sehe (ich seh das so! vermutlich jedoch nicht jeder):
Daten und Funktion bilden eine Einheit. Von dir so genannte "Datenklassen" dürften überhaupt nicht existieren. Um mal bei Beispiel von Rober zu bleiben:
  • IUserServie.GetFriendsFromUser gehört für mich definitiv in die User-Klasse!
  • Die User-Klasse müsste eigentlich für mich in der Funktionsschicht liegen und neben einem Zustand auch alle Funktionen mit liefern.

Weichen wir zu weit vom eigentlichen Thema ab?

Gruß
Juy Juka
private Nachricht | Beiträge des Benutzers
Peter Bucher
myCSharp.de - Experte

Avatar #jVxXe7MDBPAimxdX3em3.jpg


Dabei seit:
Beiträge: 6141
Herkunft: Zentralschweiz

beantworten | zitieren | melden

Hallo JuyJuka
Zitat von JuyJuka
  • IUserServie.GetFriendsFromUser gehört für mich definitiv in die User-Klasse!
  • Die User-Klasse müsste eigentlich für mich in der Funktionsschicht liegen und neben einem Zustand auch alle Funktionen mit liefern.
Das was du beschreibst geht in Richtung Active Record Pattern.
Es gibt aber noch andere Vorgehensweisen, wenn dir bspw. POCO was sagt?
Zitat von JuyJuka
Weichen wir zu weit vom eigentlichen Thema ab?
Langsam, aber es könnte immer noch nützlich für den Threadersteller sein, wenn da ein Ergebnis rauskommt.


Gruss Peter
--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

- https://peterbucher.ch/ - Meine persönliche Seite
- https://fpvspots.net/ - Spots für FPV Dronenflüge
private Nachricht | Beiträge des Benutzers
JuyJuka
myCSharp.de - Experte

Avatar #avatar-2316.jpg


Dabei seit:
Beiträge: 2282
Herkunft: Deutschland

beantworten | zitieren | melden

Hallo Peter Bucher,
Zitat von Peter Bucher
Das was du beschreibst geht in Richtung Active Record Pattern.
Es gibt aber noch andere Vorgehensweisen, wenn dir bspw. POCO was sagt?
Wieso nur geht? Das ist Activ Record - auch wenn ich nicht glaube, dass dies bereits ein Pattern ist.
Alles anderen als dieses Vorgehen verstösst meines Wissens und Verständnisses nach gegen eine der Grundideen der Objektorientierung :
Daten und Funktionen bilden eine Einheit.

Hast du auch so ein stechendes Dejavuê ?

Gruß
Juy Juka
private Nachricht | Beiträge des Benutzers
Peter Bucher
myCSharp.de - Experte

Avatar #jVxXe7MDBPAimxdX3em3.jpg


Dabei seit:
Beiträge: 6141
Herkunft: Zentralschweiz

beantworten | zitieren | melden

Hallo JuyJuka

Du hast mir leider meine Frage nicht beantwortet.
Und auch wieso der zweitere Ansatz immer stärker vertreten ist.
Also eine Klasse zu haben, die mit den Objekten arbeiten.
Und eine Klasse zu haben, die das Objekt an sich repräsentiert, ohne Logik.

Ich sehe nicht ein, inwiefern der andere Ansatz gegen die Objektorientierung verstösst.


Gruss Peter
--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

- https://peterbucher.ch/ - Meine persönliche Seite
- https://fpvspots.net/ - Spots für FPV Dronenflüge
private Nachricht | Beiträge des Benutzers
JuyJuka
myCSharp.de - Experte

Avatar #avatar-2316.jpg


Dabei seit:
Beiträge: 2282
Herkunft: Deutschland

beantworten | zitieren | melden

Hallo Peter Bucher,

Deine Frage kann/konnte ich leider nicht erkennen.
(hey! Beitrag editieren ist unfaier)

Die Grundidee ist doch schon der Wiederspruch: Klasse O enthält alle Daten und Klasse F enthält die Funktion zu den Daten.
Genau das sollte ja in der Objektorientierung nicht der Fall sein. Die Daten sollten eigentlich mit der Logik zusammen sein, damit sie sich selbst Schützen können. Die Klasse, die nur die Daten darstellt müsste ja theoretisch alles Mögliche in den Feldern zulassen. Beispiel:

class Task{public DataTime Von{get;set;} public DateTime Bis{get;set;}}
Hier dürfte keine Prüfung in den Properties erfolgen, da es ja Logik ist, das "Von" kleiner als "Bis" sein muss.

Gruß
Juy Juka
private Nachricht | Beiträge des Benutzers
Christoph Burgdorf
myCSharp.de - Member

Avatar #avatar-2915.jpg


Dabei seit:
Beiträge: 365
Herkunft: Hannover

beantworten | zitieren | melden

Hallo miteinander,

das mit dem Objekt und den Funktionen finde ich jetzt sehr interessant und ich klinke mich hier mal ein, wenn ich darf ;-)
Ich stelle mir nämlich hin und wieder auch die Frage, was ich als Funktion auf ein Objekt schreibe und was besser als Service Klasse um das Objekt herum. Ich habe für mich folgende Erklärung gefunden. Wenn ich eine Funktion habe, die direkt Einfluss auf die Daten des Objekts hat, dann packe ich diese Funktion auch direkt in das Objekt.

Einfaches kleines Beispiel:

In meinem lieblings MediaPlayer (http://banshee-project.org/) gibt ein Track Objekt, das wenn ich mich recht entsinne die Methoden IncreasePlayCount() und IncreaseSkipCount() bereitstellt. Es macht für mich absolut Sinn, diese Methoden direkt auf dem Track Objekt anwenden zu können.


Wenn die Funktion aber eher etwas mit dem Objekt macht, ohne dessen Daten zu verändern, dann packe ich die Funktion eher in eine Service Klasse.

Konkretes Beispiel:

Wir haben ein EDI Programm bei uns in der Firma, welches wir gerade überarbeiten. Kurze Erklärung zu EDI, falls es jemand nicht kennt: Belege (Rechnungen, Aufträge etc.) werden in einem speziellen Industriestandard zwischen Geschäftspartnern übertragen.

Nun ist es hier so, das unsere Kunden (Lebensmittelbranche) EDEKA, Rewe, Kaufhof so ein Belegobjekt alle ein klein wenig anders haben wollen...klar, alles EDI Standard aber der eine will, dass die Zeilen mit der Artikelnummer anfangen, der nächste will lieber mit der Menge beginnen etc.

Dafür haben wir verschiedene Implementierungen von IServiceProvider, die alle eine Convert(Document currentDocument) Methode bereitstellen.

Ich würde einen Beleg also so übertragen: IServiceProvider.Convert(myDocument).

War das jetzt totaler Stuss oder ergibt das für euch Sinn?

Gruß

Christoph
private Nachricht | Beiträge des Benutzers
moq
myCSharp.de - Member



Dabei seit:
Beiträge: 131

beantworten | zitieren | melden

Hallo Christoph,

das hört sich nach Contract First Design und evtl in Verbindung mit einem Microkernel an. Finde ich persönlich super. Wartbarkeit und lose Koppelung macht genau dort Sinn. Gerade das einzelne Komponenten ("Implementierungen") via Konfigurationsdateien ausgetauscht werden können vereinfacht die Wartbarkeit enform.

Viele Grüße,
moq
private Nachricht | Beiträge des Benutzers
JuyJuka
myCSharp.de - Experte

Avatar #avatar-2316.jpg


Dabei seit:
Beiträge: 2282
Herkunft: Deutschland

beantworten | zitieren | melden

Hallo bvsn, Hallo moq,

Das ist ja kein Privat-Thread von Peter Bucher und mir. Andere Meinunge/Kommentare sind gern gesehen.

Unter Umständen können solche "Service"-Klassen auftreten, stimmt. Ich finde es nur falsch, wenn man seine Klassen gleich von anfang an in Stücke reißt. Bei Funktionen, die über die eigenltiche Aufgabe der Klasse hinaus gehen, ist es manchmal wirklich sinnvoll das in einer anderen Klasse zu lösen.
Beispiel: Ein Auftragsklasse, die den Ablauf und ihren Status verwaltet, eventuell noch ein paar Daten prüft. Eine Beleg zu dieser Klasse/zu diesem Objekt zu drucken kann jetzt bereits die Aufgabe einer anderen Klasse darstellen.

Zu dem Konkreten EDI Beispiel hätte ich eine andere Lösung. Da ich auch sehr viele Schnittstellen (VDA, UPS Worldship, etc.) schreibe sehe ich die Formate der Empfänger weniger als Funkton sonder ehr als Daten. Ich verwende mein Komponente JuyJuka.Reports gebe die Objekte und das benötige Format rein und fertig:


JuyJuka.Reports.IReport report = Microkernel.Create<JuyJuka.Reports.IReport>();
report.Objekt = auftrag;
report.Format = System.IO.File.ReadAllText(Properties.Settings.ReportFile);
System.IO.File.AppendAllText(output,report.Compile());
@[email protected];@[email protected];@[email protected];@[email protected];@[email protected];@[email protected]@REPORT(ReportFile2,[email protected]
@[email protected];@[email protected]
Für einen Anderen Empfänger ändere ich nur die Formatbeschreibung in ReportFile2
@[email protected];@[email protected]

Gruß
Juy Juka
private Nachricht | Beiträge des Benutzers
Christoph Burgdorf
myCSharp.de - Member

Avatar #avatar-2915.jpg


Dabei seit:
Beiträge: 365
Herkunft: Hannover

beantworten | zitieren | melden

Hallo,

@moq

Ich muss gestehen, dass mir der Begriff "Contract First Design" bisher neu war und ich diesbezüglich erstmal etwas nachlesen sollte. Dieser Link scheint recht gut zu sein:

http://weblogs.asp.net/ralfw/archive/2006/08/02/Dynamic-component-binding-made-easier-_2D00_-An-easy-to-use-Microkernel-to-help-reap-Contract_2D00_First_2D00_Design-benefits-in-.NET-programs.aspx

Um es mal so allgemein wie möglich zu formulieren, ich versuche in jedem Fall so zu entwickeln, dass die Komponenten leicht austauschbar und erweiterbar sind.

@JuyJuka
Zitat
Ich finde es nur falsch, wenn man seine Klassen gleich von anfang an in Stücke reißt.

Da möchte ich dir voll und ganz zustimmen. Ich würde auch auf gar keinen Fall künstlich die Funktionen vom Objekt trennen. Das zerstört den schönen OOP Ansatz von Anfang an und im Grunde kann ich dann auch direkt auf einer DataRow arbeiten.

Interessanterweise, hatte ich zu Anfang einen ähnlichen Ansatz in Erwägung gezogen. Das bedeutet, dass jeder Belege eine Eigenschaft ServiceProvider (also mehr oder weniger das Format um es auf deinen Code zu projezieren) bereitstellt und diese dann auf einen IServiceProvider gesetzt werden kann. Ich hätte dann direkt auf dem Beleg Convert() aufrufen können und er hätte die Konvertierung mit dem gesetzten ServiceProvider vorgenommen.

Ich bin allerdings davon abgekommen. Unter anderem z.B. auch deshalb, weile jede EDI Datei den Header ein bisschen anders hat - dies ist auch wiederum ServiceProvider bezogen. Also stellen alle ServiceProvider noch eine GetHeader() Methode bereit. Dies hat nun ganz eindeutig nichts mehr mit dem Beleg zu tun und insofern, finde ich, dass die Benutzung so irgendwie straighter ist:

Eine EDI Datei wird nun also so vorbereitet (stark vereinfacht):

IServiceProvider.GetHeader();
IServiceProvider.Convert(doc1);
IServiceProvider.Convert(doc2);
IServiceProvider.Convert(doc3);
...(natürlich im Schleifchen)

Gruß

Christoph
private Nachricht | Beiträge des Benutzers
Reman
myCSharp.de - Member



Dabei seit:
Beiträge: 117
Herkunft: Dresden

beantworten | zitieren | melden

Die Diskussion ist sehr interessant - genau sowas hab ich auch schon mit mehreren Kollegen geführt.
Ich find die Idee von "Service"/"Manager" Klassen besser - sodass man eine klare Trennung zwischen Daten- und Funktionsklasse hat.

Ich hatte bei einem sehr alten Projekt in die User-Klasse alles reingeschmissen (Stichwort Active Record). Allerdings wird dadurch die User Klasse immer größer. Auch wenn man nun die pure größe einer Datei durch partial etc. "verringern" kann, macht es das ja nicht gleich schön ;)
Ein andere Problem hat sich dann später ergeben: Manche Klassen hatten plötzlich "Insert,Update..." oder andere Logiken drin und manche halten einfach nur Daten. Am Ende hatte keiner mehr genau durchgesehen. Unit Tests oder Dependency Injection waren da für mich auch noch ein Fremdwort.

Aus pragmatischer Sicht find ich daher das mit den Service/Manager praktischer: Klare Zuständigkeiten. Dependency Injection ist wesentlich "durchschaubarer" und das testen ist IMHO einfacher.
Ich hab auf der einen Seite dumme POCOs, auf der anderen Seite intelligente Services.

Ob das klar nach "OOP" geht oder nicht, ist aus meiner Sicht dann garnicht so wichtig. Es gibt auch viele andere Design Prinzipien die man beherzigen kann. Bei der vermischung von Daten- und Funktionsklassen hat man nämlich sehr schnell das Single Responsible Principle (SRP) verletzt. Auch wenn man hier die Definition von "Verantwortlichkeit" sich so zurecht legen kann, wie es gerade passt.

Momentan hab ich bei einem Projekt die ganzen Useraktion sogar in getrennten Services:
- RegistrationService
- ActivationService
- ProfileService
- ...
... weil mir nämlich der "UserService" zu ungenau und groß war und das testen langsam unangenehm.

Solange das Team eine einheitliche Linie findet und es klare Zuständigkeiten gibt und auch die Testbarkeit gegeben ist, kann man das jedoch machen wie man möchte - beide Varianten kann ich verstehen. Insbesondere bei Desktopprogrammen mit den INotifyPropertyChanged Events etc. hat vielleicht die Variante von Juy auch etwas mehr Charm. Ich sprech aus der Sicht eines Webentwicklers.

@Juy: Danke zudem für deine Lob und deine Kritik an dem Artikel den Peter von mir verlinkt hat :)
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Reman am .
private Nachricht | Beiträge des Benutzers
JuyJuka
myCSharp.de - Experte

Avatar #avatar-2316.jpg


Dabei seit:
Beiträge: 2282
Herkunft: Deutschland

beantworten | zitieren | melden

Hallo Reman,

Du hast recht, eine Klasse muss ihre Grenzen kennen aber man sollte die eine Aufgabe einer Klasse auch nicht zu klein Fassen.
Wenn jetzt jede Klasse nur die Aufgabe einer Methode übernimmt (z.B. Benutzer Registrieren, Benutzer Aktivieren, etc.), kann ich auch gleich wieder prodzedural Programmieren, weil alle Klassen vollen Zugriff auf die Daten brauchen und jeder Klasse genau wissen muss, was die anderen Klassen tuen (bzw. der Entwickler muss es wissen, wärend er die Klasse schreibt).
Genau das beschriebene Szenario sollte durch die Objektorientierung (durch Kapselung und die Einheit von Daten und Funktion) verhindert werden.
Das fett Angezeigte soll nur die kritischen Stellen hervor heben.

Um beim Beispiel zu bleiben: Eine Aufgabe wäre für mich das Verwalten eines Benutzers. Also Anlegen, Löschen, Aktivieren, Deaktivieren, Datenvollständigkeit prüfen, etc.
Nicht die Aufgabe der Benutzer-Klasse wäre es:
- Den Benutzer an- und abzumelden.
- Belege für den Benutzer drucken (z.B. Mitgliedsausweis).
- Den Benutzer einer Gruppe zu zuordnen.

Das große Kunststück ist es eben, zu sagen: Was ist die Aufgabe der Klasse und was nicht.
Jeder einzelne Funktion (sry, mir fällt keine bessere Bezeichnung ein) in eine seperate Klasse zu packen ist auf jeden fall zu ... fein.
Zitat von Reman
Es gibt auch viele andere Design Prinzipien die man beherzigen kann.
Tja, nur ist Objektorientierung (mit Aspektorientierung) nicht um sonst der "neuste Schrei". Die Anderen Paradigmen/Prinzipien haben auch Ihre vorteile, doch werden diese im Normalfall in der Objektorientierung vereint.

Gruß
Juy Juka
private Nachricht | Beiträge des Benutzers
LaTino
myCSharp.de - Experte

Avatar #avatar-4122.png


Dabei seit:
Beiträge: 3062
Herkunft: Thüringen

beantworten | zitieren | melden

Noch ein kurzer Kommentar von mir:

Active Record bzw. gemeinsame Kapselung von Funktion und Eigenschaften mag hin und wieder sinnvoll sein, ist es jedoch nicht immer. Im Gegenteil kann es sehr sinnvoll sein, Funktionen an andere Klassen auszulagern.

-> Verhalten einer Klasse zur Laufzeit ändern
-> u.U. schlechte Erweiterbarkeit der Funktionalität

Methoden, i.e. das Verhalten von Klassen in extra Verhaltensobjekte zu kapseln, ist oft eine sehr gute Idee und widerspricht in keinem Fall der Objektorientierung. Stichwort Strategie-Muster.

Active Record ist eine Idee, die sich anbietet, wenn man Daten modelliert. Nur, deshalb ist es weder die einzige objektorientierte Herangehensweise, noch in jedem Fall die beste (rein gefühlsmäßig würde ich sogar behaupten - je komplexer das Szenario, desto weniger gut geeignet...).

Was die fetten Stellen im Text oben angeht: wieso zum Teufel verlangst du eigentlich sonst immer, dass gegen Schnittstellen programmiert wird (was ja richtig ist), aber bei diesem Ansatz lässt du komplett hinten runterfallen, dass man auch Verhaltensklassen gegen Schnittstellen programmieren kann. Alles, was den Daten- oder Controllerklassen dann bekannt sein muss, ist das Interface.

LaTino
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von LaTino am .
"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)
private Nachricht | Beiträge des Benutzers
JuyJuka
myCSharp.de - Experte

Avatar #avatar-2316.jpg


Dabei seit:
Beiträge: 2282
Herkunft: Deutschland

beantworten | zitieren | melden

Hallo LaTino,

Hab mich schon gefragt, wann endlich das richtige Pattern fällt :) Glückwunsch.
Der große Unterschied zwischen diesem "Service"-Zeug und dem Strategie-Pattern ist, dass die "Strategie" beim Pattern in der Klasse enthalten ist und die Klasse troz allem gekapselt bleibt. Die Methode(n) werden mit diesem Pattern einfach nur als Daten behandelt, was auch sinnvoll ist und im normalfall keine Regeln der Objektorientierung bricht.

Das "Service"-Zeug hingegen steth (so weit mein verständniss für Objektorientierung geht) in konkretem und exakten Gegensatz zu einer der Hauptregel der Objektorientierung (Daten+Fukion=Eine Einheit).
Zitat von LaTino
rein gefühlsmäßig würde ich sogar behaupten - je komplexer das Szenario, desto weniger gut geeignet...
Die Objektorientierung wurde genau für's einfacher Modelieren/Programmieren von komplexen Systemen entwickelt. Und ich empfinde es auch oder gar speziell bei komplexen Sachverhalten einfacher, wenn jedes "Objekt" seine Aufgaben auch selber erledigen kann.

Nur um Missverständnissen vorzubeugen: Nicht zu sehr auf "Activ Record" versteifen. Ein Objekt kann auch eine Liste darstellen und die Aufgaben der Liste übernehmen. "Active Record" wird ja (zumindest von mir) sehr stark mit EINEM Objekt verbunden, stimmt aber in dem Kontext wie hier der Name "Activ Record" verwendet wird nicht.

[EDIT]
Gleichzeitig Schreiben ist doof :)
Zitat von LaTino
Was die fetten Stellen im Text oben angeht: wieso zum Teufel verlangst du eigentlich sonst immer, dass gegen Schnittstellen programmiert wird (was ja richtig ist), aber bei diesem Ansatz lässt du komplett hinten runterfallen, dass man auch Verhaltensklassen gegen Schnittstellen programmieren kann. Alles, was den Daten- oder Controllerklassen dann bekannt sein muss, ist das Interface.
Ob man jetzt bei den fetten Stellen Schnittstellen hat oder nicht ändert nichts. Ob ich jetzt über eine Schnittstelle oder einen konkrtetn Typ ":) hab dich lieb" in das Postleitzahl-Property schreibe macht für den armen Postboten auch keinen Unterschied mehr.
[/EDIT]

Gruß
Juy Juka
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von JuyJuka am .
private Nachricht | Beiträge des Benutzers
LaTino
myCSharp.de - Experte

Avatar #avatar-4122.png


Dabei seit:
Beiträge: 3062
Herkunft: Thüringen

beantworten | zitieren | melden

Zitat von JuyJuka
Das "Service"-Zeug hingegen steth (so weit mein verständniss für Objektorientierung geht) in konkretem und exakten Gegensatz zu einer der Hauptregel der Objektorientierung (Daten+Fukion=Eine Einheit).

Das ist deine Regel, keine der Objektorientierung . Der Unterschied Service<->Strategie ist, wenn die Services richtig implementiert und angesprochen werden, keiner. Wenn überhaupt, ist die Orientierung hin zu Services die konsequente Weiterentwicklung in der Kapselung von Daten, und zwar eine rein räumliche, keine logische.
Zitat von JuyJuka
Und ich empfinde es auch oder gar speziell bei komplexen Sachverhalten einfacher, wenn jedes "Objekt" seine Aufgaben auch selber erledigen kann.

Das mag sein, aber wenn du jeder Klasse überlässt, wie sie ihre Daten manipuliert, bekommst du ein massives Problem mit der Wartbarkeit. In komplexen Klassenhierarchien müsstest du dann bei einer Änderung der Datenbehandlung ganz genau wissen, welche Stellen geändert werden müssen - zonk, sozusagen. Genau der Grund, wieso ich Active Record bei komplexen Szenarien für weniger geeignet halte. Eine Extrahierarchie für die Datenmanipulation ist naturgemäß leichter veränderbar und wartbar.

Du machst da noch einige Aussagen, bei denen sich mir einfach die Nackenhaare aufstellen - würde aber den Rahmen hier leider sprengen :D. Nur eines:
Zitat von JuyJuka
Die Anderen Paradigmen/Prinzipien haben auch Ihre vorteile, doch werden diese im Normalfall in der Objektorientierung vereint.
Falsch, falsch, FALSCH. Das beweist dir jeder PROLOG-Programmierer in 5 Sekunden. There is no silver bullet. OO ist NICHT das Allheilmittel, das alle anderen Paradigmen ablöst, weil es Probleme besser/schneller/effizienter löst. OO ist ein Werkzeug wie jedes andere. Ich mein's nicht böse, aber ich habe ein wenig den Eindruck, du argumentierst aus der Sichtweise: "Wenn man nur einen Hammer hat, sieht alles wie ein Nagel aus."


Gruß,

LaTino
"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)
private Nachricht | Beiträge des Benutzers
JuyJuka
myCSharp.de - Experte

Avatar #avatar-2316.jpg


Dabei seit:
Beiträge: 2282
Herkunft: Deutschland

beantworten | zitieren | melden

Hallo LaTino,
Zitat von LaTino
Das ist deine Regel, keine der Objektorientierung
Definitiv nicht.
Zitat von Mein Lehrer
Objekte definieren sich durch ihren Zustand (die Attribute) und ihr Verhalten (Methoden).
Zitat von Wikipedia
Die Objektorientierung, kurz OO, ist ein Ansatz zur Entwicklung von Software, der darauf beruht, die zu verarbeitenden Daten anhand ihrer Eigenschaften und der möglichen Operationen zu klassifizieren.
...
Als Weiterentwicklung von Datenstrukturen (aus mehreren Komponenten - auch unterschiedlichen Datentyps - zusammengesetzte Variablen) können Objekte auch noch über Eigenschaften und Methoden verfügen, die durch Programmcode in der Klasse festgelegt sind.
...
Blöd dass ich grad kein besseres Buch da habe.
Zitat von Buch: Visual C# Schritt für Schritt
Dies ermöglicht IHnen Klassen zu definieren, in denen das Verhalten (Methoden) und die Attribute (die Daten) eines Objekts zusammen gefasst sind.
Okey, ich muss hier ja nicht den Klotz-Kopf spielen. Es ist keine der grundlegensten Regeln der Objektorientieren. Wenn man sich aber nicht daran hält, verstöst man im normalfall gegen die Regel der Kapselung (an der wird hir ja wohl niemand zweifeln, ne?).
Zitat von LaTino
... die Orientierung hin zu Services die konsequente Weiterentwicklung in der Kapselung von Daten...
Was soll da eine Weiterentwicklung sein? Dadurch werden höchstens einfer von zwei Effekte erziehlt:
- Die Kapselung der Daten wird aufgebrochen.
- Die Logik (primär die Datenvalidierung) wird auf zwei Klassen (die Daten-Klasse und die Funktions-Klasse) aufgeteilt.
Also ich seh da keine Weiterentwicklung oder bin ich blind?
Zitat von LaTino
Eine Extrahierarchie für die Datenmanipulation ist naturgemäß leichter veränderbar und wartbar.
Wie soll so eine "Extrahierarchie" aussehen? Wie soll man die Zuordnung machen? Wer prüft, dass die "Extrahirarchie" auch aufgerufen wird? Was soll einfacher Wartbar sein?
Zitat von LaTino
.. jeder Klasse überlässt, wie sie ihre Daten manipuliert ...
Naja, nicht jede Klasse. z.B. Die Gültigkeit einer Auftrags-Position würde ich (in bestimmten Szenarien) im dazu gehörigen Auftrags-Kopf prüfen. Man muss und kann nicht jede Aussage 100%ig nehmen, es giebt immer seltene und dumme Fälle, in denen man nicht drum rum kommt, die eine oder andere Regel zu verletzen. Man sollte sich dabei einfach nur Schuldig fühlen.:)

[offtopic]
Zitat von LaTino
There is no silver bullet.
Du kannst mich gern immer mal wieder zurück hohlen. Ich bin ja auch nicht Perfekt und kann sonst wo hin abdriften. :)
[/offtopic]

Gruß
Juy Juka
private Nachricht | Beiträge des Benutzers
Golo Roden
myCSharp.de - Member

Avatar #avatar-2167.png


Dabei seit:
Beiträge: 4649
Herkunft: Riegel am Kaiserstuhl

beantworten | zitieren | melden

Zitat von JuyJuka
Hallo Peter Bucher,
[...]
Daten und Funktionen bilden eine Einheit.

NEIN!

Jedenfalls nicht zwingend. Gerade zB bei Businessobjekten macht es IMHO nicht zwingend immer Sinn, Daten und Funktionen in eine Klasse zu kapseln. Andernfalls erschwert man sich die Versionerung unter Umständen enorm.

Korrekter wäre IMHO, hier zu sagen: Die Datenklassen bieten das Interface (!) für die Funktionen an, leiten diese Aufrufe intern aber an ein Service-Objekt weiter, das die konkrete Implementierung bietet.
Wissensvermittler und Technologieberater
für .NET, Codequalität und agile Methoden

www.goloroden.de
www.des-eisbaeren-blog.de
private Nachricht | Beiträge des Benutzers
LaTino
myCSharp.de - Experte

Avatar #avatar-4122.png


Dabei seit:
Beiträge: 3062
Herkunft: Thüringen

beantworten | zitieren | melden

Ich zitier mal nicht alles einzeln, wird - fürchte ich - zu unübersichtlich.
Also: eine Klasse besteht aus drei Teilen:

a) ihre Daten
b) ihr Verhalten
c) ihre Schnittstellen

EDIT: damn you , Golo! Ich schreib's trotzdem nochmal ;)

Einverstanden soweit? Ich fordere ja nicht (eigentlich forder ich gar nix^^), dass die Verhaltensweisen (sprich: Methoden) grundsätzlich aus der Klasse rausgeworfen werden sollen. Aber IMO spricht absolut nichts dagegen, die nicht-privaten (protected, internal, public) Methoden durch eigene Klassen zu realiseren. Beispiel aus dem Leben: eine "meiner" Applikationen hier benutzt eine relativ komplexe abstrakte Fabrik, um Klassen der Modellschicht zu erzeugen. Vereinfacht gesagt besitzen die erzeugten Modellklassen sämtlich Schnittstellen für die CRUD-Operationen. Die jedoch werden (-> Strategy) durch Klassen einer anderen Familie durchgeführt. Die abstrakte Fabrik hat die Kontrolle und entscheidet, welche Klasse welche Methoden bekommt - die Klasse selbst weiß davon nichts, und soll das auch nicht wissen. Die Klassen im Beispiel bestehen natürlich immer noch aus den drei Komponenten oben, aber der wesentliche Kern ihrer Methoden ist ausgelagert. Und wieso? Weil mir das erst die Freiheit gegeben hat, die CRUD-Funktionalität komplett auszulagern in einen Service, der eben nur dafür zuständig ist. Dem sind die Daten wiederum völlig egal, er tut, was die Verhaltensklassen ihm sagen.

Der Vorteil von einem Service liegt nicht in der Architektur, das hätte man so auch ohne Service machen können. Aber der kann vn mehreren Applikationen angesprochen werden, lässt sich in bestehende Prozesse anbinden, auch unternehmensübergreifend, und ist einzeln leichter wartbar. Von Spielereien wie extrem einfacher Lastverteilung etc. mal abgesehen. Wie ich schrieb, die Vorteile liegen eher in der _räumlichen_ Trennung, die logische Trennung geht genauso auch ohne Service.

In Sachen Wartung und Erweiterbarkeit ist dieses System jedem System, indem jede der Modellklassen ihre eigene Implementierung der CRUD-Funktionen (und sei es nur ein Serviceaufruf) hat, himmelweit überlegen. Ein anderer Entwickler kann die kompletten Zugriffsmethoden überarbeiten, ohne auch nur den Hauch einer Ahnung zu haben, wie die Klassen konkret aussehen. Er muss nur die Schnittstellen kennen (tatsächlich läuft das auch so).

Die konsequente Weiterentwicklung besteht darin, dass man (freigegebene) Methoden von Klassen als das erkennt, was sie sind: wiederum Familien von Objekten, und das man das auch entsprechend modelliert. Wenn man sich auf dieser Ebene befindet, sollte man sich nicht schuldig fühlen, sondern sich bewusst werden, dass man kein Prinzip der OO verletzt hat, sondern sie entsprechend ihrer Stärken eingesetzt hat. Pragmatismus gehört dazu :). Wenn es funktioniert, Objekte verwendet, leichter wartbar und übersichtlicher ist als die herkömmliche Herangehensweise, dann ist es die bessere OO-Praktik.

Das meinte ich: OO ist ein Werkzeug. Es ist nicht starr, sondern soll helfen, in bestimmten Bahnen zu denken. Und Patterns helfen eine Ebene höher, zu erkennen, in welchen Bahnen man denkt, und zu reflektieren, welche protenziellen Probleme damit zusammenhängen können. Wenn man aber das konkrete Problem, das man hat, an OO und Patterns anpasst, damit man es besser lösen kann - falsche Herangehensweise. (ist ja schön, dass dann alles wie aus dem Lehrbuch ist. Leider löst es nicht mehr das Problem, das man ursprünglich hatte...)

LaTino
"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)
private Nachricht | Beiträge des Benutzers
JuyJuka
myCSharp.de - Experte

Avatar #avatar-2316.jpg


Dabei seit:
Beiträge: 2282
Herkunft: Deutschland

beantworten | zitieren | melden

Hallo LaTino, Hallo Golo Roden,

Gegen Strategie-Pattern und interne Weiterleitung und so sag ich garnichts. Ist ja nicht umsonst ein Pattern.
Es geht mir nur um wirklich getrennte Daten- und Funktionsklassen, in folgendem Sinne:


MeineNeueKomischeServicKlasse.ManipuliereMeinObjekt(obj);
MeineNormaleServiceKlasse.VerarbeiteMeinObjekt(obj); // fliegt raus, weil die neue Komische Klasse die Daten "kaputt" gemacht hat und obj sich nicht dagegen wehrt.
Was ich bis jetzt an Service-Orientierung geshen/gelesen/verstanden habe, läuft das immer so.

Wenn Service-Orientierung ehr wie folgt gemeint ist:


obj.Manipuliere();


public class ...
{
  public Manipuliere()
  {
    this.Property = Service.Verarbeite(this);
  }
}
Hab ich irgend wo was falsch Verstanden.

Weil Servic-Orientierung und Datenobjekte für mich heißt (vielleicht bald "hieß"), das die Datenklassen ungefähr so gekapselt sind wie eine System.Data.DataTable.

[offtopic]
@LaTino: An den Praktiker, ich bin jetzt einige Jahre im "realen Leben" angekommen und aus der Schule/Theori draußen. Und bis jetzt ist mir nur aufgefallen, dass es nur ein Problem beim umsetzen der Theorie in die Praxis giebt:
Den Programmiere.
Wenn man es nicht versucht/macht, klappt es nicht. Wenn man es aber macht (machen will), sehen echte Anwendungen auch aus wie aus dem Lehrbuch.
Natürlich hat man verloren, wenn man ca. 2GB Quellcode im "Theorie ist unmöglich"-Stiel gegenüber steht. Aber wenn man daran arbeitet wird auch der Berg mit den Jahren kleiner.

Naja, falls wir da weiter Diskotieren wollen, sollten wir wohl lieber in einen anderen Thread umziehen.
[/offtopic]

Ach ja, bevor ich's vergesse: Danke für die Diskusion. Endlich mal jemand, der "antwortet" :)

Gruß
Juy Juka
private Nachricht | Beiträge des Benutzers
LaTino
myCSharp.de - Experte

Avatar #avatar-4122.png


Dabei seit:
Beiträge: 3062
Herkunft: Thüringen

beantworten | zitieren | melden

Denkbar zum Bleistift:


interface IDataUpdater 
{
  void Update(DataObject o);
}
abstract class DataObject 
{ 
  public abstract IDataUpdater Updater { get; set; }
  abstract bool Synchronized { get; }
  public abstract void MarkSynchronized();

  public Update()
  {
      if(!this.Synchronized)
      {
        Updater.Update(this);
        this.MarkSynchronized();
      }
   }
}
//service, wird vom in diesem Bsp vom Kontext aufgerufen
[ServiceContract]
public void UpdateAddress(DataObject myAddress)
{
   //Service entscheidet über die konkrete Updatemethode
   myAddress.Updater = MethodFactory.GetUpdateMethod(myAddress.GetType());
   myAdress.Update();
}

Ich denke mal, du hast ein Problem damit, dass die Methoden u.U. mehr Informationen brauchen, als die Klasse sonst öffentlich machen müsste. Ist das der Fall, stimmt aber schon etwas anderes nicht: das würde bedeuten, dass der Datenzugriff abhängig ist von internen Strukturen der Klasse. Mit anderen Worten: die Datenzugriffsschicht ist nicht entkoppelt vom Datenmodell.

Entkopplung geht einher mit der Veröffentlichung von Schnittstellen, so ist das nunmal. Obiges Beispiel kann man sicher noch auf ein halbes Dutzend bessere Varianten umschreiben, soll nur zeigen, dass es legitim sein kann, bestimmte Methoden nicht nur in andere Klassen, sondern auch auf Services auszulagern.

Ich halte nicht viel davon, der Datenklasse neben den Informationen, die sie haben muss, auch noch eine Servicereferenz zu geben. Das sollte Aufgabe des Kontextes (lies: Controllers) sein.

LaTino
"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)
private Nachricht | Beiträge des Benutzers