Reduzierung der Komplexität einer Klasse


10

Ich habe mir einige Antworten angesehen und bei Google gesucht, konnte aber nichts Hilfreiches finden (dh das hätte keine unangenehmen Nebenwirkungen).

Mein abstraktes Problem ist, dass ich ein Objekt habe und eine lange Abfolge von Operationen daran ausführen muss. Ich betrachte es als eine Art Fließband, als würde man ein Auto bauen.

Ich glaube, diese Objekte würden Methodenobjekte genannt .

In diesem Beispiel hätte ich also irgendwann eine CarWithoutUpholstery, auf der ich dann installBackSeat, installFrontSeat, installWoodenInserts ausführen müsste (die Vorgänge stören sich nicht gegenseitig und könnten sogar parallel ausgeführt werden). Diese Operationen werden von CarWithoutUpholstery.worker () ausgeführt und ergeben ein neues Objekt, das eine CarWithUpholstery wäre, auf der ich dann möglicherweise cleanInsides (), verifyNoUpholsteryDefects () usw. ausführen würde.

Die Operationen in einer einzelnen Phase sind bereits unabhängig, dh ich habe bereits mit einer Teilmenge davon zu kämpfen, die in beliebiger Reihenfolge ausgeführt werden kann (vordere und hintere Sitze können in beliebiger Reihenfolge installiert werden).

Meine Logik verwendet derzeit Reflection, um die Implementierung zu vereinfachen.

Das heißt, sobald ich eine CarWithoutUpholstery habe, überprüft sich das Objekt selbst auf Methoden namens performSomething (). Zu diesem Zeitpunkt werden alle folgenden Methoden ausgeführt:

myObject.perform001SomeOperation();
myObject.perform002SomeOtherOperation();
...

beim Überprüfen von Fehlern und Sachen. Während der Reihenfolge der Operation ist unwichtig, habe ich eine lexikographische Ordnung im Fall zugewiesen immer ich entdecken einige Reihenfolge nach alle wichtig ist. Dies widerspricht YAGNI , kostet aber sehr wenig - eine einfache Sortierung () - und könnte eine massive Umbenennung der Methode (oder die Einführung einer anderen Methode zur Durchführung von Tests, z. B. einer Reihe von Methoden) auf der ganzen Linie ersparen.

Ein anderes Beispiel

Sagen wir, anstatt ein Auto zu bauen, muss ich einen Geheimpolizeibericht über jemanden erstellen und ihn meinem bösen Oberherrn vorlegen . Mein letztes Objekt wird ein ReadyReport sein. Um es zu konstruieren, sammle ich zunächst grundlegende Informationen (Vor- und Nachname, Ehepartner ...). Dies ist meine Phase A. Je nachdem, ob es einen Ehepartner gibt oder nicht, muss ich möglicherweise mit den Phasen B1 oder B2 fortfahren und Sexualitätsdaten für eine oder zwei Personen sammeln. Dies besteht aus mehreren verschiedenen Anfragen an verschiedene Evil Minions, die das Nachtleben, Straßenkameras, Sexshop-Verkaufsbelege und was nicht kontrollieren. Und so weiter und so fort.

Wenn das Opfer keine Familie hat, werde ich nicht einmal in die GetInformationAboutFamily-Phase eintreten, aber wenn ich dies tue, ist es unerheblich, ob ich zuerst den Vater oder die Mutter oder die Geschwister (falls vorhanden) anvisiere. Das kann ich aber nicht, wenn ich keinen FamilyStatusCheck durchgeführt habe, der daher zu einer früheren Phase gehört.

Es funktioniert alles wunderbar ...

  • Wenn ich eine zusätzliche Operation benötige, muss ich nur eine private Methode hinzufügen.
  • Wenn die Operation mehreren Phasen gemeinsam ist, kann ich sie von einer Oberklasse erben lassen.
  • Operationen sind einfach und in sich geschlossen. Kein Wert von einer Operation wird jemals von einer der anderen benötigt (Operationen, die in einer anderen Phase ausgeführt werden).
  • Objekte auf der ganzen Linie müssen nicht viele Tests durchführen, da sie nicht einmal existieren könnten , wenn ihre Erstellerobjekte diese Bedingungen überhaupt nicht überprüft hätten. Dh, wenn Einsätze im Armaturenbrett platziert, das Armaturenbrett Reinigen und das Armaturenbrett zu überprüfen, muß ich nicht überprüfen, ob ein Armaturenbrett tatsächlich ist es .
  • es ermöglicht ein einfaches Testen. Ich kann ein Teilobjekt leicht verspotten und eine beliebige Methode darauf ausführen, und alle Operationen sind deterministische Blackboxen.

...aber...

Das Problem trat auf, als ich eine letzte Operation in eines meiner Methodenobjekte einfügte, wodurch das Gesamtmodul einen obligatorischen Komplexitätsindex überschritt ("weniger als N private Methoden").

Ich habe die Angelegenheit bereits nach oben gebracht und vorgeschlagen, dass in diesem Fall der Reichtum an privaten Methoden kein Hinweis auf eine Katastrophe ist. Die Komplexität ist da, aber es ist da , weil der Betrieb ist komplex, und es ist eigentlich gar nicht so komplex - es ist nur ist lang .

Am Beispiel des Evil Overlord besteht mein Problem darin, dass der Evil Overlord (auch bekannt als He Who Shall Not Denied ) alle Ernährungsinformationen angefordert hat. Meine Dietary Minions haben mir mitgeteilt, dass ich Restaurants, Küchenzeilen, Straßenverkäufer, nicht lizenzierte Straßenverkäufer und Gewächshäuser abfragen muss Besitzer usw. und der böse (Unter-) Overlord - bekannt als Er, der auch nicht geleugnet werden soll - beschweren sich, dass ich in der GetDietaryInformation-Phase zu viele Abfragen durchführe.

Hinweis : Mir ist bewusst, dass dies unter verschiedenen Gesichtspunkten überhaupt kein Problem darstellt (Ignorieren möglicher Leistungsprobleme usw.). Alles, was passiert, ist, dass eine bestimmte Metrik unglücklich ist, und es gibt eine Rechtfertigung dafür.

Was ich denke, ich könnte tun

Abgesehen von der ersten sind alle diese Optionen machbar und meiner Meinung nach vertretbar.

  • Ich habe überprüft, dass ich hinterhältig sein und die Hälfte meiner Methoden deklarieren kann protected. Aber ich würde eine Schwäche im Testverfahren ausnutzen und abgesehen davon, dass ich mich rechtfertige, wenn ich erwischt werde, mag ich das nicht. Es ist auch eine Notlösung. Was ist, wenn sich die Anzahl der erforderlichen Operationen verdoppelt? Unwahrscheinlich, aber was dann?
  • Ich kann diese Phase willkürlich in AnnealedObjectAlpha, AnnealedObjectBravo und AnnealedObjectCharlie aufteilen und habe ein Drittel der Operationen, die in jeder Phase ausgeführt werden. Ich habe den Eindruck, dass dies tatsächlich die Komplexität erhöht (N-1 weitere Klassen), ohne dass dies von Vorteil ist, außer dass ein Test bestanden wird. Ich kann natürlich davon ausgehen, dass ein CarWithFrontSeatsInstalled und ein CarWithAllSeatsInstalled logisch aufeinanderfolgende Stufen sind. Das Risiko, dass Alpha später eine Bravo-Methode benötigt, ist gering und noch geringer, wenn ich sie gut spiele. Aber dennoch.
  • Ich kann verschiedene Operationen, die aus der Ferne ähnlich sind, in einer einzigen zusammenfassen. performAllSeatsInstallation(). Dies ist nur eine Notlösung und erhöht die Komplexität der einzelnen Operation. Wenn ich die Operationen A und B jemals in einer anderen Reihenfolge ausführen muss und sie in E = (A + C) und F (B + D) gepackt habe, muss ich E und F entbündeln und den Code mischen .
  • Ich kann eine Reihe von Lambda-Funktionen verwenden und die Prüfung insgesamt ordentlich umgehen, aber ich finde das klobig. Dies ist jedoch die bisher beste Alternative. Es würde das Nachdenken loswerden. Die beiden Probleme, die ich habe, sind, dass ich wahrscheinlich gebeten werde, alle Methodenobjekte neu zu schreiben , nicht nur die hypothetischen CarWithEngineInstalled, und obwohl dies eine sehr gute Arbeitsplatzsicherheit wäre, spricht es wirklich nicht allzu viel an. und dass der Code Coverage Checker Probleme mit Lambdas hat (die lösbar sind, aber immer noch ).

Damit...

  • Was ist meiner Meinung nach meine beste Option?
  • Gibt es einen besseren Weg, den ich nicht in Betracht gezogen habe? ( Vielleicht sollte ich besser sauber kommen und direkt fragen, was es ist? )
  • Ist dieses Design hoffnungslos fehlerhaft, und ich gebe besser Niederlage und Graben zu - diese Architektur insgesamt? Nicht gut für meine Karriere, aber wäre es auf lange Sicht besser, schlecht gestalteten Code zu schreiben?
  • Ist meine derzeitige Wahl tatsächlich der One True Way, und ich muss kämpfen, um bessere Qualitätsmetriken (und / oder Instrumente) zu installieren? Für diese letzte Option benötige ich Referenzen ... Ich kann nicht einfach mit der Hand auf das @PHB winken, während ich murmle. Dies sind nicht die Metriken, nach denen Sie suchen . Egal wie gerne ich dazu in der Lage wäre

3
Sie können auch die Warnung "Maximale Komplexität überschritten" ignorieren.
Robert Harvey

Wenn ich könnte würde ich. Irgendwie hat diese Metrik-Sache eine ganz eigene heilige Qualität erlangt. Ich versuche weiterhin, die PTB davon zu überzeugen, sie als nützliches Werkzeug zu akzeptieren, aber nur als Werkzeug. Ich kann noch erfolgreich sein ...
LSerni

Konstruieren die Operationen tatsächlich ein Objekt oder verwenden Sie nur die Fließbandmetapher, weil sie die von Ihnen erwähnte spezifische Eigenschaft gemeinsam hat (viele Operationen mit einer partiellen Abhängigkeitsreihenfolge)? Die Bedeutung ist, dass das erstere das Builder-Muster vorschlägt, während das letztere möglicherweise ein anderes Muster mit mehr Details
vorschlägt

2
Die Tyrannei der Metriken. Metriken sind nützliche Indikatoren, aber es ist nicht hilfreich, sie durchzusetzen, während ignoriert wird, warum diese Metrik verwendet wird.
Jaydee

2
Erklären Sie den Leuten oben den Unterschied zwischen wesentlicher Komplexität und zufälliger Komplexität. en.wikipedia.org/wiki/No_Silver_Bullet Wenn Sie sicher sind , dass die Komplexität, die die Regel bricht wesentlich ist, dann , wenn Sie refactor pro programmers.stackexchange.com/a/297414/51948 Sie möglicherweise sind Skaten die Regel um und Verbreitung aus der Komplexität. Wenn das Einkapseln von Dingen in Phasen nicht willkürlich ist (die Phasen beziehen sich auf das Problem) und das Redesign die kognitive Belastung für Entwickler, die den Code pflegen, verringert, ist dies sinnvoll.
Fuhrmanator

Antworten:


15

Die lange Abfolge von Operationen scheint eine eigene Klasse zu sein, die dann zusätzliche Objekte benötigt, um ihre Aufgaben zu erfüllen. Es hört sich so an, als wären Sie dieser Lösung nahe. Dieser lange Vorgang kann in mehrere Schritte oder Phasen unterteilt werden. Jeder Schritt oder jede Phase kann eine eigene Klasse sein, die eine öffentliche Methode verfügbar macht. Sie möchten, dass jede Phase dieselbe Schnittstelle implementiert. Dann verfolgt Ihre Montagelinie eine Liste von Phasen.

Stellen Sie sich die CarKlasse vor, um auf Ihrem Beispiel für die Fahrzeugmontage aufzubauen :

public class Car
{
    public IChassis Chassis { get; private set; }
    public Dashboard { get; private set; }
    public IList<Seat> Seats { get; private set; }

    public Car()
    {
        Seats = new List<Seat>();
    }

    public void AddChassis(IChassis chassis)
    {
        Chassis = chassis;
    }

    public void AddDashboard(Dashboard dashboard)
    {
        Dashboard = dashboard;
    }
}

Ich werde die Implementierungen von einigen der Komponenten wegzulassen, wie IChassis, Seatund Dashboardaus Gründen der Kürze.

Der Carist nicht für den Bau selbst verantwortlich. Das Fließband ist, also lassen Sie uns das in einer Klasse zusammenfassen:

public class CarAssembler
{
    protected List<IAssemblyPhase> Phases { get; private set; }

    public CarAssembler()
    {
        Phases = new List<IAssemblyPhase>()
        {
            new ChassisAssemblyPhase(),
            new DashboardAssemblyPhase(),
            new SeatAssemblyPhase()
        };
    }

    public void Assemble(Car car)
    {
        foreach (IAssemblyPhase phase in Phases)
        {
            phase.Assemble(car);
        }
    }
}

Der wichtige Unterschied besteht darin, dass der CarAssembler über eine Liste der implementierten Assembler-Phasenobjekte verfügt IAssemblyPhase. Sie beschäftigen sich jetzt explizit mit öffentlichen Methoden, was bedeutet, dass jede Phase für sich getestet werden kann und Ihr Codequalitäts-Tool zufriedener ist, weil Sie nicht so viel in eine einzelne Klasse stecken. Der Montageprozess ist ebenfalls kinderleicht. AssembleÜberqueren Sie einfach die Phasen und rufen Sie im Auto vorbei. Erledigt. Die IAssemblyPhaseSchnittstelle ist mit nur einer einzigen öffentlichen Methode, die ein Autoobjekt übernimmt, kinderleicht:

public interface IAssemblyPhase
{
    void Assemble(Car car);
}

Jetzt brauchen wir unsere konkreten Klassen, die diese Schnittstelle für jede spezifische Phase implementieren:

public class ChassisAssemblyPhase : IAssemblyPhase
{
    public void Assemble(Car car)
    {
        car.AddChassis(new UnibodyChassis());
    }
}

public class DashboardAssemblyPhase : IAssemblyPhase
{
    public void Assemble(Car car)
    {
        Dashboard dashboard = new Dashboard();

        dashboard.AddComponent(new Speedometer());
        dashboard.AddComponent(new FuelGuage());
        dashboard.Trim = DashboardTrimType.Aluminum;

        car.AddDashboard(dashboard);
    }
}

public class SeatAssemblyPhase : IAssemblyPhase
{
    public void Assemble(Car car)
    {
        car.Seats.Add(new Seat());
        car.Seats.Add(new Seat());
        car.Seats.Add(new Seat());
        car.Seats.Add(new Seat());
    }
}

Jede Phase kann ziemlich einfach sein. Einige sind nur ein Liner. Einige blenden möglicherweise nur vier Sitze aus, andere müssen das Armaturenbrett konfigurieren, bevor sie dem Auto hinzugefügt werden. Die Komplexität dieses langwierigen Prozesses ist in mehrere wiederverwendbare Klassen unterteilt, die leicht zu testen sind.

Auch wenn Sie sagten, dass die Reihenfolge momentan keine Rolle spielt, könnte dies in Zukunft der Fall sein.

Schauen wir uns zum Abschluss den Prozess für die Montage eines Autos an:

Car car = new Car();
CarAssembler assemblyLine = new CarAssembler();

assemblyLine.Assemble(car);

Sie können CarAssembler der Unterklasse zuordnen, um eine bestimmte Art von Auto oder LKW zusammenzubauen. Jede Phase ist eine Einheit innerhalb eines größeren Ganzen, was die Wiederverwendung von Code erleichtert.


Ist dieses Design hoffnungslos fehlerhaft, und ich gebe besser Niederlage und Graben zu - diese Architektur insgesamt? Nicht gut für meine Karriere, aber wäre es auf lange Sicht besser, schlecht gestalteten Code zu schreiben?

Wenn die Entscheidung, was ich geschrieben habe, umgeschrieben werden muss, ein schwarzer Fleck in meiner Karriere wäre, würde ich jetzt als Grabenbagger arbeiten, und Sie sollten keinen meiner Ratschläge befolgen. :) :)

Software Engineering ist ein ewiger Prozess des Lernens, Anwendens und anschließenden erneuten Lernens. Nur weil Sie sich entscheiden, Ihren Code neu zu schreiben, heißt das nicht, dass Sie gefeuert werden. In diesem Fall gäbe es keine Softwareentwickler.

Ist meine derzeitige Wahl tatsächlich der One True Way, und ich muss kämpfen, um bessere Qualitätsmetriken (und / oder Instrumente) zu installieren?

Was Sie skizziert haben, ist nicht der One True Way. Beachten Sie jedoch, dass Codemetriken auch nicht der One True Way sind. Codemetriken sollten als Warnungen angesehen werden. Sie können in Zukunft auf Wartungsprobleme hinweisen. Sie sind Richtlinien, keine Gesetze. Wenn Ihr Code-Metrik-Tool auf etwas hinweist, würde ich zuerst den Code untersuchen, um festzustellen, ob er die SOLID- Prinzipien ordnungsgemäß implementiert . Oftmals wird das Umgestalten von Code, um ihn fester zu machen, den Code-Metrik-Tools gerecht. Manchmal nicht. Sie müssen diese Metriken von Fall zu Fall verwenden.


1
Ja, viel besser als eine Gottklasse zu haben.
BЈовић

Dieser Ansatz funktioniert, aber hauptsächlich, weil Sie die Elemente für ein Auto ohne Parameter erhalten können. Was wäre, wenn Sie sie bestehen müssten? Dann können Sie das alles aus dem Fenster werfen und den Vorgang komplett überdenken, denn Sie haben nicht wirklich eine Autofabrik mit 150 Parametern, das ist verrückt.
Andy

@DavidPacker: Auch wenn Sie eine Reihe von Dingen parametrisieren müssen, können Sie dies auch in eine Art Konfigurationsklasse kapseln, die Sie an den Konstruktor Ihres "Assembler" -Objekts übergeben. In diesem Fahrzeugbeispiel können Lackfarbe, Innenfarbe und Materialien, das Ausstattungspaket, der Motor und vieles mehr angepasst werden, aber Sie müssen nicht unbedingt 150 Anpassungen vornehmen. Sie werden wahrscheinlich ein Dutzend oder so haben, was sich für ein Optionsobjekt eignet.
Greg Burghardt

Aber nur das Hinzufügen der Konfiguration würde dem Zweck der schönen for-Schleife widersprechen, was eine enorme Schande ist, da Sie die Konfiguration analysieren und den richtigen Methoden zuweisen müssten, die Ihnen im Gegenzug die richtigen Objekte geben würden. Sie würden also wahrscheinlich etwas ganz Ähnliches wie das ursprüngliche Design erhalten.
Andy

Aha. Anstatt eine Klasse und fünfzig Methoden zu haben, hätte ich tatsächlich fünfzig Assembler-Lean-Klassen und eine Orchestrierungsklasse. Am Ende scheint das Ergebnis das gleiche zu sein, auch was die Leistung betrifft, aber das Code-Layout ist sauberer. Ich mag das. Es wird etwas umständlicher sein, Operationen hinzuzufügen (ich muss sie in ihrer Klasse einrichten und die Orchestrierungsklasse informieren; ich sehe keinen einfachen Ausweg aus dieser Notwendigkeit), aber ich mag es trotzdem sehr. Lassen Sie mich ein paar Tage Zeit, um diesen Ansatz zu untersuchen.
LSerni

1

Ich weiß nicht, ob dies für Sie möglich ist, aber ich würde über einen datenorientierteren Ansatz nachdenken. Die Idee ist, dass Sie einen (deklarativen) Ausdruck von Regeln und Einschränkungen, Operationen, Abhängigkeiten, Status usw. erfassen. Dann sind Ihre Klassen und Ihr Code allgemeiner, orientieren sich an den Regeln, initialisieren den aktuellen Status von Instanzen und führen Operationen aus Ändern Sie den Status, indem Sie die deklarative Erfassung von Regeln und Statusänderungen verwenden, anstatt Code direkter zu verwenden. Man könnte Datendeklarationen in der Programmiersprache oder einem DSL-Tool oder einer Regel- oder Logik-Engine verwenden.


Einige der Operationen werden tatsächlich auf diese Weise implementiert (als Liste von Regeln). Um dem Beispiel des Autos gerecht zu werden, verwende ich beim Einbau einer Box mit Sicherungen, Lichtern und Steckern nicht drei verschiedene Methoden, sondern nur eine, die generisch zweipolige elektrische Geräte anbringt und die Liste der drei Sicherungslisten übernimmt , Lichter und Stecker. Die große Mehrheit der anderen Methoden ist für diesen Ansatz leider nicht leicht zugänglich.
LSerni

1

Irgendwie erinnert mich das Problem an Abhängigkeiten. Sie möchten ein Auto in einer bestimmten Konfiguration. Wie Greg Burghardt würde ich für jeden Schritt / Gegenstand /… Objekte / Klassen verwenden.

Jeder Schritt deklariert, was er zum Mix hinzufügt (was er ist provides) (kann mehrere Dinge sein), aber auch was er benötigt.

Anschließend definieren Sie irgendwo die endgültige erforderliche Konfiguration und verfügen über einen Algorithmus, der überprüft, was Sie benötigen, und entscheidet, welche Schritte ausgeführt werden müssen / welche Teile hinzugefügt werden müssen / welche Dokumente gesammelt werden müssen.

Algorithmen zur Auflösung von Abhängigkeiten sind nicht so schwer. Der einfachste Fall ist, wenn jeder Schritt eine Sache liefert und keine zwei Dinge dasselbe liefern und die Abhängigkeiten einfach sind (kein 'oder' in den Abhängigkeiten). Für die komplexeren Fälle: Schauen Sie sich einfach ein Tool wie Debian Apt oder so an.

Schließlich reduziert sich der Bau Ihres Autos auf die möglichen Schritte und lässt den CarBuilder die erforderlichen Schritte herausfinden.

Eine wichtige Sache ist jedoch, dass Sie einen Weg finden müssen, um den CarBuilder über alle Schritte zu informieren. Versuchen Sie dies mit einer Konfigurationsdatei. Das Hinzufügen eines zusätzlichen Schritts würde dann nur ein Hinzufügen zur Konfigurationsdatei erfordern. Wenn Sie Logik benötigen, fügen Sie der Konfigurationsdatei ein DSL hinzu. Wenn alles andere fehlschlägt, müssen Sie sie nicht mehr im Code definieren.


0

Ein paar Dinge, die Sie erwähnen, fallen mir als "schlecht" auf

"Meine Logik verwendet derzeit Reflection, um die Implementierung zu vereinfachen."

Dafür gibt es wirklich keine Entschuldigung. Verwenden Sie wirklich einen Zeichenfolgenvergleich von Methodennamen, um zu bestimmen, was ausgeführt werden soll?! Wenn Sie die Reihenfolge ändern, müssen Sie den Namen der Methode ändern?!

"Die Operationen in einer einzelnen Phase sind bereits unabhängig"

Wenn sie wirklich unabhängig voneinander sind, deutet dies darauf hin, dass Ihre Klasse mehr als eine Verantwortung übernommen hat. Für mich scheinen sich beide Beispiele für eine Art Single zu eignen

MyObject.AddComponent(IComponent component) 

Ansatz, mit dem Sie die Logik für jede Operation in eine eigene Klasse oder Klassen aufteilen können.

Tatsächlich stelle ich mir vor, dass sie nicht wirklich unabhängig sind. Ich stelle mir vor, dass jeder den Zustand des Objekts untersucht und modifiziert oder zumindest eine Art Validierung durchführt, bevor er beginnt.

In diesem Fall können Sie entweder:

  1. Werfen Sie OOP aus dem Fenster und haben Sie Dienste, die mit den exponierten Daten in Ihrer Klasse arbeiten, z.

    Service.AddDoorToCar (Auto Auto)

  2. Haben Sie eine Builder-Klasse, die Ihr Objekt erstellt, indem Sie zuerst die erforderlichen Daten zusammenstellen und das fertige Objekt zurückgeben, d. H.

    Car = CarBuilder.WithDoor (). WithDoor (). WithWheels ();

(Die withX-Logik kann wieder in Subbuilder aufgeteilt werden.)

  1. Nehmen Sie den funktionalen Ansatz und übergeben Sie Funktionen / Delgates an eine Änderungsmethode

    Car.Modify ((c) => c.doors ++);


Ewan sagte: "Wirf OOP aus dem Fenster und habe Dienste, die mit den exponierten Daten in deiner Klasse arbeiten." --- Wie ist das nicht objektorientiert?
Greg Burghardt

?? Es ist nicht, es ist eine Nicht-OO-Option
Ewan

Sie verwenden Objekte, was bedeutet, dass es "objektorientiert" ist. Nur weil Sie kein Programmiermuster für Ihren Code identifizieren können, heißt das nicht, dass es nicht objektorientiert ist. Die SOLID-Prinzipien sind eine gute Regel. Wenn Sie einige oder alle SOLID-Prinzipien implementiert haben, ist dies objektorientiert. Wenn Sie Objekte verwenden und nicht nur Strukturen an verschiedene Prozeduren oder statische Methoden weitergeben, ist dies wirklich objektorientiert. Es gibt einen Unterschied zwischen "objektorientiertem Code" und "gutem Code". Sie können fehlerhaften objektorientierten Code schreiben.
Greg Burghardt

Es ist 'nicht OO', weil Sie die Daten wie eine Struktur verfügbar machen und sie in einer separaten Klasse wie in einer Prozedur
bearbeiten

tbh Ich habe nur den führenden Kommentar hinzugefügt, um zu versuchen, das unvermeidliche "das ist nicht OOP!" Kommentare
Ewan
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.