Ich verstehe, was SOLID leisten soll, und verwende es regelmäßig in Situationen, in denen Modularität wichtig ist und die Ziele eindeutig nützlich sind. Zwei Dinge hindern mich jedoch daran, es konsistent auf meiner Codebasis anzuwenden:
Ich möchte eine vorzeitige Abstraktion vermeiden. Nach meiner Erfahrung führt das Zeichnen von Abstraktionslinien ohne konkrete Anwendungsfälle (wie sie jetzt oder in absehbarer Zukunft existieren) dazu, dass sie an den falschen Stellen gezeichnet werden. Wenn ich versuche, solchen Code zu ändern, stören die Abstraktionslinien eher, als dass sie helfen. Daher neige ich dazu, mich auf die Seite zu stellen, keine Abstraktionslinien zu zeichnen, bis ich eine gute Vorstellung davon habe, wo sie nützlich wären.
Ich finde es schwierig, eine Erhöhung der Modularität aus eigenen Gründen zu rechtfertigen, wenn dadurch mein Code ausführlicher, schwerer zu verstehen usw. wird und keine Duplikate beseitigt werden. Ich finde, dass einfacher, eng gekoppelter prozeduraler oder Gott-Objekt-Code manchmal leichter zu verstehen ist als Ravioli-Code mit sehr guten Faktoren, da der Ablauf einfach und linear ist. Es ist auch viel einfacher zu schreiben.
Andererseits führt diese Denkweise oft zu Gegenständen Gottes. Ich überarbeite diese im Allgemeinen konservativ und füge nur dann klare Abstraktionslinien hinzu, wenn sich klare Muster abzeichnen. Was ist, wenn überhaupt, mit Gottobjekten und eng gekoppeltem Code falsch, wenn Sie nicht eindeutig mehr Modularität benötigen, keine signifikanten Duplikate haben und der Code lesbar ist?
EDIT: In Bezug auf einzelne SOLID-Prinzipien wollte ich betonen, dass die Liskov-Substitution meiner Meinung nach eine Formalisierung des gesunden Menschenverstands ist und überall angewendet werden sollte, da Abstraktionen keinen Sinn ergeben, wenn dies nicht der Fall ist. Außerdem sollte jede Klasse auf einer bestimmten Abstraktionsebene eine einzelne Verantwortung haben, auch wenn die Implementierungsdetails auf einer sehr hohen Ebene liegen, die alle zu einer riesigen Klasse mit 2.000 Zeilen zusammengefasst sind. Grundsätzlich sollten Ihre Abstraktionen dort Sinn machen, wo Sie sich für eine Abstraktion entscheiden. Die Prinzipien, die ich in Fällen, in denen Modularität nicht eindeutig nützlich ist, in Frage stelle, sind Open-Closed, Schnittstellentrennung und insbesondere Abhängigkeitsinversion, da es um Modularität geht und nicht nur Abstraktionen Sinn machen.
quelle
Antworten:
Die folgenden einfachen Prinzipien können Sie anwenden, um zu verstehen, wie Sie Ihr Systemdesign ausbalancieren können:
S
Einzelverantwortung : das in SOLID. Sie können ein sehr großes Objekt in Bezug auf die Anzahl der Methoden oder die Datenmenge haben und dieses Prinzip dennoch einhalten. Nehmen Sie zum Beispiel das Ruby String-Objekt. Dieses Objekt verfügt über mehr Methoden, als Sie mit einem Stift schütteln können, aber es hat nur eine Verantwortung: Halten Sie eine Textzeichenfolge für die Anwendung. Sobald Ihr Objekt eine neue Verantwortung übernimmt, sollten Sie darüber nachdenken. Das Wartungsproblem ist die Frage: "Wo würde ich diesen Code voraussichtlich finden, wenn ich später Probleme damit hätte?"Im Wesentlichen versuchen Sie, alles zu tun, um sich nicht in den Fuß zu schießen, wenn die Software gewartet werden muss. Wenn es sich bei Ihren großen Objekten um vernünftige Abstraktionen handelt, gibt es wenig Grund, sie aufzuteilen, nur weil jemand eine Metrik erstellt hat, die besagt, dass eine Klasse nicht mehr als X Zeilen / Methoden / Eigenschaften / etc sein sollte. Wenn überhaupt, sind dies Richtlinien und keine festen Regeln.
quelle
Ich denke, zum größten Teil haben Sie Ihre eigene Frage beantwortet. SOLID enthält Richtlinien, wie Sie Ihren Code umgestalten können, wenn Konzepte eine höhere Abstraktionsebene benötigen.
Wie bei allen anderen kreativen Disziplinen gibt es keine absoluten - nur Kompromisse. Je mehr Sie es tun, desto "einfacher" wird es, zu entscheiden, wann es für Ihre aktuelle Problemdomäne oder Ihre aktuellen Anforderungen ausreicht.
Allerdings ist das Abstrahieren das Herzstück der Softwareentwicklung. Wenn es also keinen guten Grund gibt, es nicht zu tun, werden Sie durch Übung besser darin und bekommen ein gutes Gefühl für die Kompromisse. Also bevorzuge es, wenn es nicht schädlich wird.
quelle
Dies ist teilweise richtig und teilweise falsch.
Falsch
Dies ist kein Grund, die Anwendung von OO-Grundsätzen / SOLID zu verhindern. Es verhindert nur, dass sie zu früh angewendet werden .
Welches ist...
Richtig
Refaktorieren Sie den Code erst, wenn er refaktorisiert werden kann. wenn es Anforderungen abgeschlossen ist. Oder wenn die "Use Cases", wie Sie sagen, alle da sind.
Wenn Sie alleine oder in einem Silo arbeiten
Das Problem, OO nicht zu tun, ist nicht sofort offensichtlich. Nachdem Sie die erste Version eines Programms geschrieben haben:
3/4 dieser guten Dinge sterben innerhalb kurzer Zeit schnell ab.
Schließlich erkennen Sie, dass Sie Gott-Objekte haben (Objekte mit vielen Funktionen), so dass Sie einzelne Funktionen erkennen. Kapselung. Legen Sie sie in Ordner. Verwenden Sie gelegentlich Schnittstellen. Tun Sie sich selbst einen Gefallen für die langfristige Wartung. Zumal nicht umgestaltete Methoden dazu neigen, sich unendlich aufzublähen und zu Gottesmethoden zu werden.
Im Team
Die Probleme, OO nicht zu machen, sind sofort spürbar. Guter OO-Code ist zumindest etwas selbstdokumentierend und lesbar. Vor allem in Kombination mit gutem Green Code. Außerdem erschwert mangelnde Struktur die Trennung von Aufgaben und die Integration erheblich.
quelle
An großen Objekten und eng gekoppeltem Code ist nichts auszusetzen, wenn sie geeignet sind und nichts Besseres erforderlich ist . Dies ist nur eine weitere Manifestation einer Faustregel, die sich in ein Dogma verwandelt.
Lose Kopplung und kleine, einfache Objekte bieten in der Regel in vielen Fällen bestimmte Vorteile. Daher ist es eine gute Idee, sie im Allgemeinen zu verwenden. Das Problem liegt in Menschen, die die Hintergründe der Prinzipien nicht verstehen und blind versuchen, sie universell anzuwenden, auch wenn sie nicht zutreffen.
quelle
Ich nähere mich dem eher vom Standpunkt aus, dass du es nicht brauchst. Dieser Beitrag konzentriert sich auf einen bestimmten Punkt: die Vererbung.
In meinem ersten Entwurf erstelle ich eine Hierarchie von Dingen, von denen ich weiß, dass es zwei oder mehr geben wird. Möglicherweise benötigen sie einen Großteil des gleichen Codes. Es lohnt sich daher, diesen Code von Anfang an zu entwerfen. Nachdem der ursprüngliche Code vorhanden ist und ich weitere Features / Funktionen hinzufügen muss, schaue ich auf das, was ich habe, und denke: "Hat etwas, das ich bereits implementiert habe, die gleiche oder eine ähnliche Funktion?" Wenn ja, ist das höchstwahrscheinlich eine neue Abstraktion, die veröffentlicht werden soll.
Ein gutes Beispiel hierfür ist ein MVC-Framework. Ausgehend von Grund auf erstellen Sie eine große Seite mit einem Code dahinter. Aber dann möchten Sie eine weitere Seite hinzufügen. Der Code dahinter implementiert jedoch bereits einen Großteil der Logik, die für die neue Seite erforderlich ist. Sie abstrahieren also eine
Controller
Klasse / Schnittstelle, die die für diese Seite spezifische Logik implementiert , und lassen das gemeinsame Zeug im ursprünglichen "Gott" -Code zurück.quelle
Wenn SIE als Entwickler wissen, wann Sie die Prinzipien aufgrund der Zukunft des Codes NICHT anwenden sollten, ist dies gut für Sie.
Ich hoffe, SOLID bleibt im Hinterkopf, um zu wissen, wann Dinge abstrahiert werden müssen, um Komplexität zu vermeiden und die Qualität des Codes zu verbessern, wenn sich die von Ihnen angegebenen Werte allmählich verschlechtern.
Noch wichtiger ist jedoch, dass die Mehrheit der Entwickler den Tagesjob erledigt und sich nicht so sehr um Sie kümmert. Wenn Sie anfänglich Methoden mit langer Laufzeit festgelegt haben, welche anderen Entwickler werden sich vorstellen, wenn sie den Code warten oder erweitern? ... Ja, Sie haben geraten, BIGGER-Funktionen, LONGER-Ausführungsklassen und MEHR Gott-Objekte, und sie haben nicht die Prinzipien im Sinn, den wachsenden Code zu erfassen und ihn richtig zu verkapseln. Ihr "lesbarer" Code wird jetzt zu einem verrottenden Durcheinander.
Sie können also argumentieren, dass ein schlechter Entwickler dies trotzdem tun wird, aber zumindest haben Sie einen besseren Vorteil, wenn die Dinge von Anfang an einfach und gut organisiert sind.
Mir macht eine globale "Registry Map" oder ein "God Object" nichts aus, wenn sie einfach sind. Es hängt wirklich vom Systemdesign ab, manchmal kann man damit durchkommen und einfach bleiben, manchmal nicht.
Vergessen wir auch nicht, dass sich große Funktionen und Gottobjekte als äußerst schwierig zu testen erweisen können. Wenn Sie agil bleiben und sich beim Umbauen und Ändern von Code "sicher" fühlen möchten, benötigen Sie Tests. Tests sind schwierig zu schreiben, wenn Funktionen oder Objekte viele Dinge tun.
quelle
Das Problem mit einem Gott-Objekt ist, dass Sie es normalerweise gewinnbringend in Stücke zerbrechen können. Per Definition macht es mehr als eine Sache. Der gesamte Zweck des Aufteilens in kleinere Klassen besteht darin, dass Sie eine Klasse haben, die eine Sache gut macht (und Sie sollten die Definition von "einer Sache" auch nicht strecken). Dies bedeutet, dass Sie für eine bestimmte Klasse, wenn Sie einmal wissen, was sie tun soll, in der Lage sein sollten, es ziemlich leicht zu lesen und zu sagen, ob sie das eine richtig macht oder nicht.
Ich denke, es gibt so etwas wie zu viel Modularität und zu viel Flexibilität, aber das ist eher ein Überdesign- und Überengineering-Problem, bei dem Sie Anforderungen berücksichtigen, die Sie nicht einmal vom Kunden wissen wollen. Viele Designideen zielen darauf ab, Änderungen an der Codeausführung einfach zu steuern. Wenn jedoch niemand die Änderung erwartet, ist es sinnlos, die Flexibilität einzubeziehen.
quelle
Queryer
, das Datenbankabfragen optimiert, das RDBMS abfragt und die Ergebnisse in zurückzugebende Objekte zerlegt. Wenn es in Ihrer App nur eine Möglichkeit gibt, dies zu tun, und sieQueryer
hermetisch versiegelt ist und diese eine Möglichkeit einschließt, dann tut sie nur eine Sache. Wenn es mehrere Möglichkeiten gibt, dies zu tun, und jemand sich für die Details eines Teils interessiert, dann führt er mehrere Dinge aus, und Sie möchten es möglicherweise aufteilen.Ich verwende eine einfachere Richtlinie: Wenn Sie Komponententests dafür schreiben können und keine Code-Duplizierung haben, ist es abstrakt genug. Außerdem sind Sie für eine spätere Umgestaltung gut aufgestellt.
Darüber hinaus sollten Sie SOLID im Hinterkopf behalten, jedoch eher als Richtlinie als als Regel.
quelle
Wenn die Anwendung klein genug ist, kann alles gewartet werden. In größeren Anwendungen werden Gottobjekte schnell zu einem Problem. Um eine neue Funktion zu implementieren, müssen 17 Gottobjekte geändert werden. Menschen machen es nicht besonders gut, wenn sie Verfahren in 17 Schritten ausführen. Die God-Objekte werden ständig von mehreren Entwicklern geändert, und diese Änderungen müssen wiederholt zusammengeführt werden. Sie wollen nicht dorthin gehen.
quelle
Ich teile Ihre Besorgnis über übermäßige und unangemessene Abstraktion, aber ich bin nicht unbedingt so besorgt über vorzeitige Abstraktion.
Das klingt wahrscheinlich nach einem Widerspruch, aber es ist unwahrscheinlich, dass die Verwendung einer Abstraktion ein Problem verursacht, solange Sie sich nicht zu früh zu sehr darauf festlegen - dh solange Sie bereit und in der Lage sind, bei Bedarf umzugestalten.
Dies impliziert einen gewissen Grad an Prototyping, Experimentieren und Zurückverfolgen im Code.
Das heißt, es gibt keine einfache deterministische Regel, die alle Probleme löst. Erfahrung zählt viel, aber diese Erfahrung muss man durch Fehler machen. Und es gibt immer mehr Fehler, auf die Sie frühere Fehler nicht vorbereitet haben.
Behandeln Sie die Lehrbuchprinzipien dennoch als Ausgangspunkt, lernen Sie dann, wie viel programmiert wird und wie diese Prinzipien funktionieren. Wenn es möglich wäre, bessere, präzisere und zuverlässigere Richtlinien als SOLID zu geben, hätte dies wahrscheinlich jetzt jemand getan - und selbst wenn dies der Fall gewesen wäre, wären diese verbesserten Prinzipien immer noch unvollkommen, und die Leute würden nach den Einschränkungen in diesen fragen .
Gute Arbeit auch - wenn jemand einen klaren, deterministischen Algorithmus zum Entwerfen und Codieren von Programmen bereitstellen könnte, gäbe es nur ein letztes Programm, das Menschen schreiben könnten - das Programm, das automatisch alle zukünftigen Programme ohne menschliches Eingreifen schreibt.
quelle
Das Zeichnen der entsprechenden Abstraktionslinien ist etwas, was man aus der Erfahrung zieht. Sie werden dabei Fehler machen, die nicht zu leugnen sind.
SOLID ist eine konzentrierte Form dieser Erfahrung, die Ihnen von Menschen überreicht wird, die diese Erfahrung auf die harte Tour gesammelt haben. Sie haben es ziemlich genau verstanden, wenn Sie sagten, Sie würden es unterlassen, Abstraktion zu schaffen, bevor Sie eine Notwendigkeit dafür sehen. Nun, die Erfahrung, die SOLID Ihnen bietet, wird Ihnen helfen, Probleme zu lösen, die Sie nie zuvor hatten . Aber vertrau mir ... es gibt sehr echte.
Bei SOLID geht es um Modularität, bei Modularität um Wartbarkeit, und bei Wartbarkeit geht es darum, dass Sie einen humanen Arbeitsplan einhalten können, sobald die Software die Produktion erreicht und der Kunde anfängt, Fehler zu bemerken, die Sie nicht haben.
Der größte Vorteil der Modularität ist die Testbarkeit. Je modularer Ihr System ist, desto einfacher ist es zu testen und desto schneller können Sie solide Grundlagen schaffen, um mit den schwierigeren Aspekten Ihres Problems fertig zu werden. Daher erfordert Ihr Problem möglicherweise nicht diese Modularität, sondern nur die Tatsache, dass Sie damit besseren Testcode schneller erstellen können, ist ein Kinderspiel, wenn Sie Modularität anstreben.
Bei Agilität geht es darum, die Balance zwischen schnellem Versand und guter Qualität zu finden. Agilität bedeutet nicht, dass wir Abstriche machen sollten. Die erfolgreichsten agilen Projekte, an denen ich beteiligt war, waren diejenigen, die solchen Richtlinien besondere Aufmerksamkeit schenkten.
quelle
Ein wichtiger Punkt, der offenbar übersehen wurde, ist, dass SOLID zusammen mit TDD praktiziert wird. TDD hebt in der Regel das hervor, was in Ihrem speziellen Kontext "angemessen" ist, und trägt dazu bei, einen Großteil der in den Ratschlägen enthaltenen Mehrdeutigkeiten zu lindern.
quelle