Sollten wir vermeiden, Designmuster in sich ständig ändernden Projekten zu verwenden?

32

Ein Freund von mir arbeitet für eine kleine Firma an einem Projekt, das jeder Entwickler hassen würde: Er ist unter Druck gesetzt, so schnell wie möglich zu veröffentlichen, er ist der einzige, der sich anscheinend um technische Schulden kümmert, der Kunde hat keinen technischen Hintergrund usw.

Er erzählte mir eine Geschichte, die mich über die Angemessenheit von Designmustern in Projekten wie diesem nachdenken ließ. Hier ist die Geschichte.

Wir mussten Produkte an verschiedenen Stellen auf der Website anzeigen. Beispielsweise könnten Content Manager die Produkte, aber auch die Endbenutzer oder die Partner über die API anzeigen.

Manchmal fehlten Informationen in den Produkten: Beispielsweise hatten einige von ihnen keinen Preis, als das Produkt gerade erstellt wurde, aber der Preis war noch nicht festgelegt. Einige hatten keine Beschreibung (die Beschreibung ist ein komplexes Objekt mit Änderungsprotokollen, lokalisiertem Inhalt usw.). Einigen fehlten Versandinformationen.

Inspiriert von meinen jüngsten Lesungen über Entwurfsmuster, hielt ich dies für eine hervorragende Gelegenheit, das magische Null-Objekt-Muster zu verwenden . Also habe ich es geschafft und alles war glatt und sauber. Man musste nur anrufen product.Price.ToString("c"), um den Preis anzuzeigen oder product.Description.Currentdie Beschreibung anzuzeigen ; Keine bedingten Dinge erforderlich. Bis der Stakeholder eines Tages bat, ihn in der API anders anzuzeigen, indem er einen nullin JSON hatte. Und auch anders für Content-Manager, indem "Preis nicht angegeben [Ändern]" angezeigt wird. Und ich musste mein geliebtes Null-Objekt-Muster ermorden, weil es nicht mehr nötig war.

Auf die gleiche Weise musste ich ein paar abstrakte Fabriken und ein paar Erbauer entfernen. Schließlich ersetzte ich mein wunderschönes Fassadenmuster durch direkte und hässliche Aufrufe, da sich die zugrunde liegenden Schnittstellen drei Monate lang zweimal täglich änderten und sogar der Singleton mich verließ als die Anforderungen besagten, dass das betroffene Objekt je nach Kontext unterschiedlich sein musste.

Mehr als drei Wochen Arbeit bestanden darin, Designmuster hinzuzufügen und sie dann einen Monat später zu zerreißen, und mein Code wurde schließlich zu einer Spaghetti, die von niemandem, einschließlich mir, gepflegt werden konnte. Wäre es nicht besser, diese Muster überhaupt nicht zu verwenden?

In der Tat musste ich mich mit Projekten befassen, bei denen sich die Anforderungen ständig ändern und die von Personen vorgegeben werden, die den Zusammenhalt oder die Kohärenz des Produkts nicht wirklich berücksichtigen. In diesem Zusammenhang spielt es keine Rolle, wie agil Sie sind, Sie erhalten eine elegante Lösung für ein Problem, und wenn Sie diese schließlich implementieren, stellen Sie fest, dass sich die Anforderungen so drastisch geändert haben, dass Ihre elegante Lösung nicht mehr passt länger.

Was wäre die Lösung in diesem Fall?

  • Verwenden Sie keine Entwurfsmuster, hören Sie auf zu denken und schreiben Sie Code direkt?

    Es wäre interessant, eine Erfahrung zu machen, bei der ein Team Code direkt schreibt, während ein anderes Team vor dem Tippen zweimal darüber nachdenkt und das Risiko eingeht, einige Tage später das ursprüngliche Design wegwerfen zu müssen: Wer weiß, vielleicht haben beide Teams das gleiche technische Schulden. Ohne solche Daten würde ich nur behaupten, dass es sich nicht richtig anfühlt , Code ohne vorherige Überlegung bei der Arbeit an einem 20-Mann-Monat-Projekt zu tippen.

  • Behalten Sie das Designmuster bei, das keinen Sinn mehr ergibt, und versuchen Sie, weitere Muster für die neu erstellte Situation hinzuzufügen?

    Dies scheint auch nicht richtig. Muster werden verwendet, um das Verständnis des Codes zu vereinfachen. Wenn Sie zu viele Muster eingeben, wird der Code zu einem Chaos.

  • Überlegen Sie sich ein neues Design, das die neuen Anforderungen berücksichtigt, und überarbeiten Sie dann langsam das alte Design in das neue?

    Als Theoretiker und derjenige, der Agile bevorzugt, bin ich total begeistert. In der Praxis, wenn Sie wissen, dass Sie jede Woche zum Whiteboard zurückkehren und den Großteil des vorherigen Designs wiederholen müssen und der Kunde einfach nicht genug Geld hat, um Sie dafür zu bezahlen, und auch nicht genug Zeit zum Warten Das wird wahrscheinlich nicht funktionieren.

Also irgendwelche Vorschläge?

Arseni Mourzenko
quelle
43
Ich denke, das ist ein falsches Dilemma. Nach Angaben Ihres Freundes ist der Code jetzt ein unerreichbarer Teller Spaghetti. Das ist nicht die Schuld von Software-Mustern; Es ist die Unfähigkeit Ihres Freundes, diese Muster richtig zu verwenden, um die Wartbarkeit zu erhöhen und nicht zu verringern. Wenn ich konkrete Beispiele finden kann, werde ich eine angemessene Antwort veröffentlichen.
Robert Harvey
5
FWIW: Jeder Kunde, der keine Toleranz für Driftkosten hat, sollte wahrscheinlich auch Agile nicht ausführen, es sei denn, die Kosten für die Verlagerung von Anforderungen enthalten Zulagen.
Robert Harvey
5
Ich vermute, dass der Code ohne die Entwurfsmuster viel früher einen unerreichbaren Zustand erreicht hätte
Steven A. Lowe
28
Diese Frage ergibt keinen Sinn. Die einzige Möglichkeit, "Entwurfsmuster zu vermeiden", besteht darin, überhaupt keine Software zu schreiben. Dinge wie "XYZ-Muster" sind nur Bezeichnungen für gängige Codierungsstrategien, mit denen wir Programmierer Informationen und Ratschläge zu unserer Codestruktur und unseren Auswahlmöglichkeiten bequemer miteinander teilen können. Jede Designauswahl in Ihrem Code könnte mit einem Namen versehen und als "Designmuster" bezeichnet werden, auch wenn dies nicht unbedingt allgemein bekannt ist (es sei denn, Sie sind stolz auf Ihr einzigartiges Design und motiviert genug, ihm einen Namen und einen Blog zu geben) es oder so).
Jason C
9
Sie können es vermeiden, Ihren Fuß als "Fuß" zu bezeichnen, aber Sie haben immer noch diesen Fuß. Es ist nur schwieriger, mit jemandem darüber zu sprechen, wenn Sie ihn nicht als "Fuß" bezeichnen. Sie denken vielleicht, dass Sie "Designmuster vermeiden", aber wenn Sie ein anständiges Design entwickeln, das funktioniert, dann treten Sie zurück und schauen Sie es sich an. Sie werden wahrscheinlich feststellen, dass Ihr Design sowieso gerade in eines der gebräuchlichsten Muster passt. ob du es so nennst oder nicht.
Jason C

Antworten:

86

Ich sehe einige falsche Annahmen in dieser Frage:

  • Code mit Entwurfsmustern benötigt, obwohl er richtig angewendet wird, mehr Zeit als Code ohne diese Muster, um implementiert zu werden.

Designmuster sind kein Selbstzweck, sie sollten Ihnen dienen, nicht umgekehrt. Wenn ein Entwurfsmuster die Implementierung des Codes nicht einfacher oder zumindest besser entwickelbar macht ( dh einfacher an sich ändernde Anforderungen anzupassen ist), verfehlt das Muster seinen Zweck. Wenden Sie keine Muster an, wenn sie dem Team das "Leben" nicht erleichtern. Wenn das neue Null-Objektmuster Ihrem Freund für die Zeit diente, in der er es verwendete, war alles in Ordnung. Wenn es später beseitigt werden sollte, dann könnte dies auch in Ordnung sein. Wenn das Null-Objektmuster die (korrekte) Implementierung verlangsamt hat, war seine Verwendung falsch. Beachten Sie, dass aus diesem Teil der Geschichte noch keine Ursache für "Spaghetti-Code" abgeleitet werden kann.

  • Der Kunde ist schuld, weil er keinen technischen Hintergrund hat und sich nicht für den Zusammenhalt oder die Kohärenz des Produkts interessiert

Das ist weder sein Job noch seine Schuld! Ihre Aufgabe ist es, sich um Zusammenhalt und Kohärenz zu kümmern. Wenn sich die Anforderungen zweimal täglich ändern, sollte Ihre Lösung nicht darin bestehen, die Codequalität zu beeinträchtigen. Sagen Sie dem Kunden einfach, wie lange es dauert, und wenn Sie der Meinung sind, dass Sie mehr Zeit benötigen, um das Design "richtig" zu machen, fügen Sie jeder Schätzung einen ausreichend großen Sicherheitsspielraum hinzu. Verwenden Sie das "Scotty-Prinzip", besonders wenn ein Kunde versucht, Druck auf Sie auszuüben . Und wenn Sie sich mit einem nicht-technischen Kunden über den Aufwand streiten, vermeiden Sie Begriffe wie "Refactoring", "Unit-Tests", "Design Patterns" oder "Code-Dokumentation" - das sind Dinge, die er nicht versteht und die er wahrscheinlich für "unnötig" hält Unsinn ", weil er keinen Wert darin sieht. oder zumindest für den Kunden verständlich (Funktionen, Unterfunktionen, Verhaltensänderungen, Benutzerdokumente, Fehlerkorrekturen, Leistungsoptimierung usw.).

  • Die Lösung für sich schnell ändernde Anforderungen besteht darin, den Code schnell zu ändern

Wenn sich die zugrunde liegenden Schnittstellen drei Monate lang zweimal täglich ändern, sollte die Lösung nicht darin bestehen, den Code zweimal täglich zu ändern. Die eigentliche Lösung besteht darin, zu fragen, warum sich die Anforderungen so häufig ändern und ob es möglich ist, an diesem Teil des Prozesses Änderungen vorzunehmen. Vielleicht hilft noch eine Vorausanalyse. Möglicherweise ist die Schnittstelle zu breit, weil die Grenze zwischen den Komponenten falsch gewählt wurde. Manchmal ist es hilfreich, weitere Informationen darüber anzufordern, welcher Teil der Anforderungen stabil ist und welcher noch diskutiert wird (und tatsächlich die Implementierung für die diskutierten Dinge zu verschieben). Und manchmal müssen manche Leute einfach "in den Arsch getreten" werden, um nicht zweimal am Tag ihre Meinung zu ändern.

Doc Brown
quelle
30
+1 - Sie können die Probleme der Menschen nicht mit technischen Lösungen lösen.
Telastyn
1
+1, aber "oder zumindest besser entwickelbar (das heißt: einfacher an sich ändernde Anforderungen anzupassen)" - ich würde dies mit sich vernünftigerweise ändernden Anforderungen qualifizieren, oder?
Fuhrmanator
1
@Fuhrmanator: Ich denke, das ist allgemein schwer zu diskutieren. IMHO ist es offensichtlich, dass kein Entwurfsmuster Ihnen helfen wird, wenn Ihre erste Anforderung "Wir brauchen ein Textverarbeitungsprogramm" ist und dies in "Wir brauchen einen Flugsimulator" geändert wird. Bei weniger drastischen Anforderungsänderungen ist es nicht immer einfach zu entscheiden, was Ihnen dabei hilft, Ihre Software weiterzuentwickeln. Das Beste ist meiner Meinung nach, nicht zu viele Muster anzuwenden, sondern einige Prinzipien - hauptsächlich die SOLID-Prinzipien und YAGNI. Und ich stimme Telastyn zu 100% zu. Wenn sich die Anforderungen zu oft ändern, ist dies wahrscheinlich kein technisches Problem.
Doc Brown
4
+1 - "Ganz ehrlich, wenn sich die zugrunde liegenden Schnittstellen drei Monate lang zweimal täglich ändern, sollte die Lösung nicht darin bestehen, den Code zweimal täglich zu ändern. Die eigentliche Lösung besteht darin, zu fragen, warum sich die Anforderungen so oft ändern, und wenn Es ist möglich, an diesem Teil des Prozesses eine Änderung vorzunehmen. "Wenn Sie ständig neue Anweisungen erhalten, ist es am besten, sich mit allen Beteiligten zusammenzusetzen und die Erwartungen erneut zu bekräftigen. Arbeiten Sie Ihre Differenzen aus und verschwenden Sie hoffentlich nicht die Zeit und das Geld aller, indem Sie dem Projekt ein klareres Ziel geben.
Krillgar
1
@Cornelius Doc Brown sagte, es sei schwer ohne Konkretheit. Anforderungen, die von einem Textverarbeitungsprogramm an einen Flugsimulator gestellt werden, wären nicht angemessen. kein Designmuster würde helfen. Zwischen seinem Beispiel und dem Hinzufügen eines neuen Dateiformats zur Speicherfunktion eines Textverarbeitungsprogramms (was sehr vernünftig ist) liegt eine ganze Menge Grauzonen. Ohne Einzelheiten ist es schwer zu diskutieren. Es ist auch nicht so, dass man sich nicht ändern möchte . Es ist so, dass solche Änderungen schwierig sind, wenn Sie bereits frühzeitig eine Entwurfsentscheidung basierend auf einer Anforderung getroffen haben. Die Textverarbeitung vs. Flugsimulation ist ein gutes Beispiel für die frühe Wahl.
Fuhrmanator
43

Meiner bescheidenen Meinung nach sollten Sie die Verwendung von Entwurfsmustern nicht vermeiden oder nicht vermeiden.

Entwurfsmuster sind einfach bekannte und vertrauenswürdige Lösungen für allgemeine Probleme, die mit Namen versehen wurden. Sie unterscheiden sich in technischer Hinsicht nicht von jeder anderen Lösung oder Konstruktion, die Sie sich vorstellen können.

Ich denke, die Wurzel des Problems könnte darin liegen, dass Ihr Freund überlegt, ein Entwurfsmuster anzuwenden oder nicht anzuwenden, anstatt darüber nachzudenken, was die beste Lösung ist, die ich mir vorstellen kann, einschließlich, aber nicht beschränkt auf die Muster Ich kenne".

Vielleicht führt dieser Ansatz dazu, dass er Muster auf teilweise künstliche oder erzwungene Weise an Orten verwendet, an denen sie nicht dazu gehören. Und das führt zu einem Durcheinander.

Aviv Cohn
quelle
13
+1 "Entwurfsmuster sind einfach bekannte und vertrauenswürdige Lösungen für allgemeine Probleme, die mit Namen versehen wurden. Sie unterscheiden sich in technischer Hinsicht nicht von anderen Lösungen oder Entwürfen, die Sie sich vorstellen können." Genau. Die Leute sind so in benannte Entwurfsmuster verstrickt, dass sie vergessen, dass dies nichts anderes als Namen für verschiedene Strategien sind, die es uns Programmierern erleichtern, miteinander über unseren Code und unsere Entwurfsentscheidungen zu kommunizieren. Diese Einstellung wird sehr oft mit dem Versuch in Verbindung gebracht, unangemessene "Muster" bei Problemen zu erzwingen, die nicht unbedingt von Nutzen sind - großes Durcheinander.
Jason C
14

In Ihrem Beispiel für die Verwendung des Null-Objektmusters ist es meines Erachtens letztendlich gescheitert, weil es den Anforderungen des Programmierers und nicht den Anforderungen des Kunden entsprach. Der Kunde musste den Preis in einer dem Kontext entsprechenden Form anzeigen. Der Programmierer musste einen Teil des Anzeigecodes vereinfachen.

Wenn also ein Entwurfsmuster nicht den Anforderungen entspricht, sagen wir, dass alle Entwurfsmuster Zeitverschwendung sind, oder sagen wir, dass wir ein anderes Entwurfsmuster benötigen?

BobDalgleish
quelle
9

Es scheint, dass der Fehler eher darin bestand, die Musterobjekte zu entfernen, als sie zu verwenden. Im ursprünglichen Entwurf scheint das Null-Objekt eine Lösung für ein Problem geliefert zu haben. Dies war möglicherweise nicht die beste Lösung.

Wenn Sie die einzige Person sind, die an einem Projekt arbeitet, können Sie den gesamten Entwicklungsprozess miterleben. Der große Nachteil ist, dass Sie niemanden als Mentor haben. Es ist wahrscheinlich, dass sich die Zeit zum Erlernen und Anwenden der besten oder besseren Praktiken schnell auszahlt. Der Trick besteht darin, zu ermitteln, welche Übung wann gelernt werden muss.

Verkettung von Referenzen in der Form product.Price.toString ('c') verstößt gegen das Gesetz von Demeter . Ich habe alle möglichen Probleme mit dieser Praxis gesehen, von denen sich viele auf Nullen beziehen. Eine Methode wie product.displayPrice ('c') kann Nullpreise intern verarbeiten. Ebenso könnte product.Description.Current von product.displayDescription (), product.displayCurrentDescription () behandelt werden. oder product.diplay ('Current').

Der Umgang mit der neuen Anforderung an Manager und Inhaltsanbieter muss durch Reaktion auf den Kontext erfolgen. Es gibt eine Vielzahl von Ansätzen, die verwendet werden können. Die Factory-Methoden können abhängig von der Benutzerklasse, der sie angezeigt werden, unterschiedliche Produktklassen verwenden. Ein anderer Ansatz wäre, dass die Darstellungsmethoden der Produktklassen unterschiedliche Daten für unterschiedliche Benutzer erstellen.

Die gute Nachricht ist, dass Ihr Freund merkt, dass die Dinge außer Kontrolle geraten. Hoffentlich hat er den Code in der Versionskontrolle. Dies ermöglicht ihm, schlechte Entscheidungen, die er ausnahmslos treffen wird, zurückzudrängen. Ein Teil des Lernens besteht darin, verschiedene Ansätze auszuprobieren, von denen einige scheitern werden. Wenn er mit den nächsten Monaten fertig wird, könnte er Ansätze finden, die sein Leben vereinfachen und die Spaghetti aufräumen. Er könnte versuchen, jede Woche eine Sache zu reparieren.

BillThor
quelle
2
Dies könnte auch darauf hinweisen, dass das an der Oberfläche verwendete Modell ungeeignet ist, da man das Demeter-Gesetz "brechen" muss. Warum hat das "Ansichtsmodell" (im weitesten Sinne verwendet) nicht nur die Beschreibung, die angezeigt werden soll? (Das heißt, warum gibt es auf der Benutzeroberflächenebene mehr als nur die aktuelle Beschreibung?) Die Business-Ebene könnte ein entsprechend gefülltes Objekt für die Benutzeroberflächenebene vorbereiten, das bereits unterschiedliche Inhalte aufweist, je nachdem, ob es sich um den Manager handelt oder nicht.
Cornelius
7

Die Frage scheint in so vielen Punkten falsch zu sein. Aber die offensichtlichen sind:

  • Für das von Ihnen erwähnte Null-Objektmuster ändern Sie nach Änderung der Anforderungen einen Teil des Codes. Das ist in Ordnung, aber es bedeutet nicht, dass Sie das Null-Objekt-Muster "ermorden" (Übrigens, seien Sie vorsichtig mit Ihrer Formulierung, das klingt zu extrem, manche Leute, die zu paranoisch sind, sehen das überhaupt nicht als lustig an).

Viele Leute haben richtig gesagt, Design Patterns handeln sehr stark vom Beschriften und Benennen einer gängigen Praxis. Denken Sie also an ein Hemd, ein Hemd hat einen Kragen, aus irgendeinem Grund entfernen Sie den Kragen oder einen Teil des Kragens. Die Benennung und Beschriftung ändert sich, aber es ist immer noch ein T-Shirt im Wesentlichen. Genau das ist hier der Fall, kleine Detailänderungen, die nicht bedeuten, dass Sie dieses Muster "ermordet" haben. (Nochmals die extreme Formulierung beachten)

  • Das Design, über das Sie gesprochen haben, ist schlecht, weil Sie überall massive strategische Designänderungen vornehmen, wenn sich die Anforderungen geringfügig ändern. Solange sich das allgemeine Geschäftsproblem nicht ändert, können Sie massive Designänderungen nicht rechtfertigen.

Nach meiner Erfahrung müssen Sie bei geringen Anforderungen nur einen kleinen Teil der Codebasis ändern. Einige mögen ein bisschen hacky sein, aber nichts zu ernstes, um die Wartbarkeit oder Lesbarkeit wesentlich zu beeinträchtigen, und oft genügen ein paar Zeilen mit Kommentaren, um den hacky-Teil zu erklären. Dies ist auch eine sehr verbreitete Praxis.

InformedA
quelle
7

Lassen Sie uns einen Moment innehalten und uns das grundlegende Problem hier ansehen - die Architektur eines Systems, bei dem das Architekturmodell zu stark an systemnahe Features gekoppelt ist, sodass die Architektur im Entwicklungsprozess häufig unterbrochen wird.

Ich denke, wir müssen uns daran erinnern, dass die Verwendung von Architektur- und Designmustern, die damit zusammenhängen, auf einer angemessenen Ebene verlegt werden muss und dass die Analyse der richtigen Ebene nicht trivial ist. Auf der einen Seite können Sie die Architektur Ihres Systems mit nur sehr grundlegenden Einschränkungen wie "MVC" oder ähnlichem leicht auf einem zu hohen Niveau halten, was dazu führen kann, dass Gelegenheiten wie klare Richtlinien und die Nutzung von Code verpasst werden und wo Spaghetti-Code leicht möglich ist gedeihen in all dem freien Raum.

Auf der anderen Seite könnten Sie Ihr System genauso gut überplanen, wie die Einschränkungen auf einer bis ins Detail gehenden Ebene festzulegen, wobei Sie davon ausgehen, dass Sie sich auf Einschränkungen verlassen können, die in der Realität volatiler sind als erwartet, und ständig Ihre Einschränkungen brechen und zwingt Sie, ständig umzugestalten und wieder aufzubauen, bis Sie zu verzweifeln beginnen.

Änderungen der Anforderungen an ein System werden immer in geringerem oder größerem Umfang vorhanden sein. Und die potenziellen Vorteile der Verwendung von Architektur- und Designmustern sind immer vorhanden, sodass es nicht wirklich darum geht, Designmuster zu verwenden oder nicht, sondern auf welcher Ebene Sie sie verwenden sollten.

Dies setzt voraus, dass Sie nicht nur die aktuellen Anforderungen des vorgeschlagenen Systems verstehen, sondern auch wissen, welche Aspekte davon als stabile Kerneigenschaften des Systems angesehen werden können und welche Eigenschaften sich möglicherweise im Laufe der Entwicklung ändern.

Wenn Sie feststellen, dass Sie ständig mit unorganisiertem Spaghetti-Code kämpfen müssen, tun Sie wahrscheinlich nicht genug Architektur oder auf einem zu hohen Niveau. Wenn Sie feststellen, dass Ihre Architektur häufig kaputt geht, arbeiten Sie wahrscheinlich mit einer zu detaillierten Architektur.

Die Verwendung von Architektur- und Designmustern ist nicht etwas, in das man ein System einfach "einkleiden" kann, als würde man einen Schreibtisch streichen. Hierbei handelt es sich um Techniken, die mit Bedacht angewendet werden sollten, und zwar auf einer Ebene, auf der die Abhängigkeiten, auf die Sie sich verlassen müssen, eine hohe Wahrscheinlichkeit haben, stabil zu sein, und auf der sich die Mühe lohnt, die Architektur zu modellieren und die tatsächlichen Abhängigkeiten / Architekturen / Muster zu implementieren als Code.

In Bezug auf das Problem einer zu detaillierten Architektur können Sie sich auch nur anstrengen, wenn die Architektur nicht viel wert ist. Siehe risikobasierte Architektur als Referenz. Ich mag dieses Buch. Gerade genug Softwarearchitektur , vielleicht auch.

Bearbeiten

Ich habe meine Antwort geklärt, da mir klar wurde, dass ich mich oft als "zu viel Architektur" ausdrückt, wobei ich wirklich "zu detaillierte Architektur" meinte, was nicht genau dasselbe ist. Eine zu detaillierte Architektur kann wahrscheinlich oft als "zu viel" Architektur angesehen werden, aber selbst wenn Sie die Architektur auf einem guten Niveau halten und das schönste System schaffen, das die Menschheit jemals gesehen hat, könnte dies zu viel Aufwand für die Architektur bedeuten, wenn die Prioritäten liegen auf Funktionen und Time-to-Market.

Alex
quelle
+1 Das sind sehr gute Gedanken über das sehr hohe Niveau, von dem ich denke, dass Sie es in einem System suchen müssen, aber wie Sie sagten, erfordert es viel Erfahrung im Software-Design.
Samuel
4

Ihr Freund scheint aufgrund seiner Anekdote mit zahlreichen Gegenwinden konfrontiert zu sein. Das ist bedauerlich und kann ein sehr schwieriges Umfeld für die Arbeit sein. Trotz der Schwierigkeit war er auf dem richtigen Weg, Muster zu verwenden, um sein Leben leichter zu machen, und es ist eine Schande, dass er diesen Weg verlassen hat. Der Spaghetti-Code ist das ultimative Ergebnis.

Da es zwei verschiedene Problembereiche gibt, technische und zwischenmenschliche, werde ich sie jeweils separat ansprechen.

Zwischenmenschlich

Der Kampf, den Ihr Freund hat, ist mit sich schnell ändernden Anforderungen verbunden und wie sich dies auf seine Fähigkeit auswirkt, wartbaren Code zu schreiben. Ich würde zunächst sagen, dass Anforderungen, die sich über einen so langen Zeitraum zweimal täglich ändern, ein größeres Problem darstellen und eine unrealistische implizite Erwartung haben. Die Anforderungen ändern sich schneller als sich der Code ändern kann. Wir können nicht erwarten, dass der Code oder der Programmierer mithalten. Dieses schnelle Änderungstempo ist symptomatisch für eine unvollständige Konzeption des gewünschten Produkts auf einer höheren Ebene. Das ist ein Problem. Wenn sie nicht wissen, was sie wirklich wollen, verschwenden sie viel Zeit und Geld, um es nie zu bekommen.

Es kann sinnvoll sein, Grenzen für Änderungen festzulegen. Gruppieren Sie Änderungen alle zwei Wochen zu Sets und frieren Sie sie dann für die zwei Wochen ein, während sie implementiert werden. Erstellen Sie eine neue Liste für die nächsten zwei Wochen. Ich habe das Gefühl, dass sich einige dieser Änderungen überschneiden oder widersprechen (z. B. zwischen zwei Optionen hin und her waffeln). Wenn Veränderungen schnell und heftig vor sich gehen, haben sie alle höchste Priorität. Wenn Sie sie zu einer Liste zusammenfassen, können Sie mit ihnen zusammenarbeiten, um das zu organisieren und zu priorisieren, was für die Maximierung des Aufwands und der Produktivität am wichtigsten ist. Sie sehen möglicherweise, dass einige ihrer Änderungen albern oder weniger wichtig sind und geben Ihrem Freund etwas Luft zum Atmen.

Diese Probleme sollten Sie jedoch nicht davon abhalten, guten Code zu schreiben. Schlechter Code führt zu schlimmeren Problemen. Es kann einige Zeit dauern, die Umgestaltung von einer Lösung zu einer anderen durchzuführen, aber genau die Tatsache, dass dies möglich ist Anspruch nehmen zeigt die Vorteile guter Codierungspraktiken anhand von Mustern und Prinzipien.

In einer Umgebung mit häufigen Änderungen, technische Schulden Wille fällig. Es ist weitaus besser, dafür Zahlungen zu leisten, als das Handtuch zu werfen und zu warten, bis es zu groß wird, um es zu überwinden. Wenn ein Muster nicht mehr nützlich ist, korrigieren Sie es, aber kehren Sie nicht zu den Cowboy-Codierungsmethoden zurück.

Technisch

Ihr Freund scheint ein gutes Verständnis für die grundlegenden Designmuster zu haben. Null-Objekt ist ein guter Ansatz für das Problem, mit dem er konfrontiert war. In Wahrheit ist es immer noch ein guter Ansatz. Wo er Herausforderungen zu haben scheint, ist das Verständnis der Prinzipien hinter den Mustern, das Warum von dem, was sie sind. Ansonsten glaube ich nicht, dass er seinen Ansatz aufgegeben hätte.

(Was folgt, ist eine Reihe von technischen Lösungen, die in der ursprünglichen Frage nicht gefragt wurden, die aber zeigen, wie wir uns zu Illustrationszwecken an Muster halten können.)

Das Prinzip hinter null Objekt ist die Idee, das zu verkapseln, was variiert . Wir verstecken, was sich ändert, damit wir nicht überall anders damit umgehen müssen. Hier kapselte das Nullobjekt die Varianz in der product.PriceInstanz (ich werde es ein PriceObjekt nennen und ein Nullobjektpreis wird sein NullPrice). Priceist ein Domain-Objekt, ein Geschäftskonzept. Manchmal kennen wir in unserer Geschäftslogik den Preis noch nicht. Das passiert. Perfekter Anwendungsfall für das Null-Objekt. Prices haben eine ToStringMethode, die den Preis ausgibt, oder eine leere Zeichenfolge, wenn sie nicht bekannt ist (oder NullPrice#ToStringeine leere Zeichenfolge zurückgibt). Dies ist ein vernünftiges Verhalten. Dann ändern sich die Anforderungen.

Wir müssen ein nullan die API-Ansicht oder einen anderen String an die Manager-Ansicht ausgeben . Wie wirkt sich das auf unsere Geschäftslogik aus? Nun, das tut es nicht. In der obigen Aussage habe ich das Wort 'view' zweimal verwendet. Dieses Wort wurde wahrscheinlich nicht explizit gesprochen, aber wir müssen uns darin üben, die verborgenen Wörter in Anforderungen zu hören. Warum ist das Sehen so wichtig? Weil es uns sagt, wo der Wandel wirklich stattfinden muss : aus unserer Sicht.

Abgesehen davon : Ob wir ein MVC-Framework verwenden oder nicht, spielt hier keine Rolle. Während MVC eine sehr spezifische Bedeutung für "Ansicht" hat, verwende ich es in der allgemeineren (und möglicherweise zutreffenderen) Bedeutung eines Teils des Präsentationscodes.

Wir müssen dies also wirklich in der Ansicht korrigieren. Wie könnten wir das machen? Der einfachste Weg, dies zu tun, wäre eine ifAussage. Ich weiß, das Null-Objekt sollte alle Wenns loswerden, aber wir müssen pragmatisch sein. Wir können die Zeichenfolge überprüfen, um festzustellen, ob sie leer ist, und wechseln:

if(product.Price.ToString("c").Length == 0) { // one way of many
    writer.write("Price unspecified [Change]");
} else {
    writer.write(product.Price.ToString("c"));
}

Wie wirkt sich das auf die Verkapselung aus? Der wichtigste Teil hier ist, dass die Ansichtslogik in der Ansicht gekapselt ist . Auf diese Weise können wir unsere Geschäftslogik / Domänenobjekte vollständig von Änderungen in der Sichtlogik isolieren. Es ist hässlich, aber es funktioniert. Dies ist jedoch nicht die einzige Option.

Wir könnten sagen, dass sich unsere Geschäftslogik dahingehend geändert hat, dass wir Standardzeichenfolgen ausgeben möchten, wenn kein Preis festgelegt ist. Wir können unsere Price#ToStringMethode geringfügig anpassen (tatsächlich eine überladene Methode erstellen). Wir können einen Standard-Rückgabewert akzeptieren und diesen zurückgeben, wenn kein Preis festgelegt ist:

class Price {
    ...
    // A new ToString method
    public string ToString(string c, string default) {
        return ToString(c);
    }
    ...
}

class NullPrice {
    ...
    // A new ToString method
    public string ToString(string c, string default) {
        return default;
    }
    ...
}

Und jetzt wird unser Ansichtscode:

writer.write(product.Price.ToString("c", "Price unspecified [Change]"));

Die Bedingung ist weg. Wenn Sie dies jedoch zu oft tun, kann dies zu einer Zunahme von Sonderfallmethoden in Ihren Domänenobjekten führen. Dies ist daher nur dann sinnvoll, wenn dies nur in wenigen Fällen vorkommt.

Wir könnten stattdessen eine IsSetMethode erstellen Price, die einen Booleschen Wert zurückgibt:

class Price {
    ...
    public bool IsSet() {
        return return true;
    }
    ...
}

class NullPrice {
    ...
    public bool IsSet() {
        return false;
    }
    ...
}

Logik anzeigen:

if(product.Price.IsSet()) {
    writer.write(product.Price.ToString("c"));
} else {
    writer.write("Price unspecified [Change]");
}

Wir sehen die Rückkehr der Bedingung in der Ansicht, aber die Geschäftslogik sagt mehr, wenn der Preis festgelegt ist. Wir können es Price#IsSetjetzt woanders verwenden, da wir es zur Verfügung haben.

Schließlich können wir die Idee, einen Preis vollständig zu präsentieren, in einem Helfer für die Ansicht zusammenfassen. Dies würde die Bedingung verbergen, während das Domänenobjekt so weit erhalten bleibt, wie wir möchten:

class PriceStringHelper {
    public PriceStringHelper() {}

    public string PriceToString(Price price, string default) {
        if(price.IsSet()) { // or use string length to not change the Price class at all
           return price.ToString("c");
        } else {
            return default;
        }
    }
}

Logik anzeigen:

writer.write(new PriceStringHelper().PriceToString(product.Price, "Price unspecified [Change]"));

Es gibt viele weiteren Möglichkeiten , um die Änderungen zu machen (wir verallgemeinern könnten PriceStringHelperin ein Objekt , dass die Renditen ein Standard , wenn ein String leer ist), aber diese sind ein paar schnell diejenigen , die (zum größten Teil) erhalten sowohl die Muster und die Prinzipien, wie sowie der pragmatische Aspekt einer solchen Änderung.

cbojar
quelle
3

Die Komplexität eines Entwurfsmusters kann Sie beißen, wenn das Problem, das es lösen sollte, plötzlich verschwindet. Leider wird dieses Risiko aufgrund der Begeisterung und Popularität von Designmustern nur selten explizit genannt. Die Anekdote Ihres Freundes hilft viel, um zu zeigen, wie sich Muster nicht auszahlen. Jeff Atwood hat einige gute Worte zum Thema.

Dokumentieren Sie Variationspunkte (sie sind Risiken) in Anforderungen

Viele der komplexeren Entwurfsmuster (Null-Objekt weniger) enthalten das Konzept geschützter Variationen , dh "Identifizieren von Punkten mit vorhergesagten Variationen oder Instabilitäten; Zuweisen von Verantwortlichkeiten zum Erstellen einer stabilen Schnittstelle um diese herum". Adapter, Besucher, Fassade, Ebenen, Beobachter, Strategie, Dekorateur usw. nutzen dieses Prinzip. Sie "zahlen sich aus", wenn die Software in der Dimension der erwarteten Variabilität erweitert werden muss und die "stabilen" Annahmen stabil bleiben.

Wenn Ihre Anforderungen so instabil sind, dass Ihre "vorhergesagten Abweichungen" immer falsch sind, verursachen die angewendeten Muster Schmerzen oder sind bestenfalls nicht benötigte Komplexität.

Craig Larman spricht von zwei Möglichkeiten, geschützte Variationen anzuwenden:

  • Variationspunkte - im vorhandenen, aktuellen System oder in den Anforderungen, z. B. mehrere Schnittstellen, die unterstützt werden müssen, und
  • Evolutionspunkte - spekulative Variationspunkte, die in bestehenden Anforderungen nicht vorhanden sind.

Beide sollten von Entwicklern dokumentiert werden, aber Sie sollten wahrscheinlich Kundenbindung für die Variationspunkte haben.

Um das Risiko zu managen, kann man sagen, dass jedes Entwurfsmuster, das PV anwendet, auf einen Variationspunkt in den vom Kunden festgelegten Anforderungen zurückgeführt werden sollte. Wenn ein Kunde einen Variationspunkt in den Anforderungen ändert, muss sich Ihr Design möglicherweise grundlegend ändern (weil Sie wahrscheinlich in Design [Muster] investiert haben, um diese Variation zu unterstützen). Keine Notwendigkeit, Zusammenhalt, Kopplung usw. zu erklären.

Zum Beispiel möchte Ihr Kunde, dass die Software mit drei verschiedenen Bestandsführungssystemen funktioniert. Das ist ein Variationspunkt, um den Sie herum entwerfen. Wenn der Kunde diese Anforderung nicht erfüllt, verfügen Sie natürlich über eine Reihe nutzloser Designinfrastrukturen. Der Kunde muss wissen, dass Variationspunkte etwas kosten.

CONSTANTS im Quellcode sind eine einfache Form von PV

Eine andere Analogie zu Ihrer Frage wäre, zu fragen, ob die Verwendung CONSTANTSim Quellcode eine gute Idee ist. Mit Bezug auf dieses Beispiel , sagen wir mal der Kunde die Notwendigkeit Passwörter gelöscht. Daher würde die MAX_PASSWORD_SIZEkonstante Verteilung in Ihrem Code unbrauchbar und sogar die Wartung und Lesbarkeit beeinträchtigen. Würden Sie die Verwendung von CONSTANTSals Grund dafür verantwortlich machen?

Fuhrmanator
quelle
2

Ich denke, es hängt zumindest teilweise von der Art Ihrer Situation ab.

Sie haben ständig wechselnde Anforderungen angesprochen. Wenn der Kunde sagt "Ich möchte, dass diese Imkereianwendung auch mit Wespen funktioniert", dann scheint dies die Art von Situation zu sein, in der sorgfältiges Design den Fortschritt unterstützen und nicht behindern würde (insbesondere, wenn Sie bedenken, dass sie dies in Zukunft tun möchte Halten Sie auch Fruchtfliegen.)

Auf der anderen Seite, wenn die Art der Änderung eher so aussieht wie "Ich möchte, dass diese Imkerei-Anwendung die Gehaltsabrechnung meines Waschsalon-Konglomerats verwaltet", werden Sie von keiner Codemenge aus Ihrem Loch herausgeholt.

Designmuster haben nichts von Natur aus Gutes. Sie sind Werkzeuge wie jedes andere - wir verwenden sie nur, um unsere Arbeit mittel- bis langfristig zu erleichtern. Wenn ein anderes Werkzeug (wie Kommunikation oder Recherche ) nützlicher ist, verwenden wir dieses.

Benjamin Hodgson
quelle