Breche ich mit dieser Architektur die OOP-Praxis?


23

Ich habe eine Webanwendung. Ich glaube nicht, dass die Technologie wichtig ist. Die Struktur ist eine N-Tier-Anwendung (siehe Abbildung links). Es gibt 3 Schichten.

Benutzeroberfläche (MVC-Muster), Business Logic Layer (BLL) und Datenzugriffsschicht (DAL)

Das Problem, das ich habe, ist, dass meine BLL massiv ist, da sie die Logik und Pfade für den Aufruf der Anwendungsereignisse aufweist.

Ein typischer Fluss durch die Anwendung könnte sein:

In der Benutzeroberfläche ausgelöstes Ereignis, Übergang zu einer Methode in der BLL, Logik ausführen (möglicherweise in mehreren Teilen der BLL), schließlich zur DAL, zurück zur BLL (wo wahrscheinlich mehr Logik vorhanden ist) und dann einen bestimmten Wert an die Benutzeroberfläche zurückgeben.

Die BLL in diesem Beispiel ist sehr beschäftigt und ich überlege, wie ich das aufteilen kann. Ich habe auch die Logik und die Objekte kombiniert, die ich nicht mag.

Bildbeschreibung hier eingeben

Die Version auf der rechten Seite ist meine Anstrengung.

Die Logik ist immer noch der Ablauf der Anwendung zwischen Benutzeroberfläche und DAL, aber es gibt wahrscheinlich keine Eigenschaften ... Nur Methoden (die meisten Klassen in dieser Ebene können möglicherweise statisch sein, da sie keinen Status speichern). In der Poco-Ebene gibt es Klassen mit Eigenschaften (z. B. eine Personenklasse, in der Name, Alter, Größe usw. angegeben sind). Diese haben nichts mit dem Ablauf der Anwendung zu tun, sondern speichern nur den Status.

Der Fluss könnte sein:

Wird sogar über die Benutzeroberfläche ausgelöst und übergibt einige Daten an den UI-Layer-Controller (MVC). Dadurch werden die Rohdaten übersetzt und in das Poco-Modell konvertiert. Das POCO-Modell wird dann an die Logikebene (die die BLL war) und schließlich an die Befehlsabfrageebene übergeben, die möglicherweise unterwegs manipuliert wird. Die Befehlsabfrageebene konvertiert das POCO in ein Datenbankobjekt (das fast dasselbe ist, aber eines ist für die Persistenz vorgesehen, das andere für das Front-End). Das Element wird gespeichert und ein Datenbankobjekt wird an die Befehlsabfrageebene zurückgegeben. Es wird dann in ein POCO konvertiert, wo es zur Logikebene zurückkehrt, möglicherweise weiterverarbeitet wird und schließlich zur Benutzeroberfläche zurückkehrt

In der gemeinsam genutzten Logik und den Schnittstellen sind möglicherweise persistente Daten enthalten, z. B. MaxNumberOf_X und TotalAllowed_X sowie alle Schnittstellen.

Sowohl die gemeinsame Logik / Schnittstelle als auch DAL bilden die "Basis" der Architektur. Diese wissen nichts über die Außenwelt.

Alles andere als die gemeinsame Logik / Schnittstellen und DAL weiß über Poco.

Der Ablauf ist dem ersten Beispiel immer noch sehr ähnlich, aber es hat jede Ebene für eine Sache verantwortlicher gemacht (sei es der Zustand, der Ablauf oder irgendetwas anderes) ... aber breche ich die OOP mit diesem Ansatz?

Ein Beispiel für die Demo von Logic und Poco könnte sein:

public class LogicClass
{
    private ICommandQueryObject cmdQuery;
    public PocoA Method1(PocoB pocoB) 
    { 
        return cmdQuery.Save(pocoB); 
    }

    /*This has no state objects, only ways to communicate with other 
    layers such as the cmdQuery. Everything else is just function 
    calls to allow flow via the program */
    public PocoA Method2(PocoB pocoB) 
    {         
        pocoB.UpdateState("world"); 
        return Method1(pocoB);
    }

}

public struct PocoX
{
     public string DataA {get;set;}
     public int DataB {get;set;}
     public int DataC {get;set;}

    /*This simply returns something that is part of this class. 
     Everything is self-contained to this class. It doesn't call 
     trying to directly communicate with databases etc*/
     public int GetValue()
     {

         return DataB * DataC; 
     }

     /*This simply sets something that is part of this class. 
     Everything is self-contained to this class. 
     It doesn't call trying to directly communicate with databases etc*/
     public void UpdateState(string input)
     {        
         DataA += input;  
     }
}

An Ihrer Architektur, wie Sie sie gerade beschrieben haben, sehe ich grundsätzlich nichts Falsches.
Robert Harvey

19
Ihr Codebeispiel enthält nicht genügend funktionale Details, um weitere Einblicke zu gewähren. Foobar-Beispiele bieten selten ausreichende Veranschaulichung.
Robert Harvey


4
Können wir einen besseren Titel für diese Frage finden , so es kann Online leichter gefunden werden?
Soner Gönül

1
Um pedantisch zu sein: Eine Schicht und eine Schicht sind nicht dasselbe. Eine "Ebene" spricht von Bereitstellung, eine "Ebene" von Logik. Ihre Datenschicht wird sowohl auf der Serverseite als auch auf der Datenbankebene bereitgestellt. Ihre UI-Schicht wird sowohl auf der Web-Client- als auch auf der Server-Code-Ebene bereitgestellt. Die Architektur, die Sie anzeigen, besteht aus drei Ebenen. Ihre Ebenen sind "Web-Client", "Server-Side-Code" und "Datenbank".
Laurent LA RIZZA

Antworten:


55

Ja, Sie brechen sehr wahrscheinlich Kern OOP - Konzepte. Aber fühlen Sie sich nicht schlecht, die Leute tun dies die ganze Zeit, es bedeutet nicht, dass Ihre Architektur "falsch" ist. Ich würde sagen, es ist wahrscheinlich weniger wartbar als ein richtiges OO-Design, aber das ist eher subjektiv und nicht deine Frage. ( Hier ist ein Artikel von mir, der die n-Tier-Architektur im Allgemeinen kritisiert.)

Begründung : Das grundlegendste Konzept von OOP ist, dass Daten und Logik eine einzige Einheit (ein Objekt) bilden. Obwohl dies eine sehr vereinfachende und mechanische Aussage ist, wird sie in Ihrem Entwurf nicht wirklich befolgt (wenn ich Sie richtig verstehe). Sie trennen die meisten Daten ganz klar von den meisten der Logik. Beispielsweise werden zustandslose (statisch-ähnliche) Methoden als "Prozeduren" bezeichnet und sind im Allgemeinen OOP-entgegengesetzt.

Es gibt natürlich immer Ausnahmen, aber dieses Design verstößt in der Regel gegen diese Dinge.

Auch hier möchte ich betonen "verletzt OOP"! = "Falsch", daher ist dies nicht unbedingt ein Werturteil. Es hängt alles von Ihren Architekturbeschränkungen, Wartbarkeitsanwendungsfällen, Anforderungen usw. ab.


9
Habe eine Gegenstimme, dies ist eine gute Antwort. Wenn ich meine eigene schreibe, würde ich diese kopieren und einfügen, aber füge hinzu, dass du, wenn du feststellst, dass du keinen OOP-Code schreibst, vielleicht eine Nicht-OOP-Sprache als solche in Betracht ziehen solltest Mit viel zusätzlichem Overhead, auf den Sie verzichten können, wenn Sie ihn nicht verwenden
TheCatWhisperer,

2
@TheCatWhisperer: Moderne Unternehmensarchitekturen werfen OOP nicht vollständig weg, sondern nur selektiv (z. B. für DTOs).
Robert Harvey

@RobertHarvey Einverstanden, ich meinte, wenn Sie OOP kaum irgendwo in Ihrem Design verwenden
TheCatWhisperer

@TheCatWhisperer Viele der Vorteile in einem oop wie c # liegen nicht unbedingt im oop-Teil der Sprache, sondern in der verfügbaren Unterstützung wie Bibliotheken, Visual Studio, Speicherverwaltung usw.
Anzeigename

@Orangesandlemons Ich bin sicher, es gibt viele andere gut unterstützte Sprachen da draußen ...
TheCatWhisperer

31

Eines der Kernprinzipien der funktionalen Programmierung sind reine Funktionen.

Eines der Kernprinzipien der objektorientierten Programmierung besteht darin, Funktionen mit den Daten zu kombinieren, auf die sie einwirken.

Beide Grundprinzipien fallen weg, wenn Ihre Anwendung mit der Außenwelt kommunizieren muss. In der Tat können Sie diesen Idealen nur an einem speziell dafür vorgesehenen Ort in Ihrem System treu bleiben. Nicht jede Zeile Ihres Codes muss diese Ideale erfüllen. Aber wenn keine Zeile Ihres Codes diese Ideale erfüllt, können Sie nicht wirklich behaupten, OOP oder FP zu verwenden.

Es ist also in Ordnung, nur "Objekte" mit Daten zu haben, die Sie herumschleudern, da Sie diese benötigen, um eine Grenze zu überschreiten, die Sie einfach nicht umgestalten können, um den interessierten Code zu verschieben. Weiß nur, dass das nicht OOP ist. Das ist die Realität. OOP ist, wenn Sie innerhalb dieser Grenze alle Logik, die auf diese Daten wirkt, an einem Ort sammeln.

Nicht, dass du das auch tun müsstest. OOP ist nicht alles für alle Menschen. Es ist was es ist. Behaupte einfach nicht, dass etwas OOP folgt, wenn dies nicht der Fall ist, oder du wirst Leute verwirren, die versuchen, deinen Code zu pflegen.

Ihre POCOs scheinen eine gute Geschäftslogik zu haben, sodass ich mir keine Sorgen machen müsste, ob ich anämisch bin. Was mich betrifft ist, dass sie alle sehr wandelbar scheinen. Denken Sie daran, dass Getter und Setter keine echte Kapselung bieten. Wenn Ihr POCO auf diese Grenze zusteuert, ist das in Ordnung. Verstehen Sie nur, dass dies nicht die vollen Vorteile eines echten gekapselten OOP-Objekts bietet. Einige nennen dies ein Datenübertragungsobjekt oder DTO.

Ein Trick, den ich erfolgreich angewendet habe, ist das Herstellen von OOP-Objekten, die DTOs fressen. Ich benutze das DTO als Parameterobjekt . Mein Konstruktor liest den Status davon (als defensive Kopie gelesen ) und wirft ihn beiseite. Jetzt habe ich eine vollständig gekapselte und unveränderliche Version des DTO. Alle Methoden, die mit diesen Daten zu tun haben, können hierher verschoben werden, sofern sie sich auf dieser Seite der Grenze befinden.

Ich biete keine Getter oder Setter. Ich folge tell, frag nicht . Sie rufen meine Methoden auf und sie tun, was getan werden muss. Sie erzählen Ihnen wahrscheinlich nicht einmal, was sie getan haben. Sie machen es einfach.

Nun, irgendwann wird irgendwo etwas in eine andere Grenze stoßen und das alles fällt wieder auseinander. Das ist gut. Drehen Sie einen anderen DTO auf und werfen Sie ihn über die Mauer.

Dies ist die Essenz dessen, worum es bei der Architektur von Ports und Adaptern geht. Ich habe darüber aus einer funktionalen Perspektive gelesen . Vielleicht interessiert es dich auch.


5
" Getter und Setter bieten keine echte Kapselung " - ja!
Boris die Spinne

3
@ BoristheSpider - Getter und Setter bieten absolut Kapselung, sie passen einfach nicht zu Ihrer engen Definition von Kapselung.
Davor Ždralo

4
@ DavorŽdralo: Sie sind gelegentlich als Problemumgehung nützlich, aber aufgrund ihrer Natur lösen Getter und Setter die Kapselung auf. Das Bereitstellen eines Weges zum Abrufen und Festlegen einer internen Variablen ist das Gegenteil davon, für Ihren eigenen Zustand verantwortlich zu sein und danach zu handeln.
CHAO

5
@cHao - du verstehst nicht, was ein Getter ist. Dies ist keine Methode, die einen Wert einer Objekteigenschaft zurückgibt. Es ist eine übliche Implementierung, kann jedoch einen Wert aus einer Datenbank zurückgeben, über http abfragen und im laufenden Betrieb berechnen, was auch immer. Wie ich bereits sagte, brechen Getter und Setter die Kapselung nur, wenn Menschen ihre eigenen engen (und falschen) Definitionen verwenden.
Davor Ždralo

4
@cHao - Kapselung bedeutet, dass Sie die Implementierung verstecken. Das ist es, was eingekapselt wird. Wenn Sie für eine Square-Klasse den Getter "getSurfaceArea ()" verwenden, wissen Sie nicht, ob es sich bei der Oberfläche um ein Feld handelt, ob es im laufenden Betrieb berechnet wird (Rückgabehöhe * Breite) oder eine dritte Methode, sodass Sie die interne Implementierung ändern können wann immer Sie möchten, weil es eingekapselt ist.
Davor Ždralo

1

Wenn ich Ihre Erklärung richtig lese, sehen Ihre Objekte ein bisschen so aus: (knifflig ohne Kontext)

public class LogicClass
{
    private ICommandQueryObject cmdQuery;
    public PocoA Method(PocoB pocoB) { ... }
}

public class PocoX
{
     public string DataA {get;set;}
     public int DataB {get;set;}
     ... etc
}

Insofern enthalten Ihre Poco-Klassen nur Daten und Ihre Logic-Klassen die Methoden, die auf diese Daten einwirken. ja, du hast die Prinzipien von "Classic OOP" gebrochen

Wiederum ist es schwierig, anhand Ihrer allgemeinen Beschreibung zu sagen, aber ich würde riskieren, dass das, was Sie geschrieben haben, als anämisches Domänenmodell kategorisiert werden könnte.

Ich denke nicht, dass dies ein besonders schlechter Ansatz ist, und wenn Sie Ihre Pocos als Strukturen betrachten, wird der OOP im genaueren Sinne nicht sofort zerstört. Insofern sind Ihre Objekte jetzt die LogicClasses. Wenn Sie Ihren Pocos unveränderlich machen, kann das Design durchaus als funktional angesehen werden.

Wenn Sie jedoch auf Shared Logic, Pocos verweisen, die zwar fast identisch, aber nicht identisch sind, und Statik, mache ich mir langsam Sorgen um die Details Ihres Designs.


Ich habe meinem Beitrag hinzugefügt und im Wesentlichen Ihr Beispiel kopiert. Entschuldigung, es war nicht klar, mit
MyDaftQuestions

1
Ich meine, wenn Sie uns mitteilen würden, was die Anwendung macht, wäre es einfacher, Beispiele zu schreiben. Anstelle von LogicClass könnten Sie auch PaymentProvider oder was auch immer haben
Ewan

1

Ein potenzielles Problem, das ich in Ihrem Design gesehen habe (und das sehr häufig vorkommt) - der absolut schlimmste "OO" -Code, auf den ich jemals gestoßen bin, wurde durch eine Architektur verursacht, die "Data" -Objekte von "Code" -Objekten trennte. Das ist alptraumhaftes Zeug! Das Problem ist, dass Sie überall in Ihrem Geschäftscode, wo Sie auf Ihre Datenobjekte zugreifen möchten, dazu neigen, sie direkt in der Zeile zu codieren (Sie müssen nicht, Sie könnten eine Utility-Klasse oder eine andere Funktion erstellen, um damit umzugehen, aber genau das ist es Ich habe im Laufe der Zeit immer wieder gesehen).

Der Zugriffs- / Aktualisierungscode wird im Allgemeinen nicht erfasst, sodass Sie überall doppelte Funktionen erhalten.

Andererseits sind diese Datenobjekte beispielsweise als Datenbankpersistenz nützlich. Ich habe drei Lösungen ausprobiert:

Das Kopieren von Werten in "echte" Objekte und das Wegwerfen Ihres Datenobjekts ist mühsam (kann aber eine gültige Lösung sein, wenn Sie diesen Weg einschlagen möchten).

Das Hinzufügen von Daten-Wrangling-Methoden zu den Datenobjekten kann funktionieren, aber es kann zu einem großen chaotischen Datenobjekt führen, das mehr als eine Aufgabe erfüllt. Es kann auch die Kapselung erschweren, da viele Persistenzmechanismen öffentliche Zugriffsmechanismen benötigen ... Ich habe es nicht geliebt, als ich es getan habe, aber es ist eine gültige Lösung

Die Lösung, die sich für mich am besten bewährt hat, ist das Konzept einer "Wrapper" -Klasse, die die "Data" -Klasse kapselt und alle Funktionen zum Verwirren von Daten enthält - dann mache ich die Datenklasse überhaupt nicht verfügbar (nicht einmal Setter und Getter) es sei denn, sie werden unbedingt benötigt). Dies beseitigt die Versuchung, das Objekt direkt zu manipulieren, und zwingt Sie, dem Wrapper stattdessen gemeinsame Funktionen hinzuzufügen.

Der andere Vorteil ist, dass Sie sicherstellen können, dass sich Ihre Datenklasse immer in einem gültigen Zustand befindet. Hier ist ein kurzes Pseudocode-Beispiel:

// Data Class
Class User {
    String name;
    Date birthday;
}

Class UserHolder {
    final private User myUser // Cannot be null or invalid

    // Quickly wrap an object after getting it from the DB
    public UserHolder(User me)
    {
        if(me == null ||me.name == null || me.age < 0)
            throw Exception
        myUser=me
    }

    // Create a new instance in code
    public UserHolder(String name, Date birthday) {
        User me=new User()
        me.name=name
        me.birthday=birthday        
        this(me)
    }
    // Methods access attributes, they try not to return them directly.
    public boolean canDrink(State state) {
        return myUser.birthday.year < Date.yearsAgo(state.drinkingAge) 
    }
}

Beachten Sie, dass die Altersüberprüfung in Ihrem Code nicht auf verschiedene Bereiche verteilt ist und dass Sie auch nicht versucht sind, sie zu verwenden, da Sie nicht einmal herausfinden können, was der Geburtstag ist (es sei denn, Sie benötigen sie für etwas anderes) Welchen Fall können Sie hinzufügen).

Ich neige dazu, das Datenobjekt nicht nur zu erweitern, weil Sie diese Kapselung und die Sicherheitsgarantie verlieren - an diesem Punkt können Sie die Methoden auch einfach der Datenklasse hinzufügen.

Auf diese Weise ist in Ihrer Geschäftslogik kein Haufen Junk / Iteratoren für den Datenzugriff verteilt, sondern sie ist viel besser lesbar und weniger redundant. Ich empfehle auch, sich daran zu gewöhnen, Sammlungen immer aus dem gleichen Grund zu verpacken - indem ich Konstrukte aus Ihrer Geschäftslogik heraushalte und sicherstelle, dass sie immer in einem guten Zustand sind.


1

Ändern Sie niemals Ihren Code, weil Sie denken oder jemand Ihnen sagt, dass es nicht das oder nicht das ist. Ändern Sie Ihren Code, wenn es Probleme gibt und Sie einen Weg gefunden haben, diese Probleme zu vermeiden, ohne andere zu erstellen.

Abgesehen davon, dass Sie Dinge nicht mögen, möchten Sie viel Zeit investieren, um Änderungen vorzunehmen. Notieren Sie die Probleme, die Sie gerade haben. Schreiben Sie auf, wie Ihr neues Design die Probleme lösen würde. Ermitteln Sie den Wert der Verbesserung und die Kosten für die Durchführung Ihrer Änderungen. Stellen Sie dann - und das ist am wichtigsten - sicher, dass Sie die Zeit haben, diese Änderungen abzuschließen, oder Sie werden halb in diesem Zustand, halb in diesem Zustand enden, und das ist die schlimmste mögliche Situation. (Ich habe einmal an einem Projekt mit 13 verschiedenen Arten von Zeichenfolgen gearbeitet und drei identifizierbare halbherzige Anstrengungen unternommen, um auf eine Art zu standardisieren.)


0

Die Kategorie "OOP" ist viel größer und abstrakter als das, was Sie beschreiben. Das alles kümmert es nicht. Es geht um klare Verantwortung, Zusammenhalt, Kopplung. Auf der Ebene, die Sie anfragen, ist es also nicht sinnvoll, nach der "OOPS-Praxis" zu fragen.

Das heißt, zu Ihrem Beispiel:

Mir scheint, dass es ein Missverständnis darüber gibt, was MVC bedeutet. Sie nennen Ihre Benutzeroberfläche "MVC", getrennt von Ihrer Geschäftslogik und "Back-End" -Steuerung. Aber für mich beinhaltet MVC die gesamte Webanwendung:

  • Modell - enthält die Geschäftsdaten + Logik
    • Datenschicht als Implementierungsdetail des Modells
  • Ansicht - UI-Code, HTML-Vorlagen, CSS usw.
    • Beinhaltet clientseitige Aspekte wie JavaScript oder die Bibliotheken für "einseitige" Webanwendungen usw.
  • Kontrolle - der serverseitige Klebstoff zwischen allen anderen Teilen
  • (Es gibt Erweiterungen wie ViewModel, Batch usw., auf die ich hier nicht eingehen werde.)

Hier gibt es einige außerordentlich wichtige Grundannahmen:

  • Eine Model-Klasse / ein Model-Objekt hat keinerlei Kenntnis über die anderen Teile (View, Control, ...). Es ruft sie niemals auf, nimmt nicht an, von ihnen aufgerufen zu werden, es bekommt keine Sitzungsattribute / -parameter oder irgendetwas anderes in dieser Richtung. Es ist ganz alleine. In Sprachen, die dies unterstützen (z. B. Ruby), können Sie eine manuelle Befehlszeile starten, Modellklassen instanziieren, nach Herzenslust damit arbeiten und alles tun, was sie tun, ohne eine Instanz von Control oder View oder einer anderen Kategorie. Es hat keine Kenntnisse über Sitzungen, Benutzer usw., was am wichtigsten ist.
  • Nichts berührt die Datenschicht außer durch ein Modell.
  • Die Ansicht hat nur eine leichte Berührung des Modells (Anzeigen usw.) und sonst nichts. (Beachten Sie, dass eine gute Erweiterung "ViewModel" ist. Hierbei handelt es sich um spezielle Klassen, die eine umfangreichere Verarbeitung zum Rendern von Daten auf komplizierte Weise ausführen, die weder in Model noch in View gut passen reines Modell).
  • Die Steuerung ist so einfach wie möglich, aber sie ist dafür verantwortlich, alle anderen Spieler zusammenzuführen und Dinge zwischen ihnen zu übertragen (dh Benutzereingaben aus einem Formular zu extrahieren und an das Modell weiterzuleiten, Ausnahmen aus der Geschäftslogik an ein nützliches weiterzuleiten) Fehlermeldungen für den Benutzer usw.). Bei Web- / HTTP- / REST-APIs usw. finden alle Berechtigungen, Sicherheitsfunktionen, Sitzungsverwaltung, Benutzerverwaltung usw. hier (und nur hier) statt.

Wichtig: Die Benutzeroberfläche ist Teil von MVC. Nicht umgekehrt (wie in Ihrem Diagramm). Wenn Sie das akzeptieren, dann sind fette Models eigentlich ziemlich gut - vorausgesetzt, sie enthalten tatsächlich nichts, was sie nicht sollten.

Beachten Sie, dass "Fat Models" bedeutet, dass sich die gesamte Geschäftslogik in der Kategorie "Model" befindet (Paket, Modul, unabhängig vom Namen in der Sprache Ihrer Wahl). Einzelne Klassen sollten offensichtlich gemäß den von Ihnen selbst festgelegten Codierungsrichtlinien (z. B. einige maximale Codezeilen pro Klasse oder pro Methode usw.) OOP-strukturiert sein.

Beachten Sie auch, dass die Implementierung der Datenschicht sehr wichtige Konsequenzen hat. Insbesondere, ob die Modellschicht ohne Datenschicht funktionieren kann (z. B. für Komponententests oder für billige In-Memory-DBs auf dem Entwickler-Laptop anstelle von teuren Oracle-DBs oder was auch immer Sie haben). Dies ist jedoch wirklich ein Implementierungsdetail auf der Ebene der Architektur, auf der wir uns gerade befinden. Offensichtlich möchten Sie hier noch eine Trennung haben, dh ich möchte keinen Code sehen, der eine reine Domänenlogik hat, die direkt mit dem Datenzugriff verschachtelt ist, und dies intensiv miteinander koppelt. Ein Thema für eine andere Frage.

Um auf Ihre Frage zurückzukommen: Es scheint mir, dass es eine große Überschneidung zwischen Ihrer neuen Architektur und dem von mir beschriebenen MVC-Schema gibt, sodass Sie nicht auf einem völlig falschen Weg sind, aber Sie scheinen entweder einige Dinge neu zu erfinden, oder verwenden Sie es, weil Ihre aktuelle Programmierumgebung / Bibliotheken dies vorschlagen. Schwer zu sagen für mich. Daher kann ich Ihnen keine genaue Antwort geben, ob das, was Sie beabsichtigen, besonders gut oder schlecht ist. Sie können herausfinden, ob für jedes einzelne "Ding" genau eine Klasse verantwortlich ist. ob alles sehr kohäsiv und niedrig gekoppelt ist. Das gibt Ihnen einen guten Anhaltspunkt und ist meiner Meinung nach genug für ein gutes OOP-Design (oder, wenn Sie so wollen, einen guten Maßstab dafür).

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.