Sollte die Lösung so allgemein wie möglich oder so spezifisch wie möglich sein?

124

Angenommen, ich habe eine Entität mit dem Attribut "Typ". Es könnte mehr als 20 mögliche Typen geben.

Jetzt werde ich gebeten, etwas zu implementieren, mit dem der Typ von A-> B geändert werden kann. Dies ist der einzige Anwendungsfall.

Soll ich also etwas implementieren, das willkürliche Typänderungen zulässt, solange es sich um gültige Typen handelt? Oder sollte ich NUR zulassen, dass es sich je nach Anforderung von A-> B ändert, und andere Typänderungen wie B-> A oder A-> C ablehnen?

Ich kann die Vor- und Nachteile von beiden Seiten erkennen, wo eine generische Lösung weniger Arbeit bedeuten würde, falls in Zukunft eine ähnliche Anforderung auftaucht, aber auch eine größere Wahrscheinlichkeit, dass etwas schief geht (obwohl wir den Anrufer dabei zu 100% kontrollieren) Punkt).
Eine bestimmte Lösung ist weniger fehleranfällig, erfordert jedoch in Zukunft mehr Arbeit, wenn sich eine ähnliche Anforderung ergibt.

Ich höre immer wieder, dass ein guter Entwickler versuchen sollte, die Änderung vorwegzunehmen und das System so zu gestalten, dass es in Zukunft leicht zu erweitern ist. Was hört sich nach einer generischen Lösung an?

Bearbeiten:

Hinzufügen weiterer Details zu meinem nicht so spezifischen Beispiel: Die "generische" Lösung erfordert in diesem Fall weniger Arbeit als die "spezifische" Lösung, da die spezifische Lösung eine Validierung sowohl für den alten als auch für den neuen Typ erfordert, während die generische Lösung müssen nur den neuen Typ validieren.

LoveProgramming
quelle
97
Das ist eine interessante Frage. Ich habe mit meinen Gramps (einem sehr, sehr alten Timer-Programmierer) eine Diskussion über so etwas geführt, und seine Antwort lautete "die allgemeinste Sache, die Ihr spezifisches Problem löst", was sich auf eine Fantasie hinauszählt. Es hängt davon ab, ob". Pauschalaussagen in der Softwareentwicklung funktionieren selten - Dinge sollten immer von Fall zu Fall entschieden werden.
T. Sar
2
@ T.Sar dies als Antwort hinzufügen und ich werde upvote :-)
Christophe
4
Was ist aus Ihrer Sicht eine „generische Lösung“? Bitte klären Sie auch, was Sie mit dem "einzigen Anwendungsfall" meinen. Meinen Sie, dass nur ein Übergang von A-> B zulässig ist oder nur dieser Übergang angegeben ist, und es wäre keine Fehlerbedingung, von einem anderen Zustand in einen anderen Zustand überzugehen. Als Entwickler müssen Sie den Autor des Anwendungsfalls um Klärung bitten. Wenn kein anderer Übergang zulässig ist und Ihr Code dies unabhängig davon zulässt, wer den Anrufer kontrolliert, hat Ihr Code die Anforderungen nicht erfüllt.
RibaldEddie
5
Das Prinzip, dass, wenn X eine gute Idee ist, nur X die ganze Zeit optimal sein muss, scheint einen besonderen Reiz für Entwickler (oder zumindest eine Vokalgruppe unter ihnen) zu haben wie bei Sprichwörtern findet man oft ein Paar mit entgegengesetzten Implikationen. Dies ist ein Zeichen dafür, dass Sie Ihr Urteilsvermögen (wie in den Antworten hier vertreten) einsetzen und sich vor dem Dogma hüten sollten.
Sdenham
2
Vorzeitige Generalisierung kann so viel Sodbrennen verursachen wie vorzeitige Optimierung - Sie schreiben eine Menge Code, der möglicherweise nie verwendet wird. Zuerst für das spezifische Problem gelöst, dann bei Bedarf verallgemeinern.
John Bode

Antworten:

296

Meine Faustregel:

  1. Wenn Sie das erste Mal auf das Problem stoßen, lösen Sie nur das spezifische Problem (dies ist das YAGNI- Prinzip).
  2. Wenn Sie das zweite Mal auf dasselbe Problem stoßen, sollten Sie erwägen, den ersten Fall zu verallgemeinern, wenn es nicht viel Arbeit ist
  3. Wenn Sie drei spezifische Fälle haben, in denen Sie die verallgemeinerte Version verwenden können, sollten Sie damit beginnen, die verallgemeinerte Version wirklich zu planen. Inzwischen sollten Sie das Problem gut genug verstehen, um es tatsächlich verallgemeinern zu können.

Dies ist natürlich eine Richtlinie und keine feste Regel: Die wirkliche Antwort besteht darin, von Fall zu Fall nach bestem Wissen zu urteilen.

Daniel Pryden
quelle
1
Können Sie erklären, was YAGNI ist?
Bernhard
7
@Bernhard Du wirst es nicht brauchen . Ein sehr nützliches Software-Design-Prinzip.
Angew
4
" thing1, thing2Denken Sie darüber nach, ein Array zu verwenden. thing1, thing2, thing3Verwenden Sie thing[]stattdessen mit ziemlicher Sicherheit ein Array." ist eine ähnliche Faustregel für die Entscheidung zwischen mehreren Variablen oder einem einzelnen Array.
Joker_vD
15
Ein anderer Weg, Regel Nr. 1 zu formulieren: "Sie können eine Sache nicht verallgemeinern."
Wayne Conrad
2
@SantiBailors: Wie Doc Brown in seiner Antwort ausführt, überschätzen wir häufig die Menge an verschwendetem Aufwand, die wir möglicherweise aufwenden, wenn wir den ersten bauen, der weggeworfen wird. In The Mythical Man-Month sagt Fred Brooks: "Planen Sie, einen wegzuwerfen - das werden Sie jedenfalls." Das heißt: Wenn Sie sofort auf mehr als eine Verwendung für etwas stoßen - zum Beispiel, indem Sie eine Reihe von Anforderungen erhalten, bei denen Sie das gleiche Problem eindeutig mehr als einmal lösen müssen -, müssen Sie bereits mehr als einen Fall verallgemeinern von, und das ist völlig in Ordnung und nicht im Widerspruch zu meiner Antwort.
Daniel Pryden
95

Eine spezifische [...] Lösung erfordert in Zukunft mehr Arbeit, wenn ähnliche Anforderungen erforderlich sind

Ich habe dieses Argument mehrere Dutzend Mal gehört, und meiner Erfahrung nach erweist es sich regelmäßig als Irrtum. Wenn Sie jetzt oder später verallgemeinern, wenn die zweite ähnliche Anforderung auftritt, wird die Arbeit insgesamt fast gleich sein. Es ist also absolut sinnlos, zusätzlichen Aufwand in die Verallgemeinerung zu investieren, wenn Sie nicht wissen, dass sich dieser Aufwand jemals auszahlt.

(Dies gilt natürlich nicht, wenn eine allgemeinere Lösung weniger kompliziert ist und weniger Aufwand erfordert als eine bestimmte, aber meiner Erfahrung nach sind dies seltene Fälle. Eine solche Art von Szenario wurde anschließend in die Frage bearbeitet, und es ist nicht die Eine meiner Antworten ist ungefähr).

Wenn der "zweite ähnliche Fall" auftaucht, ist es an der Zeit, über eine Verallgemeinerung nachzudenken. Eine korrekte Verallgemeinerung ist dann viel einfacher , da diese zweite Anforderung Ihnen ein Szenario bietet, in dem Sie überprüfen können, ob Sie die richtigen allgemeinen Dinge festgelegt haben. Wenn Sie versuchen, nur einen Fall zu verallgemeinern, schießen Sie im Dunkeln. Die Chancen stehen gut, dass Sie bestimmte Dinge, die nicht verallgemeinert werden müssen, übergeneralisieren und andere Teile übersehen, die es sollten. Und wenn dann ein zweiter Fall auftritt und Sie feststellen, dass Sie die falschen Dinge verallgemeinert haben, müssen Sie noch viel mehr tun, um dies zu beheben.

Deshalb empfehle ich, die Versuchung, Dinge zu tun, "nur für den Fall" zu verschieben. Dieser Ansatz wird nur dann zu mehr Arbeits- und Wartungsaufwand führen, wenn Sie die Gelegenheit verpasst haben, drei-, vier- oder mehrmals zu generalisieren und dann einen Stapel ähnlich aussehenden (also duplizierten) Codes zu warten.

Doc Brown
quelle
2
Für mich macht diese Antwort im Kontext von OP keinen Sinn. Er sagt, dass er jetzt weniger Zeit für die Verallgemeinerung und in Zukunft weniger Zeit aufwenden wird, es sei denn, die Implementierung muss in Zukunft spezifisch sein. Sie sprechen sich dagegen aus, "zusätzliche Anstrengungen in die Verallgemeinerung zu investieren", während OP zusätzliche Anstrengungen nur dann unternimmt, wenn sie nicht verallgemeinern. (denk nach) .... seltsam: $
msb
2
@msb: Dieser Kontext wurde hinzugefügt, nachdem ich meine Antwort geschrieben hatte und widerspricht den Aussagen des OP, insbesondere in dem von mir zitierten Teil. Siehe meine Bearbeitung.
Doc Brown
2
Außerdem wird die verallgemeinerte Lösung komplizierter. Wenn Sie sie für den zweiten Fall anpassen müssen, dauert es viel länger, bis Sie sie wieder verstehen.
AndreKR
4
Doc, beiseite, hätte aber nie gedacht, dass Sie kein Muttersprachler sind. Ihre Antworten sind immer gut geschrieben und gut argumentiert.
user949300
2
Ich stelle mir Ihren Akzent vor, wenn ich Ihre zukünftigen Antworten durchlese. :-)
user949300
64

TL; DR: Es hängt davon ab, was Sie zu lösen versuchen.

Ich hatte ein ähnliches Gespräch mit meinen Gramps darüber, während wir darüber sprachen, wie großartig Func und Action in C # sind. My Gramps ist ein sehr alter Timer-Programmierer, bei dem es um Quellcodes ging, seit Software auf Computern ausgeführt wurde, die einen ganzen Raum beanspruchten.

Er wechselte mehrmals in seinem Leben die Technik. Er schrieb Code in C, COBOL, Pascal, BASIC, Fortran, Smalltalk und Java und begann schließlich C # als Hobby. Ich lernte mit ihm zu programmieren, saß auf seinem Schoß, als ich noch ein Entwickler war, und schnitzte meine ersten Codezeilen im blauen Editor von IBMs SideKick weg. Als ich 20 Jahre alt war, hatte ich bereits mehr Zeit damit verbracht, zu programmieren als draußen zu spielen.

Das sind ein paar meiner Erinnerungen, entschuldigen Sie mich, wenn ich beim Nacherzählen nicht ganz praktisch bin. Ich mag diese Momente ein bisschen.

Das hat er zu mir gesagt:


"Sollten wir uns um die Verallgemeinerung eines Problems bemühen oder es in einem bestimmten Bereich lösen, fragen Sie sich? Nun, das ist eine ... Frage."

Gramps machte eine Pause, um einen kurzen Moment darüber nachzudenken, während er die Position seiner Brille auf seinem Gesicht festlegte. Er spielte ein Match-3-Spiel auf seinem Computer, während er auf seinem alten Soundsystem eine Deep Purple-LP hörte.

"Nun, das hängt davon ab, welches Problem Sie lösen wollen", sagte er mir. "Es ist verlockend zu glauben, dass es eine einzige, heilige Lösung für alle Designentscheidungen gibt, aber es gibt keine. Sie sehen, Software-Architektur ist wie Käse."

"... Käse, Gramps?"

"Egal, was Sie über Ihren Favoriten denken, es wird immer jemanden geben, der denkt, dass es stinkt".

Ich blinzelte einen Moment verwirrt, aber bevor ich etwas sagen konnte, ging Gramps weiter.

"Wenn Sie ein Auto bauen, wie wählen Sie das Material für ein Teil aus?"

"Ich ... ich denke, es hängt von den damit verbundenen Kosten ab und was der Teil tun sollte, nehme ich an."

"Es kommt auf das Problem an, das das Teil zu lösen versucht. Sie werden keinen Reifen aus Stahl oder eine Windschutzscheibe aus Leder herstellen. Sie wählen das Material aus, das das vorliegende Problem am besten löst. Nun, was ist ein Problem?" Eine generische Lösung oder eine spezifische Lösung? Für welches Problem, für welchen Anwendungsfall? Sollten Sie sich für einen voll funktionsfähigen Ansatz entscheiden, um einem Code, der nur einmal verwendet wird, maximale Flexibilität zu verleihen? Sollten Sie einen sehr speziellen, fragilen Code schreiben? Ein Teil Ihres Systems, der viele, viele Verwendungen und möglicherweise viele Änderungen erfahren wird. Konstruktionsentscheidungen wie diese sind wie die Materialien, die Sie für ein Teil in einem Auto auswählen, oder die Form des Legosteins, den Sie für den Bau eines kleinen Hauses auswählen Welcher Legostein ist der beste? "

Der ältere Programmierer griff nach einem kleinen Lego-Modell, das er auf dem Tisch hat, bevor er fortfuhr.

"Das können Sie nur beantworten, wenn Sie wissen, wofür Sie diesen Baustein brauchen. Wie zum Teufel werden Sie wissen, ob die spezifische Lösung besser ist als die generische, oder umgekehrt, wenn Sie nicht einmal wissen, welches Problem Sie haben Versuche zu lösen? Du kannst keine Wahl hinter dir lassen, die du nicht verstehst. "

"..Haben Sie gerade die Matrix zitiert ? "

"Was?"

"Nichts, mach weiter."

"Nun, nehmen wir an, Sie versuchen, etwas für das National Invoice System zu entwickeln. Sie wissen, wie diese teuflische API und ihre XML-Datei mit ihren dreißigtausend Zeilen von innen aussehen. Wie würde eine" generische "Lösung zum Erstellen dieser Datei überhaupt aussehen? Die Datei ist voll von optionalen Parametern, voll von Fällen, die nur sehr bestimmte Branchen verwenden sollten. In den meisten Fällen können Sie sie ignorieren. Sie müssen kein generisches Rechnungssystem erstellen, wenn Sie das einzige sind, was Sie benötigen. Wenn Sie nur Schuhe verkaufen möchten, erstellen Sie einfach ein System für den Verkauf von Schuhen und machen Sie es zum besten Rechnungssystem für den Verkauf von Schuhen auf dem Markt. als eigenständiges, generisches Verkaufssystem weiterverkauft werden,zum Beispiel - jetzt ist es interessant, die Optionen zu implementieren, die nur für Gas, Lebensmittel oder Alkohol verwendet werden.Nun sind dies mögliche Anwendungsfälle. Vorher waren es nur einige hypothetische Don't Use Cases, und Sie möchten Don't Use Cases nicht implementieren . Don't use ist der kleine Bruder von Don't need . "

Gramps stellte den Lego-Zug wieder auf seinen Platz und wandte sich wieder seinem 3-Gewinnt-Spiel zu.

"Um eine generische oder eine spezifische Lösung für ein bestimmtes Problem zu finden, muss man zuerst verstehen, was zum Teufel dieses Problem ist. Andernfalls wird nur geraten, und das Erraten ist die Aufgabe von Managern, nicht von Programmierern. Wie fast alles in der IT, es kommt darauf an. "


Also, da hast du es. "Es hängt davon ab, ob". Das ist wahrscheinlich der mächtigste Ausdruck mit zwei Wörtern, wenn man über Software-Design nachdenkt.

T. Sar
quelle
14
Ich bin mir sicher, dass diese Geschichte etwas Nützliches enthält, aber es war zu schmerzhaft zum Lesen, sorry
GoatInTheMachine
28
Dein erster Beitrag war eine kurze lustige Anekdote - und natürlich ist es eine Ansichtssache - aber wenn ich nicht wirklich jemanden mag, der schreibt, finde ich lange Geschichten wie diese, die sich selbst
hingeben
16
@T.Sar, hör nicht auf die Hasser, dein Beitrag ist ein Juwel nützlicher Ratschläge von einem Guru. +1
LLlAMnYP
7
Der Teil "Software-Architektur ist wie Käse" war einfach nur genial. In der Tat ist diese Antwort ein Juwel!
Mathieu Guindon
3
Vielleicht ist diese Antwort auch wie Käse? ;) Aber bitte, "SmallTalk" sollte "Smalltalk" sein, und ja, ich bin dieser Typ, sorry.
fede s.
14

In erster Linie sollten Sie versuchen, vorauszusehen, ob eine solche Änderung wahrscheinlich ist - und nicht nur eine entfernte Möglichkeit irgendwo auf der ganzen Linie.

Wenn nicht, ist es normalerweise besser, sich jetzt für die einfache Lösung zu entscheiden und sie später zu erweitern. Es ist gut möglich, dass Sie ein klareres Bild davon haben, was dann benötigt wird.

doubleYou
quelle
13

Wenn Sie in einer für Sie neuen Domäne arbeiten, sollte die von Daniel Pryden erwähnte Dreierregel auf jeden Fall angewendet werden. Wie sollen Sie schließlich nützliche Abstraktionen erstellen, wenn Sie ein Neuling in diesem Bereich sind? Menschen sind oft selbstsicher, wenn es darum geht, Abstraktionen einzufangen, obwohl dies selten der Fall ist. Nach meiner Erfahrung ist eine vorzeitige Abstraktion nicht weniger schlimm als eine Code-Vervielfältigung. Falsche Abstraktionen sind wirklich schmerzhaft zu verstehen. Manchmal noch schmerzhafter zu refaktorieren.

Es gibt ein Buch , das meinen Standpunkt über einen unbekannten Bereich anspricht, in dem Entwickler arbeiten. Es besteht aus bestimmten Domänen mit extrahierten nützlichen Abstraktionen.

Zapadlo
quelle
Die Frage ist klar nach einem konkreten Problem.
RibaldEddie
4
Die Antwort ist allgemein genug, um dieses konkrete Problem anzugehen. Und die Frage ist nicht spezifisch genug, um eine konkrete Quittung zu erhalten: Wie Sie sehen, werden in einer Frage keine Domain-Details erwähnt. Auch Klassennamen werden nicht angegeben (A und B zählen nicht).
Zapadlo
7
"Sollte eine Stapelüberlaufantwort so allgemein wie möglich oder so spezifisch wie möglich sein?"
Lyndon White
@LyndonWhite sollte eine Stackoverflow-Frage so allgemein wie möglich (zu allgemein!) Oder so spezifisch wie möglich (wie auch immer dieser Fehler heißt!) Sein? Ha.
4

Unter der Annahme, dass ich den Inhalt Ihrer Frage richtig verstanden habe, sehe ich dies in der Tat als Entwurfsfrage für zentrale Systemfunktionen, die mehr ist als eine Frage nach generischen vs. spezifischen Lösungen.

Und wenn es um zentrale Systemfunktionen und -fähigkeiten geht, sind die zuverlässigsten diejenigen, die nicht vorhanden sind. Es lohnt sich, auf der Seite des Minimalismus zu irren, vor allem angesichts der Tatsache, dass es im Allgemeinen einfacher ist, Funktionalität zentral hinzuzufügen, lange gewünscht, als problematische Funktionalität zu entfernen, lange unerwünscht, mit zahlreichen Abhängigkeiten, weil es die Arbeit mit dem System so viel schwieriger gemacht hat als es sein muss, während endlose Designfragen mit jedem neuen Feature aufgeworfen werden.

In der Tat würde ich es vermeiden, dies als Typersatz von A nach B zu betrachten, da ich nicht genau ahne, ob dies in Zukunft häufig benötigt wird, und es stattdessen nur als einen Weg suchen, den Zustand von A zu transformieren. Stellen Sie zum Beispiel einige Felder in A so ein, dass sie sich verwandeln und dem Benutzer wie B erscheinen, ohne sich tatsächlich in einen anderen 'Typ' von Dingen zu verwandeln. Sie können A möglicherweise privat zum Laden von B machen, indem Sie Kompositions- und Aufruffunktionen in B verwenden, wenn der Zustand von A vorliegt soll angeben, dass B nachgebildet werden soll, um die Implementierung nach Möglichkeit zu vereinfachen. Das sollte eine sehr einfache und minimalinvasive Lösung sein.

Trotzdem würde ich, wie viele andere, vorschlagen, auf der Seite der Vermeidung von generischen Lösungen zu irren, aber mehr noch, weil ich davon ausgehe, dass dies im Hinblick auf das Hinzufügen einer sehr gewagten Funktion zum zentralen System eine Rolle spielt schlagen vor, auf der Seite des Auslassens zu irren, vor allem für den Moment.


quelle
"Sie vermissen alle Fehler, die Sie nicht codieren." (Aus dem Basketball-Poster: Sie verpassen alle Aufnahmen, die Sie nicht machen)
3

Es ist schwierig, eine generische Antwort auf dieses spezielle Problem zu geben ;-)

Je allgemeiner es ist, desto mehr Zeit gewinnen Sie für zukünftige Änderungen. Aus diesem Grund verwenden beispielsweise viele Spielprogramme das Entitätskomponentenmuster, anstatt ein sehr ausgeklügeltes, aber starres Typensystem der Charaktere und Objekte im Spiel zu erstellen.

Auf der anderen Seite erfordert das Erstellen von etwas Generischem einen Zeit- und Arbeitsaufwand im Voraus, der viel höher ist als für etwas sehr Spezifisches. Dies birgt das Risiko eines Überengineerings und kann sogar dazu führen, dass Sie sich in potenziellen zukünftigen Anforderungen verlieren.

Es lohnt sich immer zu prüfen, ob es eine natürliche Verallgemeinerung gibt, die Ihnen einen Durchbruch verschafft. Letztendlich ist es jedoch eine Frage des Gleichgewichts zwischen dem Aufwand, den Sie jetzt und in Zukunft aufwenden können.

Christophe
quelle
3
  1. Hybrid. Dies muss keine Entweder-Oder-Frage sein. Sie können die API für allgemeine Typkonvertierungen entwerfen und gleichzeitig nur die spezifische Konvertierung implementieren, die Sie gerade benötigen. (Vergewissern Sie sich nur, dass ein Aufruf Ihrer allgemeinen API mit einer nicht unterstützten Konvertierung mit dem Fehlerstatus "Nicht unterstützt" fehlschlägt.)

  2. Testen. Für die A-> B-Konvertierung muss ich einen (oder eine kleine Anzahl) Tests schreiben. Für eine generische x-> y-Konvertierung muss ich möglicherweise eine ganze Matrix von Tests schreiben. Das ist erheblich mehr Arbeit, auch wenn alle Konvertierungen eine einzige generische Implementierung gemeinsam haben.

    Wenn sich andererseits herausstellt, dass es eine generische Möglichkeit gibt, alle möglichen Konvertierungen zu testen, ist die Arbeit nicht viel länger und ich bin möglicherweise geneigt, früher zu einer generischen Lösung zu wechseln.

  3. Kupplung. Ein Konverter von A nach B muss möglicherweise unbedingt Implementierungsdetails zu A und B kennen (enge Kopplung). Wenn sich A's und B's noch weiterentwickeln, bedeutet dies, dass ich den Konverter (und seine Tests) möglicherweise immer wieder überarbeiten muss, was zum Kotzen ist, aber zumindest beschränkt auf A's und B's.

    Wenn ich eine generische Lösung gewählt hätte, die Zugriff auf die Details aller Arten benötigt, müsste ich möglicherweise den generischen Konverter (und eine Reihe von Tests) weiter optimieren, auch wenn sich C und D weiterentwickeln, was mich sogar verlangsamen kann obwohl noch niemand in ein C oder ein D konvertieren muss .

    Wenn sowohl die generischen als auch die spezifischen Konvertierungen auf eine Weise implementiert werden können, die nur lose mit den Details der Typen gekoppelt ist, würde ich mir darüber keine Sorgen machen. Wenn einer davon lose gekoppelt sein kann, der andere jedoch eine enge Kopplung erfordert, ist dies ein starkes Argument für die lose gekoppelte Herangehensweise direkt vor der Tür.

Adrian McCarthy
quelle
2
Testen ist ein guter Punkt. Es ist ein großer Vorteil, wenn Sie die generischen Lösungen auf der Grundlage von Konzepten aus der Kategorietheorie entwerfen , denn dann erhalten Sie häufig freie Theoreme, die beweisen, dass die einzig mögliche Implementierung für eine Signatur (die der Typprüfer des Compilers akzeptiert) die richtige ist oder zumindest Wenn der Algorithmus für einen bestimmten Typ funktioniert, muss er auch für alle anderen Typen funktionieren.
links um ca. 30.11.17 Uhr
Ich würde vermuten, dass es einen domänenspezifischen Grund gibt, warum nur eine Typkonvertierung angefordert wurde, was bedeutet, dass die meisten Typkonvertierungen ungültige Aktionen in der Problemdomäne sind und aus diesem Grund von der App erst dann unterstützt werden sollten, wenn sie durchgeführt werden sind offiziell erlaubt. Diese Antwort kommt dieser Argumentation am nächsten.
Ralf Kleberhoff
3

Sollte die Lösung so allgemein wie möglich oder so spezifisch wie möglich sein?

Das ist keine beantwortbare Frage.

Das Beste, was Sie vernünftigerweise bekommen können, sind ein paar Heuristiken, um zu entscheiden, wie allgemein oder spezifisch eine bestimmte Lösung sein soll. Wenn Sie wie folgt vorgehen, ist normalerweise die Annäherung erster Ordnung korrekt (oder gut genug). Wenn dies nicht der Fall ist, ist der Grund wahrscheinlich zu domänenspezifisch, um hier ausführlich behandelt zu werden.

  1. Näherung erster Ordnung: Die übliche YAGNI-Dreierregel, wie sie von Daniel Pryden, Doc Brown et al .

    Dies ist eine allgemein nützliche Heuristik, da sie wahrscheinlich das Beste ist, was Sie tun können, und nicht von der Domäne und anderen Variablen abhängt.

    Die anfängliche Annahme lautet also: Wir machen das Spezifischste.

  2. Näherung zweiter Ordnung: Basierend auf Ihrem Expertenwissen über die Lösungsdomäne , sagen Sie

    Die "generische" Lösung erfordert in diesem Fall weniger Arbeit als die "spezifische" Lösung

    Daher könnten wir YAGNI als Empfehlung interpretieren, unnötige Arbeit zu vermeiden , anstatt unnötige Allgemeingültigkeit zu vermeiden. Wir könnten also unsere ursprüngliche Annahme ändern und stattdessen das Einfachste tun.

    Wenn Ihre Lösungsdomänenkenntnisse jedoch darauf hinweisen, dass die einfachste Lösung wahrscheinlich viele Fehler aufdeckt, nur schwer angemessen zu testen ist oder ein anderes Problem verursacht, ist die einfachere Codierung nicht unbedingt ein ausreichender Grund, unsere zu ändern ursprüngliche Wahl.

  3. Annäherung dritter Ordnung: Schlägt Ihr Problemdomänenwissen vor, dass die einfachste Lösung tatsächlich korrekt ist, oder lassen Sie viele Übergänge, von denen Sie wissen, dass sie bedeutungslos oder falsch sind, zu?

    Wenn die einfache, aber generische Lösung problematisch aussieht oder Sie sich nicht sicher sind, ob Sie diese Risiken einschätzen können, ist es wahrscheinlich besser, die zusätzliche Arbeit zu erledigen und bei Ihrer anfänglichen Einschätzung zu bleiben.

  4. Näherung vierter Ordnung: Verändert Ihre Kenntnis des Kundenverhaltens oder der Beziehung dieser Funktion zu anderen oder der Prioritäten des Projektmanagements oder sonstige nicht strikt technische Überlegungen Ihre aktuelle Arbeitsentscheidung?

Nutzlos
quelle
2

Dies ist keine einfache Frage, die mit einer einfachen Antwort beantwortet werden kann. Viele Antworten haben Heuristiken gegeben, die auf einer Regel von 3 oder ähnlichem basieren. Über solche Faustregeln hinauszugehen ist schwierig.

Um Ihre Frage wirklich wirklich zu beantworten, müssen Sie berücksichtigen, dass Ihr Job höchstwahrscheinlich nichts implementiert, was A-> B ändert. Wenn Sie ein Auftragnehmer sind, ist dies vielleicht die Voraussetzung, aber wenn Sie ein Mitarbeiter sind, wurden Sie beauftragt, viele, viele kleinere Aufgaben für das Unternehmen zu erledigen. Das Ändern von A-> B ist nur eine dieser Aufgaben. Ihr Unternehmen wird sich auch darum kümmern, wie gut die zukünftigen Änderungen vorgenommen werden können, auch wenn dies nicht in der Anfrage angegeben ist. Um "TheBestImplementation (tm)" zu finden, müssen Sie das Gesamtbild dessen betrachten, was Sie wirklich tun sollen, und dann das verwenden, um die kleine Aufforderung zu interpretieren, die Sie erhalten haben, A-> B zu ändern.

Wenn Sie ein Programmierer mit niedrigem Einstiegsniveau sind, ist es oft ratsam, genau das zu tun, was Ihnen gesagt wurde. Wenn Sie als Softwarearchitekt mit 15 Jahren Erfahrung angestellt wurden, ist es normalerweise ratsam, über große Zusammenhänge nachzudenken. Jeder tatsächliche Job wird irgendwo zwischen "genau das tun, was die enge Aufgabe ist" und "über das große Ganze nachdenken" liegen. Sie werden herausfinden, wo Ihr Job in dieses Spektrum passt, wenn Sie genug mit Menschen sprechen und genug für sie arbeiten.

Ich kann einige konkrete Beispiele nennen, bei denen Ihre Frage eine endgültige Antwort auf der Grundlage des Kontexts hat. Stellen Sie sich den Fall vor, in dem Sie sicherheitskritische Software schreiben. Dies bedeutet, dass Sie ein Testteam haben, das bereit ist, sicherzustellen, dass das Produkt die versprochenen Leistungen erbringt. Einige dieser Testteams müssen jeden einzelnen möglichen Pfad durch den Code testen. Wenn Sie mit ihnen sprechen, werden Sie möglicherweise feststellen, dass Sie, wenn Sie das Verhalten verallgemeinern, die Testkosten um 30.000 US-Dollar erhöhen, da sie all diese zusätzlichen Pfade testen müssen. Fügen Sie in diesem Fall keine verallgemeinerten Funktionen hinzu, selbst wenn Sie die Arbeit deswegen sieben- oder achtmal duplizieren müssen. Sparen Sie dem Unternehmen Geld und tun Sie genau das, was in der Anfrage angegeben wurde.

Stellen Sie sich andererseits vor, Sie erstellen eine API, mit der Kunden auf Daten in einem von Ihrem Unternehmen erstellten Datenbankprogramm zugreifen können. Ein Kunde bittet darum, Änderungen A-> B zuzulassen. APIs haben normalerweise einen Aspekt von goldenen Handschellen: Sobald Sie einer API Funktionen hinzufügen, sollten Sie diese Funktionen normalerweise nicht mehr entfernen (bis zur nächsten Hauptversionsnummer). Viele Ihrer Kunden sind möglicherweise nicht bereit, die Kosten für das Upgrade auf die nächste Hauptversionsnummer zu bezahlen, sodass Sie möglicherweise lange Zeit nicht mit der von Ihnen gewählten Lösung zufrieden sind. In diesem Fall empfehle ich dringend , die generische Lösung von Anfang an zu erstellen. Sie möchten wirklich keine schlechte API mit einmaligem Verhalten entwickeln.

Cort Ammon
quelle
1

Hmm ... es gibt nicht viel Kontext, um eine Antwort zu finden ... und ich spreche von früheren Antworten: "Es kommt darauf an".

In gewissem Sinne muss man auf seine Erfahrung zurückgreifen. Wenn nicht deins, dann jemand älterer in der Domäne. Sie könnten darüber streiten, was die Annahmekriterien angeben. Wenn es sich um etwas in der Art von "Der Benutzer sollte in der Lage sein, den Typ von" A "nach" B "zu ändern, im Gegensatz zu" Der Benutzer sollte in der Lage sein, den Typ von seinem aktuellen Wert in einen zulässigen alternativen Wert zu ändern ".

Häufig werden Akzeptanzkriterien interpretiert, aber gute QS-Mitarbeiter können der jeweiligen Aufgabe entsprechende Kriterien aufstellen und so den Interpretationsbedarf minimieren.

Gibt es Domain-Beschränkungen, die keinen Wechsel von "A" nach "C" oder einer anderen Option zulassen, sondern nur von "A" nach "B"? Oder ist dies nur eine eng spezifizierte Anforderung, die nicht "vorausschauend" ist?

Wenn der allgemeine Fall schwieriger wäre, würde ich fragen, bevor ich mit der Arbeit beginne, aber wenn ich in Ihrem Fall "vorhersagen" könnte, dass in Zukunft andere "Typ" -Änderungsanforderungen kommen, wäre ich versucht: a) Schreiben Sie etwas, das für den allgemeinen Fall wiederverwendbar ist, und b) wickeln Sie es in eine Bedingung ein, die vorerst nur A -> B zulässt.

Einfach genug, um den aktuellen Fall in automatisierten Tests zu überprüfen, und einfach genug, um später andere Optionen zu eröffnen, wenn / wenn unterschiedliche Anwendungsfälle auftreten.

railsdog
quelle
1

Eine Richtlinie, die ich vor einiger Zeit aufgestellt habe, lautet für mich: "Schreiben Sie für hypothetische Anforderungen nur hypothetischen Code." Das heißt, wenn Sie zusätzliche Anforderungen antizipieren, sollten Sie ein wenig darüber nachdenken, wie diese implementiert werden sollen, und Ihren aktuellen Code so strukturieren, dass dies nicht blockiert wird.

Aber schreiben Sie jetzt keinen eigentlichen Code dafür - denken Sie nur ein wenig darüber nach, was Sie tun würden. Andernfalls werden Sie die Dinge normalerweise unnötig komplex machen und sich wahrscheinlich später darüber ärgern, wenn tatsächliche Anforderungen auftreten, die von Ihren Erwartungen abweichen.

4 Ihr Beispiel: Wenn Sie alle Verwendungszwecke der convert-Methode im Griff haben, können Sie sie vorerst einfach als convertAToB bezeichnen und planen, eine Umbenennungsmethode in der IDE zu verwenden, um sie umzubenennen, wenn Sie später allgemeinere Funktionen benötigen. Wenn die Konvertierungsmethode jedoch Teil einer öffentlichen API ist, kann dies ganz anders sein: Wenn sie spezifisch ist, wird die Generalisierung später blockiert, da es in diesem Fall schwierig ist, die Dinge umzubenennen.

Hans-Peter Störr
quelle
0

Ich höre immer wieder, dass ein guter Entwickler versuchen sollte, die Änderung zu antizipieren und das System so zu gestalten, dass es in Zukunft problemlos erweitert werden kann.

Im Prinzip ja. Dies führt jedoch nicht unbedingt zu generischen Lösungen.

Für mich gibt es zwei Arten von Themen in der Softwareentwicklung, zu denen Sie zukünftige Änderungen antizipieren sollten:

  • Bibliotheken, die für Dritte bestimmt sind, und
  • Gesamtsoftwarearchitektur.

Der erste Fall wird gelöst, indem Sie Ihre Kohäsion / Kopplung, Abhängigkeitsinjektion oder was auch immer beobachten. Der zweite Fall betrifft eine abstraktere Ebene, beispielsweise die Auswahl einer serviceorientierten Architektur anstelle eines großen monolothischen Code-Blocks für eine große Anwendung.

In Ihrem Fall fragen Sie nach einer bestimmten Lösung für ein bestimmtes Problem, die keinerlei vorhersehbare Auswirkungen auf die Zukunft hat. In diesem Fall sind YAGNI und DRY gute Mottos für:

  • YAGNI (Sie ist nicht gonna es brauchen) sagt Ihnen , die absolut minimale, grundlegende Dinge zu implementieren , die Sie brauchen , und verwenden jetzt . Es bedeutet, das Minimum zu implementieren, das Ihre aktuelle Testsuite von Rot zu Grün wechselt, wenn Sie die TDD / BDD / FDD-Stilentwicklung verwenden sollten. Keine einzige Zeile mehr.
  • DRY (nicht selbst wiederholen) bedeutet , wenn Sie über ein ähnliches Problem wieder kommen, dann nehmen Sie einen guten harten Blick auf , ob Sie eine generische Lösung benötigen.

In Kombination mit anderen modernen Methoden (z. B. eine gute Testabdeckung, um sicher umgestalten zu können) bedeutet dies, dass Sie schnell geschriebenen, schlanken, mittleren Code erhalten, der bei Bedarf wächst.

Was klingt nach einer generischen Lösung?

Nein, es hört sich so an, als ob Sie über Programmierumgebungen, Sprachen und Werkzeuge verfügen sollten, mit denen es einfach und unterhaltsam ist, Änderungen vorzunehmen, wenn Sie sie benötigen. Generische Lösungen bieten dies nicht. Sie entkoppeln die Anwendung von der eigentlichen Domäne.

Schauen Sie sich moderne ORMs oder MVC-Frameworks an, zum Beispiel Ruby on Rails; Auf der Anwendungsebene liegt der Schwerpunkt auf nicht generischer Arbeit. Die Rails-Bibliotheken selbst sind offensichtlich fast zu 100% generisch, aber der Domain-Code (um den es in Ihrer Frage geht) sollte in dieser Hinsicht nur minimale Spielereien ausüben.

AnoE
quelle
0

Eine andere Art, über das Problem nachzudenken, ist zu überlegen, was Sinn macht.

Zum Beispiel gab es eine Anwendung, die ich gerade entwickelte und deren Abschnitte fast dasselbe taten, aber inkonsistente Berechtigungsregeln hatten. Als ich diesen Abschnitt überarbeitete, gab es keinen Grund, sie anders zu gestalten, und ich ließ sie alle Berechtigungen auf die gleiche Weise ausführen. Dadurch wurde der Gesamtcode kleiner, einfacher und die Benutzeroberfläche konsistenter.

Als das Management beschloss, anderen Benutzern den Zugriff auf eine Funktion zu ermöglichen, konnten wir dies durch einfaches Ändern einer Markierung tun.

Natürlich ist es sinnvoll, die spezifische Typkonvertierung vorzunehmen. Ist es auch sinnvoll, zusätzliche Typkonvertierungen vorzunehmen?

Denken Sie daran, wenn die allgemeine Lösung schneller zu implementieren ist, ist auch der spezifische Fall einfach. Überprüfen Sie nur, ob es sich um die einzige zulässige Typkonvertierung handelt.

Wenn sich die Anwendung in einem stark regulierten Bereich befindet (eine medizinische oder finanzielle Anwendung), versuchen Sie, mehr Personen in Ihr Design einzubeziehen.

Robert Baron
quelle