Laden...

Unterschied: Separation of Concerns <-> Schichtenmodell

Erstellt von MarzAttak vor 13 Jahren Letzter Beitrag vor 13 Jahren 2.020 Views
M
MarzAttak Themenstarter:in
18 Beiträge seit 2008
vor 13 Jahren
Unterschied: Separation of Concerns <-> Schichtenmodell

Hi,

bitte eine Nachfrage sei erlaubt. Habe in einem anderen Thread von Separation of Concerns gelesen. Hier im Forum nachgesucht, gegoogelt und dann grob verstanden, worum es sich handelt.
Was ich nicht verstehe ist, wo genau der Unterschied zum Schichtenmodell liegt. In dem anderen Thread Entitätsklassen und Schichten: Hat man eine 1:1 Beziehung zwischen Datenmodell und Objektmodell? wurden die Concerns so dargestellt, dass es eins für die GUI, eines für Domänenlogik und eines für Persistenz gäbe.
Ich verstehe jetzt nicht, wo der Unterschied zu einer Anwenderschicht, einer Schicht für Geschäftslogik und einer Datenschicht ist.

Es klingt für mich doch sehr ähnlich. Ist es das Gleiche nur aus einem anderen Blickwinkel oder was folgt da praktisch draus, wenn man seine Anwendung nicht nach Schichten, sondern nach Concerns aufbaut???

Neugierige Grüße
Marz

5.742 Beiträge seit 2007
vor 13 Jahren

Hallo MarzAttak,

SoC ist weitaus grundlegender als ein Schichtenmodell.
Letzteres ist quasi ein Architekturpattern, welches SoC vorantreiben soll.
Eine Schichtenanwendung realisiert also Ansprüche von SoC.

SoC kann man jedoch aber auch sehr gut ohne Schichtenarchitekturen erreichen - und innerhalb der Schichten tut man ebenfalls gut daran, Anliegen voneinander zu trennen.

wenn man seine Anwendung nicht nach Schichten, sondern nach Concerns aufbaut???

Eine Schicht ist ja an sich auch nichts anderes als ein Concern - eine Schicht ist für das UI zuständig, eine für den DB-Zugriff, ... (alles einzelne Anliegen).

Der Unterschied liegt vor allem in der Dimension: SoC lässt sich sowohl im "großen" (auf Ebene der gesamten Anwendung) als auch im kleinen anwenden (auf Klassen- und Methodenebene - sogar auf Anweisungsebene).
Schichten hingegen sind sozusagen SoC auf Anwendungsebene.

M
MarzAttak Themenstarter:in
18 Beiträge seit 2008
vor 13 Jahren

Danke für die Antwort. Ich kann also Schichten als Untermenge oder vielleicht besser gesagt, als eine mögliche Anwendung von SoC ansehen.
Und SoC zu meinem Leitstern in der Anwendungsentwicklung machen 😄.

Ich hätte da aber noch eine praktische Frage.

Nehmen wir mal als Beispiel eine Finanzsoftware in der Buchungen (Einnahme/Ausgaben) als Objekte dargestellt werden.
Man würde als Persistenzschicht eine DB laufen lassen und ein UI basteln, damit der Anwender solche Buchungen abrufen und bearbeiten kann. Sowas in der Art will ich demnächst als Lernaufgabe machen.
Zur Geschäftslogik gehört nun natürlich auch, dass man sich für einen gegebenen Zeitraum alle hierzu anfallenden Buchungen ausgeben lassen kann.
Man könnte das jetzt so umsetzen, dass die Buchungsobjekte eine Methode enthalten, in der sie den Suchzeitraum als Parameter entgegennehmen und sich in irgendeiner Form bei der Geschäftslogik melden, wenn sie von der Suche betroffen sind (zB sich selbst zurückgeben, so dass eine Liste aller betroffenen Buchungen entsteht). In der Geschäftslogikschicht würde man das dann weiterverarbeiten (Report aufbereiten o.ä.).

Wäre das ein Verstoß gegen das SoC-Prinzip?

Man könnte ja ebenso im Namespace der Geschäftlogik selbst eine eigene Funktion schreiben, welche die Gültigkeitszeiträume der Buchungsobjekte abprüft und dann entsprechend weitermacht.

Ähnlich ist es mit der Persistenz. Das Herausholen und das Abspeichern in der DB kann ich als Methode im Objekt anlegen, ich kann es natürlich auch als separate Funktion in einen Namespace legen, den ich 'Persistenzschicht' nenne.

Also letztlich die Frage: wie schlau mache ich meine Klassen/Objekte? Beschränke ich sie darauf, Daten zu halten und Lowlevel-Validierung über den Setter durchzuführen, oder sollen die Objekte über ihre Methoden aktiv in der Anwendung mitmischen?

Liebe Grüße
Marz

3.511 Beiträge seit 2005
vor 13 Jahren

Hallo,

auch hier gibt es verschiedene Möglichkeiten. Z.B. "Active Records". Dieses Pattern hält vor, dass sich die Objekte selber um die Persistenz kümmern. Das also z.B. die Klasse Person die üblichen CRUD Methoden enthalten. Dann gibt es noch das "Repository Pattern". Hier werden die CRUD Methoden über eigenständige Klassen behandelt. Ich persönlich halte von dem Active Record Pattern nichts, da es IMHO gegen SoC verstößt.

Zu deinem Beispiel:
Das ganze würde ich in drei Klassen unterteilen. Erstens die Buchung. Zweitens das Repository für die Buchungen. Und drittens ein Objekt, welches für die Suchanfragen benutzt wird*.

Im Prinzip also folgendes:


public class Buchung
{
  public Guid Id { get; set; }
  public decimal Betrag { get; set; }
  public DateTime Zeitpunkt { get; set; }
}

public class BuchungsQuery
{
  public decimal Betrag { get; set; }
  public DateTime Von { get; set; }
  public DateTime Bis { get; set; }
}

public class BuchungsRepository
{
  // Sonstiges Methoden (GetByID, Save, Update, Delete, etc...)
  IEnumerable<Buchung> GetByQuery(BuchungsQuery query);
}

Wenn du jetzt Buchungen über einen bestimmten Zeitraum haben willst, kannst du einfach schreiben:


BuchungsQuery query = new BuchungsQuery() { Von = DateTime.Now, Bis = DateTime.Now + 10 };
var result = BuchungsRepository.GetByQuery(query);

Dieses Verhalten entspricht IMHO SoC, da Objekt, Suche und Logik eigenständig sind und jede Klasse ihre eigene einzelne Bedeutung hat.

Zu deiner Validierungsfrage:
Die Objekte sollten IDataErrorInfo implementieren, um Validierungsmöglichkeiten zu erhalten. Die Validierung selber würde ich aber dennoch auslagern. Sprich, innerhalb von IDataErrorInfo werden nur spezielle Klassen wiederum aufgerufen. Den Objekten selber würde ich nur das nötigste an Eigenständigkeit beibringen. Ebenso sollten alles Businessobjekte INotifyPropertyChanged implementieren.

  • Nennt sich auch Extended Query Object Pattern.

"Jedes Ding hat drei Seiten, eine positive, eine negative und eine komische." (Karl Valentin)

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo MarzAttak,

wie schlau mache ich meine Klassen/Objekte? Beschränke ich sie darauf, Daten zu halten und Lowlevel-Validierung über den Setter durchzuführen, oder sollen die Objekte über ihre Methoden aktiv in der Anwendung mitmischen?

das hatten wir schon sehr ausführlich in 3-Schichten-Design?? [harte vs. weiche (Daten-)Objekte / OOP vs. ActiveRecods] (meine Sichtweite steht in 3-Schichten-Design?? [harte vs. weiche (Daten-)Objekte / OOP vs. ActiveRecods] und den darauf folgenden Beiträgen), so dass wir diese Frage hier nicht weiter erörtern sollten. Für meine Begriffe ist diese Frage ein bisschen neben dem eigentlichen Thema, denn nach dem Titel geht es ja um den "Unterschied: Separation of Concerns <-> Schichtenmodell". Bitte beim eigentlichen Thema bleiben.

herbivore

5.742 Beiträge seit 2007
vor 13 Jahren

Beschränke ich sie darauf, Daten zu halten und Lowlevel-Validierung über den Setter durchzuführen

Ein Beispiel hierzu bezogen auf SoC:
Natürlich könntest du hier in den Setter jede Menge if-Abfragen schreiben, die die Werte validieren.
Deutlich "separierter" sind jedoch die Anliegen, wenn du mit Validation Rules arbeitest - also sozusagen im Setter nur eine Zeile:


//statt:
if (value > 20 || value == 42 || value % 5 == 1)
   //Ungültigen Wert behandeln

//also:
if (!this.ValidateValue(FooProperty, value))
   //Ungültigen Wert behandeln

Somit hast du auch wieder SoC betrieben: Nicht mehr der getter selbst validiert (er setzt lediglich die Validierung in Gang), sondern die ValidationRule.
Die ValidationRules kannst du dann irgendwie in die Klasse injizieren und evtl. sogar aus XML-Dateien laden (wobei die Klasse selbst natürlich keinen Dateizugriff vornehmen soll - aufgrund von SoC).

Wo diese Validierung genau stattfindet und was bei Validierungsfehlern passieren soll, wurde ja in den von herbivore genannten Threads diskutiert.

71 Beiträge seit 2010
vor 13 Jahren

Du kannst Concern mit Zweck übersetzen. Code gehört unterschiedlichen Concerns an, wenn er grundsätzlich unterschiedliche Zwecke hat.

Ist der Zweck von einer Dialogbox derselbe wie der eines DB-Adapters oder der Berechnung der Kreditwürdigkeit eines Kunden?

Kaum. Also gehören sie unterschiedlichen Concerns an und sind sauber zu trennen.

Ist Validation derselbe Zweck wie Berechnung der Kreditwürdigkeit? Kaum. Also saubere Trennung.

Zwecke/Concerns stehen in einer Hierarchie. Kundendaten zu validieren und Kundenkreditwürdigkeit zu berechnen liegen da natürlich näher zusammen als Kundenkreditwürdigkeit berechnen und Kundenpersistenz.

Je weiter auseinander die Zwecke, desto deutlicher die Trennung zwischen den Funktionseinheiten, die sie realisieren.

Fundamentale Zwecke wie Darstellung, Persistenz, Domänenlogik, Sicherheit, Skalierbarkeit sind dann am besten in je eigenen Komponenten aufgehoben.

M
MarzAttak Themenstarter:in
18 Beiträge seit 2008
vor 13 Jahren

Danke an Khalid und WinSharp93 dafür, dass ihr so konkret auf mein Beispiel eingegangen seid. Ich knabber noch ein wenig daran, alles bis ins Detail zu verstehen, aber generell ist mir das SoC dadurch schon viel klarer geworden.

@herbivore
*seufz* und ich hatte schon überlegt, ob ich dafür einen eigenen Thread aufmache... den verlinkten Artikel hatte ich schon gelesen und habe ihn jetzt nochmal durchgekaut. Er ist aber auf einem Level, wo ich vieles noch nicht verstehe. Zum Anfang ist aber ein Link in ein Blog, wo unglaublich gut erklärt wird.

@Ralf
Danke für die Erläuterung. Die Definition zu SoC habe ich im Prinzip über den Wikipedia-Eintrag zu Crosscutting-Concerns rausbekommen. Die Google-Einträge zu SoC waren dermaßen technisch, dass sie mir nicht groß weiterhalfen. Danke auch für den Hinweis, wann eine Trennung von Zwecken in unterschiedliche Concerns (Stichwort: je weiter auseinander, desto eher) angezeigt ist.

Liebe Grüße
Marz