Warum brauchen wir so viele Klassen für Entwurfsmuster?


209

Ich bin Junior-Entwickler unter Senioren und habe große Probleme damit, ihr Denken und Denken zu verstehen.

Ich lese Domain-Driven Design (DDD) und kann nicht verstehen, warum wir so viele Klassen erstellen müssen. Wenn wir diese Methode zum Entwerfen von Software befolgen, erhalten wir 20 bis 30 Klassen, die durch höchstens zwei Dateien und 3-4 Funktionen ersetzt werden können. Ja, das könnte chaotisch sein, aber es ist viel wartbarer und lesbarer.

Immer wenn ich sehen will, was eine Art von EntityTransformationServiceImplmacht, muss ich vielen Klassen, Interfaces, ihren Funktionsaufrufen, Konstruktoren, ihrer Erstellung und so weiter folgen.

Einfache mathematik:

  • 60 Zeilen Dummy-Code im Vergleich zu 10 Klassen X 10 (sagen wir, wir haben völlig andere Logiken) = 600 Zeilen chaotischen Codes im Vergleich zu 100 Klassen + einige mehr, um sie zu verpacken und zu verwalten; Vergessen Sie nicht, die Abhängigkeitsinjektion hinzuzufügen.
  • Lesen von 600 Zeilen unordentlichem Code = ein Tag
  • 100 Stunden = eine Woche, vergiss immer noch, welche was wann macht

Jeder sagt, es sei einfach zu warten, aber wofür? Jedes Mal, wenn Sie neue Funktionen hinzufügen, fügen Sie fünf weitere Klassen mit Fabriken, Entitäten, Diensten und Werten hinzu. Ich glaube, diese Art von Code bewegt sich viel langsamer als chaotischer Code.

Nehmen wir an, wenn Sie in einem Monat 50K LOC-Code schreiben, erfordert die DDD-Sache viele Überprüfungen und Änderungen (ich habe nichts dagegen, in beiden Fällen zu testen). Eine einfache Zugabe kann eine Woche dauern, wenn nicht mehr.

In einem Jahr schreiben Sie viel chaotischen Code und können ihn sogar mehrmals umschreiben. Mit dem DDD-Stil verfügen Sie jedoch immer noch nicht über genügend Funktionen, um mit chaotischem Code zu konkurrieren.

Bitte erkläre. Warum brauchen wir diesen DDD-Stil und viele Muster?

UPD 1 : Ich habe so viele großartige Antworten erhalten. Könnt ihr bitte irgendwo einen Kommentar hinzufügen oder eure Antwort mit dem Link für die Leseliste bearbeiten (nicht sicher, von welchem ​​Anfang an, DDD, Design Patterns, UML, Code Complete, Refactoring, Pragmatic, ...). .. so viele gute Bücher), natürlich mit Sequenz, so dass ich auch anfangen kann zu verstehen und älter zu werden wie einige von euch.


81
Ich denke, dass es hier eine gute Frage gibt, aber sie verbirgt sich hinter Übertreibung und Frustration. @ user1318496, Sie könnten davon profitieren, wenn Sie Ihre Frage ein wenig umformulieren.
MetaFight

48
Auf die Gefahr, auf die Zehen zu treten, weil Ihre Sprache scheiße ist. Nehmen Sie das nicht für mehr als es ist: Eine blöde Sprache ist aus verschiedenen Gründen oft die richtige technische Wahl, aber das macht sie nicht blöde. Bei Ihrer eigentlichen Frage ist Korrektheit wichtiger als Lesbarkeit. Oder etwas anders ausgedrückt: Die Lesbarkeit ist wichtig, da sie das Denken über die Richtigkeit ermöglicht. Was ist also einfacher zu testen, deine 100 Klassen oder deine monolithische Gottklasse? Ich wette auf 100 einfache Klassen.
Jared Smith

87
"Ja, das könnte chaotisch sein, aber es ist viel wartbarer und lesbarer." Wenn es unordentlich ist, wie kann es lesbar und wartbar sein?
Polygnome

37
@Polygnome: Ich wollte genau den gleichen Kommentar eingeben. Ein weiteres Merkmal von Junior-Entwicklern ist "Ich habe das Gefühl, dass sich diese Art von Code viel langsamer als chaotischer Code bewegt." Es nützt dir nichts, so schnell wie möglich zu rennen, wenn du die Hälfte deiner Zeit damit verbringst, gegen Wände zu rennen! Langsam ist glatt, glatt ist schnell.
Eric Lippert

40
Sie nicht, es sei denn, Sie machen Java. Wenn Sie nur Java haben, sieht alles wie eine Klasse aus.
Hobbs

Antworten:


336

Dies ist ein Optimierungsproblem

Ein guter Ingenieur versteht, dass ein Optimierungsproblem ohne ein Ziel bedeutungslos ist. Sie können nicht nur optimieren, Sie müssen für etwas optimieren . Zu Ihren Compiler-Optionen gehören beispielsweise das Optimieren der Geschwindigkeit und das Optimieren der Codegröße. Dies sind manchmal gegensätzliche Ziele.

Ich möchte meiner Frau mitteilen, dass mein Schreibtisch für Zusatzgeräte optimiert ist. Es ist nur ein Haufen, und es ist sehr einfach, Dinge hinzuzufügen. Meine Frau würde es vorziehen, wenn ich für den Abruf optimierte, dh meine Sachen ein wenig organisierte, damit ich Dinge finden kann. Dies macht es natürlich schwieriger hinzuzufügen.

Software ist der gleiche Weg. Sie können die Produkterstellung auf jeden Fall optimieren - Sie können so schnell wie möglich eine Menge monolithischen Codes generieren , ohne sich um die Organisation kümmern zu müssen. Wie Sie bereits bemerkt haben, kann dies sehr, sehr schnell gehen. Die Alternative besteht darin, die Wartung zu optimieren - die Erstellung zu erschweren, Änderungen jedoch einfacher oder weniger riskant zu gestalten. Das ist der Zweck von strukturiertem Code.

Ich würde vorschlagen, dass ein erfolgreiches Softwareprodukt nur einmal erstellt, aber viele, viele Male geändert wird. Erfahrene Ingenieure haben festgestellt, dass unstrukturierte Codebasen ein Eigenleben annehmen und zu Produkten werden, die an Größe und Komplexität zunehmen, bis es sehr schwierig ist, selbst kleine Änderungen vorzunehmen, ohne ein großes Risiko einzugehen. Wenn der Code strukturiert wäre, könnte das Risiko begrenzt werden. Deshalb machen wir uns all diese Mühe.

Komplexität entsteht durch Beziehungen, nicht durch Elemente

Ich stelle fest, dass Sie sich in Ihrer Analyse mit Mengen befassen - der Menge des Codes, der Anzahl der Klassen usw. Während diese interessant sind, kommt der wahre Einfluss von Beziehungen zwischen Elementen, die kombinatorisch explodieren. Wenn Sie beispielsweise 10 Funktionen haben und keine Ahnung haben, welche davon abhängt, haben Sie 90 mögliche Beziehungen (Abhängigkeiten), über die Sie sich Gedanken machen müssen - jede der zehn Funktionen kann von einer der neun anderen Funktionen abhängen, und 9 x 10 = 90. Möglicherweise wissen Sie nicht, welche Funktionen welche Variablen ändern oder wie Daten übertragen werden. Daher müssen sich Programmierer bei der Lösung eines bestimmten Problems viele Sorgen machen. Wenn Sie dagegen 30 Klassen haben, diese aber geschickt angeordnet sind, können sie nur 29 Beziehungen haben, z. B. wenn sie geschichtet oder in einem Stapel angeordnet sind.

Wie wirkt sich das auf den Durchsatz Ihres Teams aus? Nun, es gibt weniger Abhängigkeiten, das Problem ist viel leichter zu lösen. Codierer müssen nicht zig Dinge in ihrem Kopf jonglieren, wenn sie eine Änderung vornehmen. Das Minimieren von Abhängigkeiten kann daher eine enorme Steigerung Ihrer Fähigkeit sein, ein Problem kompetent zu beurteilen. Aus diesem Grund teilen wir die Dinge so eng wie möglich in Klassen oder Module und Bereichsvariablen ein und verwenden SOLID- Prinzipien.


103
Ich würde jedoch bemerken, dass eine Überfülle an Klassen und Indirektionen weder zum Verständnis der NOR-Wartung beiträgt. Und es fließt auch keine verschlungene Kontrolle (auch bekannt als Callback-Hölle).
Matthieu M.

9
@ JohnWu Diese Erklärung ist die beste und verständlichste, die ich je gelesen habe.
Adriano Repetti

13
Gut geschrieben, aber fehlt es vielleicht, warum "einmal erstellt, aber viele, viele Male geändert" wichtig ist? (Wenn der gesamte Code enthalten ist EntityTransformationServiceImpl, müssen Sie erst lernen, wie das Ganze funktioniert, bevor Sie es beheben können. Wenn jedoch z. B. etwas mit dem Format nicht stimmt und eine FormatterKlasse dies erledigt, müssen Sie nur lernen, wie dieser Teil funktioniert . Plus Das Lesen Ihres eigenen Codes nach ~ 3 Monaten ist wie das Lesen des Codes eines Fremden.) Ich glaube, die meisten von uns haben unbewusst darüber nachgedacht, aber das könnte an der Erfahrung liegen. Nicht 100% sicher.
R. Schmitz

17
Ich würde für das Kombinatorische die vollen 10 × 10 = 100 wählen: Diese Funktionen könnten durchaus rekursiv sein und in besonders schlechtem Code nicht unbedingt offensichtlich.
KRyan

32
"Die Alternative besteht darin, die Wartung zu optimieren - die Erstellung zu erschweren, Änderungen jedoch einfacher oder weniger riskant zu gestalten. Das ist der Zweck von strukturiertem Code." Ich stelle die implizite Vorstellung in Frage, dass mehr Klassen = mehr Wartbarkeit. Insbesondere die Streuung der Logik über viele Klassen hinweg erschwert es häufig, die übergeordneten logischen Konzepte im Code zu finden (insbesondere ohne sehr bewusst darauf zu achten, dass die Konzepte gut sichtbar sind), was die Wartbarkeit erheblich beeinträchtigt.
jpmc26

67

Nun, zuallererst sind Lesbarkeit und Wartbarkeit oft im Auge des Betrachters.

Was für Sie lesbar ist, ist möglicherweise nicht für Ihren Nachbarn.

Wartbarkeit läuft oft auf Auffindbarkeit hinaus (wie leicht wird ein Verhalten oder ein Konzept in der Codebasis entdeckt), und Auffindbarkeit ist eine andere subjektive Sache.

DDD

DDD hilft Entwicklerteams unter anderem, indem es eine bestimmte (aber immer noch subjektive) Methode zur Organisation Ihrer Codekonzepte und -verhaltensweisen vorschlägt. Diese Konvention erleichtert das Erkennen von Dingen und daher das Verwalten der Anwendung.

  • Domänenkonzepte werden als Entitäten und Aggregate codiert
  • Das Domänenverhalten befindet sich in Entitäten oder Domänendiensten
  • Die Konsistenz wird durch die Aggregate Roots sichergestellt
  • Persistenzbedenken werden von Repositories behandelt

Diese Anordnung ist objektiv nicht einfacher zu pflegen. Es ist jedoch messbar einfacher zu warten, wenn jeder versteht, dass er in einem DDD-Kontext arbeitet.

Klassen

Klassen helfen bei der Wartbarkeit, Lesbarkeit, Auffindbarkeit usw., da sie eine bekannte Konvention sind .

In objektorientierten Einstellungen werden Klassen normalerweise verwendet, um eng verwandte Verhaltensweisen zu gruppieren und den Zustand zu kapseln, der sorgfältig gesteuert werden muss.

Ich weiß, das klingt sehr abstrakt, aber Sie können es so sehen:

Bei Klassen müssen Sie nicht unbedingt wissen, wie der Code in ihnen funktioniert. Sie müssen nur wissen, wofür die Klasse verantwortlich ist.

Mit Klassen können Sie über Ihre Anwendung im Hinblick auf Interaktionen zwischen genau definierten Komponenten nachdenken .

Dies reduziert die kognitive Belastung, wenn Sie über die Funktionsweise Ihrer Anwendung nachdenken. Anstatt sich merken zu müssen, was 600 Codezeilen leisten, können Sie überlegen, wie 30 Komponenten interagieren.

Und wenn man bedenkt, dass diese 30 Komponenten wahrscheinlich drei Schichten Ihrer Anwendung umfassen, müssen Sie wahrscheinlich nur über jeweils etwa 10 Komponenten nachdenken.

Das scheint ziemlich überschaubar.

Zusammenfassung

Im Wesentlichen sehen Sie, dass die älteren Entwickler Folgendes tun:

Sie unterteilen die Anwendung in leicht verständliche Klassen.

Sie organisieren diese dann in leicht verständliche Ebenen.

Sie tun dies, weil sie wissen, dass es mit dem Anwachsen der Anwendung immer schwieriger wird, darüber als Ganzes nachzudenken. Wenn Sie es in Ebenen und Klassen aufteilen, müssen Sie nie über die gesamte Anwendung nachdenken. Sie müssen immer nur über einen kleinen Teil davon nachdenken.


5
Außerdem sind kleinere Klassen leichter zu ersetzen / umzugestalten.
Zalomon

12
Overengineering liefert keinen wartbareren oder verständlicheren Code - ganz im Gegenteil, trotz Ihrer Behauptung. Wer braucht eine, AddressServiceRepositoryProxyInjectorResolverwenn Sie das Problem eleganter lösen können? Entwurfsmuster für Entwurfsmuster führen nur zu unnötiger Komplexität und Ausführlichkeit.
Vikingsteve

27
Ja, Überentwicklung ist schlecht. So ist das Töten von Welpen. Niemand hier tritt dafür ein. logicallyfallacious.com/tools/lp/Bo/LogicalFallacies/169/…
MetaFight

5
Auch "Eleganz" im Kontext der Softwareentwicklung ist oft ein Wieselwort. en.wikipedia.org/wiki/Weasel_word
MetaFight

11
Das Gleiche gilt für jeden Ansatz zur Code-Organisation. Alles hängt vom Wartungsteam ab, das versteht, warum der Code so organisiert ist, wie er ist. Das ist der springende Punkt bei der Verwendung gut etablierter und verständlicher Konventionen.
MetaFight

29

Bitte erklären Sie mir, warum brauchen wir diesen DDD-Stil, viele Patterns?

Zunächst ein Hinweis: Der wichtige Teil von DDD sind nicht die Muster , sondern die Ausrichtung des Entwicklungsaufwands auf das Geschäft. Greg Young bemerkte, dass die Kapitel im blauen Buch in der falschen Reihenfolge sind .

Aber zu Ihrer spezifischen Frage: Es gibt in der Regel viel mehr Klassen als erwartet, (a) weil versucht wird, das Domänenverhalten von der Installation zu unterscheiden, und (b) weil zusätzliche Anstrengungen unternommen werden, um diese Konzepte sicherzustellen im Domain-Modell werden explizit ausgedrückt.

Wenn Sie in der Domäne zwei unterschiedliche Konzepte haben, sollten diese im Modell eindeutig sein, auch wenn sie in der Speicherdarstellung zufällig gleich sind .

Im Endeffekt erstellen Sie eine domänenspezifische Sprache, die Ihr Modell in der Geschäftssprache beschreibt, sodass ein Domain-Experte sie anzeigen und Fehler erkennen kann.

Außerdem wird der Trennung von Bedenken etwas mehr Aufmerksamkeit geschenkt. und die Vorstellung, Verbraucher mit einer gewissen Fähigkeit von den Implementierungsdetails zu isolieren. Siehe DL Parnas . Die klaren Grenzen ermöglichen es Ihnen, die Implementierung zu ändern oder zu erweitern, ohne dass sich die Auswirkungen auf Ihre gesamte Lösung auswirken.

Die Motivation dabei: Für eine Anwendung, die Teil der Kernkompetenz eines Unternehmens ist (dh einen Ort, an dem sich ein Wettbewerbsvorteil ergibt), möchten Sie ein Domain-Verhalten einfach und kostengünstig durch eine bessere Variante ersetzen können. In der Tat haben Sie Teile des Programms, die Sie schnell entwickeln möchten (wie sich der Status im Laufe der Zeit entwickelt), und andere Teile, die Sie langsam ändern möchten (wie der Status gespeichert wird). Die zusätzlichen Abstraktionsebenen tragen dazu bei, eine versehentliche Kopplung zu vermeiden.

Fairerweise: Ein Teil davon ist auch objektorientierter Hirnschaden. Die ursprünglich von Evans beschriebenen Muster basieren auf Java-Projekten, an denen er vor über 15 Jahren teilgenommen hat. Zustand und Verhalten sind in diesem Stil eng miteinander verbunden, was zu Komplikationen führt, die Sie vielleicht lieber vermeiden möchten. siehe Wahrnehmung und Handlung von Stuart Halloway oder Inlining Code von John Carmack.

Unabhängig davon, in welcher Sprache Sie arbeiten, bietet die Programmierung in einem funktionalen Stil Vorteile. Sie sollten es tun, wann immer es zweckmäßig ist, und Sie sollten über die Entscheidung nachdenken, wenn es nicht zweckmäßig ist. Carmack, 2012


2
Ich wollte meine eigene Antwort hinzufügen, bis ich dies las. Ich möchte im ersten Absatz betonen, dass das Ziel darin besteht, Geschäftskonzepte in Software zu modellieren, um die Software an den Geschäftsanforderungen auszurichten. Der Fairness halber sollte der Analyst, der das Projekt betreibt, auch ausdrücken, wie viel Zeit der Fahrer bis zur Auslieferung benötigt, und die Code- / Testqualität oder -vollständigkeit sollte entsprechend angepasst werden.
Sentinel

1
John Carmack ist ein großartiges Beispiel für IMO, da er dafür bekannt ist, Code zu schreiben, der sauber und einfach zu verstehen ist und gleichzeitig eine gute Leistung erbringt. Dies ist besonders sichtbar, wenn Sie seinen Code mit fortschrittlichen Technologien verfolgen - mit besseren CPUs und besserem Arbeitsspeicher können Sie eine Menge Dinge erkennen, die von "das muss wirklich knapp sein" zu "das muss wirklich klar sein / isoliert sein / ... ". Einer der pragmatischsten Programmierer, die ich kenne :)
Luaan

Ich denke das ist die beste Antwort hier. Obwohl ich bei DDD keineswegs verkauft bin, erklärt diese Antwort mit Sicherheit, was es wirklich ist und was eine tatsächliche Motivation dafür ist, anstatt mich auf gängige Plattitüden zu berufen, die nichts bedeuten.
jpmc26

29

Es gibt viele gute Punkte in den anderen Antworten, aber ich denke, sie vermissen oder betonen nicht einen wichtigen begrifflichen Fehler, den Sie machen:

Sie vergleichen den Aufwand, um das gesamte Programm zu verstehen.

Dies ist bei den meisten Programmen keine realistische Aufgabe. Selbst einfache Programme bestehen aus so viel Code, dass es einfach unmöglich ist, alles zu einem bestimmten Zeitpunkt im Kopf zu verwalten. Sie haben nur die Möglichkeit, den für die jeweilige Aufgabe relevanten Programmteil zu finden (Beheben eines Fehlers, Implementieren einer neuen Funktion) und damit zu arbeiten.

Wenn Ihr Programm aus riesigen Funktionen / Methoden / Klassen besteht, ist dies fast unmöglich. Sie müssen Hunderte von Codezeilen verstehen, um zu entscheiden, ob dieser Code für Ihr Problem relevant ist. Mit den Schätzungen, die Sie angegeben haben, wird es einfach, eine Woche zu verbringen, um den Code zu finden, an dem Sie arbeiten müssen.

Vergleichen Sie dies mit einer Codebasis mit kleinen Funktionen / Methoden / Klassen, die benannt und in Paketen / Namespaces organisiert sind, wodurch klar wird, wo ein bestimmtes Stück Logik zu finden / zu platzieren ist. Wenn Sie in vielen Fällen richtig vorgehen, können Sie direkt zur richtigen Stelle springen, um Ihr Problem zu lösen, oder zumindest zu einer Stelle, von der aus Sie durch das Starten Ihres Debuggers in ein paar Schritten an die richtige Stelle gelangen.

Ich habe in beiden Arten von Systemen gearbeitet. Der Unterschied kann bei vergleichbaren Aufgaben und vergleichbarer Systemgröße leicht zwei Größenordnungen in der Leistung betragen.

Der Effekt, den dies auf andere Aktivitäten hat:

  • Das Testen wird mit kleineren Einheiten viel einfacher
  • Weniger Zusammenführungskonflikte, da die Chancen, dass zwei Entwickler an demselben Code arbeiten, geringer sind.
  • Weniger Duplizierung, da es einfacher ist, Teile wiederzuverwenden (und sie erst zu finden).

19

Weil das Testen von Code schwieriger ist als das Schreiben von Code

Viele Antworten haben aus Entwicklersicht gute Argumente geliefert - die Wartung kann reduziert werden, was wiederum den Code anstrengender macht, ihn erst zu schreiben.

Es ist jedoch noch ein weiterer Aspekt zu berücksichtigen - das Testen kann nur als Ihr ursprünglicher Code genau definiert werden.

Wenn Sie alles in einen Monolithen schreiben, ist der einzige effektive Test, den Sie schreiben können, "Ist die Ausgabe bei diesen Eingaben korrekt?". Das bedeutet, dass alle gefundenen Bugs auf "irgendwo in diesem riesigen Haufen Code" beschränkt sind.

Natürlich können Sie dann einen Entwickler mit einem Debugger beauftragen, genau herauszufinden, wo das Problem begonnen hat, und an einer Lösung arbeiten. Dies kostet viel Ressourcen und ist eine schlechte Nutzung der Zeit eines Entwicklers. Stellen Sie sich vor, dass ein immer kleinerer Fehler dazu führt, dass ein Entwickler das gesamte Programm erneut debuggen muss.

Die Lösung: Viele kleinere Tests, die jeweils einen bestimmten potenziellen Fehler identifizieren.

Diese kleinen Tests (zum Beispiel Komponententests) haben den Vorteil, dass sie einen bestimmten Bereich der Codebasis überprüfen und dabei helfen, Fehler in einem begrenzten Bereich zu finden. Dies beschleunigt nicht nur das Debuggen, wenn ein Test fehlschlägt, sondern bedeutet auch, dass Sie den Fehler bei größeren Tests leichter finden können, wenn alle Ihre kleinen Tests fehlschlagen zwischen ihnen).

Es sollte klar sein, dass kleinere Tests bedeuten, dass Ihre Codebasis in kleinere testbare Blöcke aufgeteilt werden muss. Wenn Sie dies in einer großen kommerziellen Codebasis tun, erhalten Sie häufig Code, mit dem Sie arbeiten.

Nur als Randnotiz: Das soll nicht heißen, dass die Leute die Dinge nicht "zu weit" bringen. Es gibt jedoch einen legitimen Grund, Codebasen in kleinere / weniger zusammenhängende Teile zu unterteilen - wenn dies vernünftig erfolgt.


5
Während Ihre Antwort nur das Testen und die Testbarkeit betrifft, ist dies das wichtigste Problem, das einige der anderen Antworten vollständig ignoriert haben, also +1.
Skomisa

17

Bitte erklären Sie mir, warum brauchen wir diesen DDD-Stil, viele Patterns?

Viele (die meisten ...) von uns brauchen sie wirklich nicht . Theoretiker und sehr fortgeschrittene, erfahrene Programmierer schreiben Bücher über Theorien und Methoden als Ergebnis vieler Recherchen und ihrer umfassenden Erfahrung - das bedeutet nicht, dass alles, was sie schreiben, für jeden Programmierer in seiner täglichen Praxis anwendbar ist.

Als Nachwuchsentwickler ist es gut, Bücher wie das von Ihnen erwähnte zu lesen, um Ihre Perspektiven zu erweitern und Sie auf bestimmte Probleme aufmerksam zu machen. Dies erspart Ihnen auch die Verlegenheit und Erschütterung, wenn Ihre älteren Kollegen eine Ihnen unbekannte Terminologie verwenden. Wenn Sie etwas sehr Schwieriges finden und es nicht sinnvoll oder nützlich erscheint, töten Sie sich nicht darüber hinweg - verstauen Sie es einfach in Ihrem Kopf, dass es ein solches Konzept oder einen solchen Ansatz gibt.

Wenn Sie kein Akademiker sind, besteht Ihre tägliche Aufgabe darin, praktikable und wartbare Lösungen zu finden. Wenn die Ideen, die Sie in einem Buch finden, Ihnen nicht dabei helfen, dieses Ziel zu erreichen, sorgen Sie sich jetzt nicht darum, solange Ihre Arbeit als zufriedenstellend eingestuft wird.

Es kann vorkommen, dass Sie feststellen, dass Sie einiges von dem verwenden können, worüber Sie gelesen haben, aber zuerst nicht ganz "verstanden" haben oder vielleicht auch nicht.


12
Während dies auf einer reinen Ebene korrekt ist, klingt es so, als ob DDD und andere Patterns nur Theorie sind. Sie sind nicht. Sie sind wichtige Werkzeuge, um lesbaren und wartbaren Code zu erhalten.
Jens Schauder

9
@PeteKirkham das OPs Dilemma ist die übermäßige Verwendung von Designmustern. Benötigen Sie wirklich ein AccountServiceFacadeInjectResolver(echtes Beispiel, das ich gerade in einem System bei der Arbeit gefunden habe) - Antwort ist höchstwahrscheinlich nein.
Vikingsteve

4
@vikingsteve OP ist kein Programmier-Guru, der die allgemeine Überbeanspruchung von Designmustern kommentiert. OP ist ein Lehrling und denkt, dass sie in seinem Geschäft überbeansprucht sind . Ich weiß, dass Anfänger - ich hätte das gleiche über den Code gedacht, den ich heutzutage schreibe, aber Anfänger - ich hatte eine Menge persönlicher Projekte, die scheiterten, weil sie zu verworren waren.
R. Schmitz

3
@ R.Schmitz Das heißt, Anfänger, ich hatte viele persönliche Projekte gescheitert, weil sie zu sehr bemüht waren, die Dinge gut zu gestalten, zu organisieren, zu strukturieren, OOP ... All dies sind Werkzeuge. Sie müssen richtig verstanden und verwendet werden, und Sie können den Hammer kaum dafür verantwortlich machen, dass er schlecht schraubt. Überraschenderweise scheint es viel Einsicht und Erfahrung zu
erfordern

3
@Luaan Wenn Sie sich schon für gut gestaltete, organisierte, strukturierte OOP interessieren, würde ich das kaum Anfänger nennen - Sie, aber ja, Überbeanspruchung ist schlecht. Die Frage ist jedoch eher, wie OP nicht wirklich versteht, welche Probleme diese Dinge lösen. Wenn Sie den Grund nicht kennen, scheint es keinen Grund zu geben - aber tatsächlich wissen Sie es noch nicht.
R. Schmitz

8

Da Ihre Frage sehr viel Feld abdeckt und viele Annahmen enthält, werde ich das Thema Ihrer Frage herausgreifen:

Warum brauchen wir so viele Klassen in Entwurfsmustern?

Wir nicht. Es gibt keine allgemein anerkannte Regel, die besagt, dass es in Entwurfsmustern viele Klassen geben muss.

Es gibt zwei wichtige Anleitungen, um zu entscheiden, wo Code abgelegt werden soll und wie Sie Ihre Aufgaben in verschiedene Codeeinheiten aufteilen können:

  • Zusammenhalt: Jede Codeeinheit (sei es ein Paket, eine Datei, eine Klasse oder eine Methode) sollte zusammengehören . Das heißt, eine bestimmte Methode sollte eine Aufgabe haben und diese gut erledigen. Jede Klasse sollte für ein größeres Thema verantwortlich sein (was auch immer das sein mag). Wir wollen einen hohen Zusammenhalt.
  • Kopplung: Zwei beliebige Codeeinheiten sollten so wenig wie möglich voneinander abhängen - insbesondere sollten keine zirkulären Abhängigkeiten bestehen. Wir wollen eine niedrige Kopplung.

Warum sollten diese beiden wichtig sein?

  • Kohäsion: Eine Methode, die viele Dinge erledigt (z. B. ein altmodisches CGI-Skript, das GUI, Logik, DB-Zugriff usw. in einem großen Durcheinander von Code erledigt), wird unhandlich. Zum Zeitpunkt des Schreibens ist es verlockend, Ihren Gedankengang nur in eine lange Methode zu überführen. Dies funktioniert, es ist einfach zu präsentieren und so, und Sie können damit fertig werden. Probleme treten später auf: Nach einigen Monaten können Sie vergessen, was Sie getan haben. Eine Codezeile am oberen Rand kann einige Bildschirme von einer Zeile am unteren Rand entfernt sein. Es ist leicht, alle Details zu vergessen. Jede Änderung an einer beliebigen Stelle der Methode kann eine beliebige Anzahl von Verhaltensweisen der komplexen Sache beeinträchtigen. Es wird ziemlich schwierig sein, Teile der Methode umzugestalten oder wiederzuverwenden. Und so weiter.
  • Kopplung: Jedes Mal, wenn Sie eine Codeeinheit ändern, können alle anderen davon abhängigen Einheiten zerstört werden. In strengen Sprachen wie Java erhalten Sie möglicherweise während der Kompilierung Hinweise (z. B. zu Parametern, deklarierten Ausnahmen usw.). Aber viele Änderungen lösen solche (dh Verhaltensänderungen) nicht aus, und andere, dynamischere Sprachen haben keine solchen Möglichkeiten. Je höher die Kopplung, desto schwieriger wird es, irgendetwas zu ändern, und Sie könnten zum Stillstand kommen, wo ein vollständiges Neuschreiben erforderlich ist, um ein bestimmtes Ziel zu erreichen.

Diese beiden Aspekte sind die Basis "Treiber" für jede Wahl von "Wo soll was abgelegt werden" in jeder Programmiersprache und jedem Paradigma (nicht nur OO). Nicht jeder ist sich dessen explizit bewusst, und es dauert oft Jahre, bis er ein wirklich tief verwurzeltes, automatisches Gefühl dafür bekommt, wie diese Software beeinflussen.

Offensichtlich sagen Ihnen diese beiden Konzepte nichts darüber aus, was Sie tatsächlich tun sollen . Einige Menschen irren auf der Seite von zu viel, andere auf der Seite von zu wenig. Einige Sprachen (Sie sehen hier Java) bevorzugen aufgrund der extrem statischen und pedantischen Natur der Sprache selbst viele Klassen (dies ist keine Wertangabe, aber es ist, was es ist). Dies wird besonders deutlich, wenn Sie es mit dynamischen und ausdrucksstärkeren Sprachen vergleichen, zum Beispiel Ruby.

Ein weiterer Aspekt ist , dass einige Leute auf den agilen Ansatz subscribe nur das Schreiben von Code, der notwendig ist , gerade jetzt , und Refactoring stark später, wenn nötig. Bei dieser Art der Entwicklung würden Sie keine erstellen, interfacewenn Sie nur eine implementierende Klasse haben. Sie würden einfach die konkrete Klasse implementieren. Wenn Sie später eine zweite Klasse benötigen, würden Sie umgestalten.

Manche Leute arbeiten einfach nicht so. Sie erstellen Schnittstellen (oder allgemeiner abstrakte Basisklassen) für alles, was jemals allgemeiner verwendet werden könnte. Dies führt schnell zu einer Klassenexplosion.

Auch hier gibt es Argumente dafür und dagegen, und es spielt keine Rolle, welche ich oder Sie bevorzugen. Sie werden in Ihrem Leben als Softwareentwickler auf alle Extreme stoßen, von langen Spaghetti-Methoden über aufgeklärte Klassenentwürfe, die gerade groß genug sind, bis hin zu unglaublich aufgeblasenen Klassenschemata, die viel überarbeitet sind. Je erfahrener Sie werden, desto mehr entwickeln Sie sich zu "architektonischen" Rollen und können diese in die gewünschte Richtung beeinflussen. Sie werden eine goldene Mitte für sich selbst herausfinden, und Sie werden immer noch feststellen, dass viele Menschen nicht mit Ihnen übereinstimmen, was auch immer Sie tun.

Offenheit ist hier also das Wichtigste, und das wäre mein vorrangiger Ratschlag an Sie, da Sie anscheinend große Schmerzen in dieser Angelegenheit haben, wenn Sie den Rest Ihrer Frage beurteilen ...


7

Erfahrene Programmierer haben gelernt:

  • Weil diese kleinen Programme und Klassen anfangen zu wachsen, besonders die erfolgreichen. Diese einfachen Muster, die auf einer einfachen Ebene funktionierten, skalieren nicht.
  • Weil es lästig erscheinen mag, mehrere Artefakte für jedes Hinzufügen / Ändern hinzufügen / ändern zu müssen, aber Sie wissen, was Sie hinzufügen müssen, und das ist einfach. 3 Minuten Tippen schlagen 3 Stunden cleveres Codieren.
  • Viele kleine Klassen sind nicht "chaotisch", nur weil Sie nicht verstehen, warum der Aufwand eigentlich eine gute Idee ist
  • Ohne das hinzugefügte Domänenwissen ist der für mich offensichtliche Code für meine Teamkollegen oft rätselhaft ... und für die Zukunft für mich.
  • Stammeswissen kann es für Projekte unglaublich schwierig machen, Teammitglieder dazu zu bringen, schnell und einfach produktiv zu werden.
  • Das Benennen ist eines der beiden großen Probleme beim Rechnen, und viele Klassen und Methoden sind oft eine intensive Übung beim Benennen, die für viele von großem Nutzen ist.

5
Aber ist der Aufwand notwendig? In vielen Fällen, die ich sehe, werden Entwurfsmuster überbeansprucht. Die Folge von Überkomplexität sind größere Schwierigkeiten beim Verstehen, Verwalten, Testen und Debuggen von Code. Wenn Sie 12 Klassen und 4 Maven-Module um eine einfache Serviceklasse haben (ein echtes Beispiel, das ich gerade gesehen habe), wird es schnell unhandlicher, als Sie vielleicht denken.
Vikingsteve

4
@vikingsteve Sie verweisen immer wieder auf ein einzelnes Beispiel einer fehlerhaften Implementierung, in der Hoffnung, dass sich herausstellt, dass strukturierter Code im Allgemeinen eine schlechte Idee ist. Das geht einfach nicht.
MetaFight

2
@MetaFight OP spricht nicht über Strukturcode. Er redet über zu viele Abstraktionen. Wenn du zu viele Abstraktionen hast, bekommst du schnell sehr, sehr unstrukturierten Code und viele fast identische Teile, einfach weil die Leute nicht mit der Menge an Sachen fertig werden, mit denen sie zu tun haben.
Klarer

4
@Clearer Ich bin mir ziemlich sicher, dass sich alle hier einig sind, dass die hypothetische fehlerhafte Anwendung von strukturiertem Code eine schlechte Sache ist. Das OP sagt, dass sie Junior sind. Das ist der Grund, warum die Leute annehmen, dass sie mit den Vorteilen von strukturiertem Code nicht vertraut sind, und sie wiederholen.
MetaFight

2

Die bisherigen, allesamt guten Antworten gingen von der begründeten Annahme aus, dass dem Fragesteller etwas fehlt, was auch der Fragesteller anerkennt. Es ist auch möglich, dass der Fragesteller im Grunde genommen Recht hat, und es lohnt sich zu diskutieren, wie diese Situation zustande kommen kann.

Erfahrung und Praxis sind mächtig, und wenn die Senioren ihre Erfahrung in großen, komplexen Projekten gesammelt haben, bei denen die einzige Möglichkeit, die Dinge unter Kontrolle zu halten, in vielen EntityTransformationServiceImplFällen besteht, werden sie schnell und komfortabel mit Entwurfsmustern und der engen Einhaltung von DDD. Mit einem einfachen Ansatz wären sie viel weniger effektiv, selbst für kleine Programme. Als ungerade heraus sollten Sie sich anpassen und es wird eine großartige Lernerfahrung sein.

Während Sie sich jedoch anpassen, sollten Sie es als eine Lektion in der Balance zwischen dem Erlernen eines einzigen Ansatzes verstehen, bis zu dem Punkt, an dem Sie ihn überall zum Laufen bringen können, anstatt vielseitig zu bleiben und zu wissen, welche Tools verfügbar sind, ohne notwendigerweise ein Experte mit einem von ihnen zu sein. Beides hat Vorteile und beides wird auf der Welt gebraucht.


-4

Mehr Klasse und Funktion je nach Verwendung zu schaffen, ist ein großartiger Ansatz, um Probleme auf eine bestimmte Art und Weise zu lösen, die in Zukunft bei der Lösung von Problemen hilft.

Mithilfe mehrerer Klassen können Sie sich als Basisarbeit identifizieren und jede Klasse kann jederzeit aufgerufen werden.

Wenn Sie jedoch viele Klassen und Funktionen von ihrem abrufbaren Namen haben, sind sie einfach aufzurufen und zu verwalten. Dies wird als sauberer Code bezeichnet.


9
Entschuldigung, aber was sagst du?
Deduplizierer

@Deduplicator Nehmen wir ein Beispiel in Java, wenn wir jframes-Threads und -Klassen für ihre Vererbung verwenden. Aus nur einer Klasse können wir einige Java-Werke nicht für das Design verwenden, daher müssen wir weitere Klassen
erstellen

2
Diese Antwort muss die Idee (n) enthalten, die in klarerem Englisch präsentiert werden sollen. Die Kernidee der Antwort scheint zu sein, dass kleinere Klassen mit jeweils einer genau definierten Funktionalität eine gute Idee sind, aber die schreckliche Grammatik macht es fast unmöglich, sie zu verstehen. Auch allein reicht diese Behauptung möglicherweise nicht aus.
Talonx
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.