In diesem Artikel von Alex Papadimoulis sehen Sie diesen Ausschnitt:
private void attachSupplementalDocuments()
{
if (stateCode == "AZ" || stateCode == "TX") {
//SR008-04X/I are always required in these states
attachDocument("SR008-04X");
attachDocument("SR008-04XI");
}
if (ledgerAmnt >= 500000) {
//Ledger of 500K or more requires AUTHLDG-1A
attachDocument("AUTHLDG-1A");
}
if (coInsuredCount >= 5 && orgStatusCode != "CORP") {
//Non-CORP orgs with 5 or more co-ins require AUTHCNS-1A
attachDocument("AUTHCNS-1A");
}
}
Ich verstehe diesen Artikel wirklich nicht.
Ich zitiere:
Wenn jedes Unternehmen Regel konstant in einiger Konfigurationsdatei gespeichert wurde, wäre das Leben viel [mehr ( sic )] schwierig für jede Aufrechterhaltung der Software: Es würde eine Menge Code - Dateien, die eine gemeinsam genutzte, große Datei (oder das Gegenteil, viele kleine Konfigurationsdateien); Das Bereitstellen von Änderungen an den Geschäftsregeln erfordert keinen neuen Code, sondern das manuelle Ändern der Konfigurationsdateien. und das Debuggen ist viel schwieriger.
Dies ist ein Argument gegen das Vorhandensein der Konstanten-Ganzzahl "500000" in einer Konfigurationsdatei oder der Konstanten "AUTHCNS-1A" und anderer Zeichenfolgen.
Wie kann das eine schlechte Praxis sein?
In diesem Snippet ist "500000" keine Zahl. Es ist zum Beispiel nicht dasselbe wie:
int doubleMe(int a) { return a * 2;}
Dabei ist 2 eine Zahl, die nicht abstrahiert werden muss. Die Verwendung ist offensichtlich und stellt nichts dar, das später wiederverwendet werden könnte.
Im Gegenteil, "500000" ist nicht einfach eine Zahl. Es ist ein signifikanter Wert, der die Idee eines Haltepunkts in der Funktionalität repräsentiert. Diese Nummer kann an mehreren Stellen verwendet werden, sie ist jedoch nicht die Nummer, die Sie verwenden. Es ist die Idee der Grenze, unterhalb derer eine Regel gilt und oberhalb derer eine andere.
Wie bezieht sich auf es aus einer Konfigurationsdatei, oder sogar ein #define
, const
oder was auch immer Ihre Sprache bietet, schlimmer als sein Wert einschließlich? Wenn das Programm oder ein anderer Programmierer später auch diese Grenze benötigt, damit die Software eine andere Wahl trifft, sind Sie fertig (denn wann ?) sie sich ändert, garantiert Ihnen nichts, dass sie sich in beiden Dateien ändert). Das ist eindeutig schlimmer für das Debuggen.
Wenn die Regierung morgen verlangt, dass ab dem 03.05.2050 AUTHLDG-122B anstelle von AUTHLDG-1A hinzugefügt wird, ist diese Zeichenfolgenkonstante keine einfache Zeichenfolgenkonstante. Es ist eine, die eine Idee darstellt; Es ist nur der aktuelle Wert dieser Idee (was "die Sache ist, die Sie hinzufügen, wenn das Hauptbuch über 500k ist").
Lassen Sie mich das klarstellen. Ich sage nicht, dass der Artikel falsch ist; Ich verstehe es einfach nicht. Vielleicht ist es nicht zu gut erklärt (zumindest für mein Denken).
Ich verstehe, dass das Ersetzen jedes möglichen String-Literal- oder Zahlenwerts durch eine Konstante, eine Definitions- oder eine Konfigurationsvariable nicht nur nicht erforderlich ist, sondern auch die Dinge überkompliziert, aber dieses Beispiel scheint nicht in diese Kategorie zu fallen. Woher weißt du, dass du es später nicht mehr brauchst? Oder sonst jemand?
Antworten:
Der Autor warnt vor vorzeitiger Abstraktion.
Die Linie
if (ledgerAmt > 500000)
sieht aus wie die Art von Geschäftsregel, die Sie von großen komplexen Geschäftssystemen erwarten, deren Anforderungen unglaublich komplex, aber präzise und gut dokumentiert sind.Typischerweise sind diese Arten von Anforderungen eher Ausnahmefälle als nützlich wiederverwendbare Logik. Diese Anforderungen werden in der Regel von Geschäftsanalysten und Sachverständigen und nicht von Ingenieuren verwaltet
(Beachten Sie, dass es in diesen Fällen in der Regel vorkommt, dass Entwickler, die in Spezialgebieten arbeiten, nicht über ausreichende Fachkenntnisse verfügen, dass sie die Anforderungen von Business Analysts / Experten besitzen. Zum Schutz würde ich jedoch eine umfassende Kommunikation / Zusammenarbeit zwischen Entwicklern und Domain-Experten erwarten zweideutige oder schlecht geschriebene Anforderungen.)
Wenn Systeme gepflegt werden, deren Anforderungen voll gepackt sind mit Edge-Cases und hochkomplexer Logik, gibt es normalerweise keine Möglichkeit, diese Logik sinnvoll zu abstrahieren oder wartbarer zu machen. Versuche, Abstraktionen zu erstellen, können leicht nach hinten losgehen - was nicht nur zu Zeitverschwendung, sondern auch zu weniger wartbarem Code führt.
Diese Art von Code wird tendenziell durch die Tatsache geschützt, dass der Code selbst wahrscheinlich eine Eins-zu-Eins-Zuordnung zu Anforderungen aufweist. Wenn ein Entwickler weiß, dass die
500000
Zahl in den Anforderungen zweimal vorkommt, weiß er auch, dass sie im Code zweimal vorkommt.500000
Stellen Sie sich das andere (gleichermaßen wahrscheinliche) Szenario vor, bei dem das Anforderungsdokument an mehreren Stellen angezeigt wird, die Sachverständigen jedoch beschließen, nur eines davon zu ändern. Dort besteht ein noch größeres Risiko, dass jemand, der denconst
Wert ändert, nicht merkt , dass der Wert500000
unterschiedliche Bedeutungen hat. Der Entwickler ändert ihn also an der einzigen Stelle, an der er ihn im Code findet, und bricht schließlich etwas, was er selbst tut merkte nicht, dass sie sich verändert hatten.Dieses Szenario tritt häufig in maßgeschneiderter Rechts- / Finanzsoftware auf (z. B. Logik für Versicherungsangebote) - Personen, die solche Dokumente schreiben, sind keine Ingenieure und haben kein Problem beim Kopieren + Einfügen ganzer Teile der Spezifikation, wobei einige Wörter / Zahlen geändert werden, aber das meiste bleibt gleich.
In diesen Szenarien besteht die beste Möglichkeit, mit den Anforderungen für das Kopieren und Einfügen umzugehen, darin, Code für das Kopieren und Einfügen zu schreiben und den Code so ähnlich wie möglich zu gestalten (einschließlich der Hardcodierung aller Daten).
Die Realität derartiger Anforderungen besteht darin, dass sie in der Regel nicht lange kopiert und eingefügt bleiben und sich die Werte manchmal regelmäßig ändern, sich jedoch häufig nicht gleichzeitig ändern. Daher wird versucht, diese Anforderungen zu rationalisieren oder zu abstrahieren oder zu vereinfachen Sie verursachen in jedem Fall mehr Wartungsprobleme als nur die wörtliche Übersetzung von Anforderungen in Code.
quelle
Those requirements are typically owned and maintained by business analysts and subject matter experts, rather than by engineers
Das ist nicht immer eine gute Idee. Manchmal werden durch die Umwandlung dieser Anforderungen in Code Eckfälle aufgedeckt, in denen die Anforderungen entweder nicht genau definiert sind oder so definiert sind, dass sie dem Geschäftsinteresse zuwiderlaufen. Wenn die Business Analysten und Entwickler zusammenarbeiten, um ein gemeinsames Ziel zu erreichen, können viele Probleme vermieden werden.Der Artikel hat einen guten Punkt. Wie kann es eine schlechte Praxis sein, Konstanten in eine Konfigurationsdatei zu extrahieren? Es kann eine schlechte Praxis sein, wenn es den Code unnötig kompliziert. Es ist viel einfacher, einen Wert direkt im Code zu haben, als ihn aus einer Konfigurationsdatei zu lesen, und der geschriebene Code ist einfach zu befolgen.
Ja, dann änderst du den Code. Der Punkt des Artikels ist, dass das Ändern von Code nicht komplizierter ist als das Ändern einer Konfigurationsdatei.
Der in diesem Artikel beschriebene Ansatz lässt sich nicht skalieren, wenn Sie eine komplexere Logik erhalten. Der springende Punkt ist jedoch, dass Sie eine Entscheidung treffen müssen. Manchmal ist die einfachste Lösung einfach die beste.
Dies ist der Punkt des YAGNI-Prinzips. Entwerfen Sie nicht für eine unbekannte Zukunft, die sich als völlig anders herausstellen könnte, sondern entwerfen Sie für die Gegenwart. Sie haben Recht, wenn der Wert 500000 an mehreren Stellen im Programm verwendet wird, sollte er natürlich in eine Konstante extrahiert werden. Dies ist jedoch im fraglichen Code nicht der Fall.
Softcoding ist wirklich eine Frage der Trennung von Bedenken . Die Ihnen bekannten Softcode-Informationen können sich unabhängig von der Kernanwendungslogik ändern . Sie würden niemals eine Verbindungszeichenfolge zu einer Datenbank fest codieren, da Sie wissen, dass sie sich unabhängig von der Anwendungslogik ändern kann und Sie sie für verschiedene Umgebungen unterscheiden müssen. In einer Web-App trennen wir gerne Geschäftslogik von HTML-Vorlagen und Stylesheets, da diese unabhängig voneinander geändert werden können und sogar von verschiedenen Personen geändert werden können.
Im Codebeispiel sind die fest codierten Zeichenfolgen und Zahlen jedoch ein integraler Bestandteil der Anwendungslogik. Es ist denkbar, dass eine Datei ihren Namen aufgrund einer Änderung der Richtlinie ändert, die außerhalb Ihrer Kontrolle liegt. Es ist jedoch ebenso denkbar, dass wir eine neue if-Verzweigungsprüfung für eine andere Bedingung hinzufügen müssen. Das Extrahieren der Dateinamen und -nummern unterbricht in diesem Fall die Kohäsion.
quelle
if
auf einer anderen Variablen basiert! Wenn Sie in der Konfiguration nicht auf die gewünschte Variable zugreifen können, müssen Sie die Software trotzdem ändern.Der Artikel geht weiter auf 'Enterprise Rule Engines' ein, die wahrscheinlich ein besseres Beispiel dafür sind, wogegen er argumentiert.
Die Logik ist, dass Sie auf den Punkt verallgemeinern können, an dem Ihre Konfiguration so kompliziert wird, dass sie eine eigene Programmiersprache enthält.
Beispielsweise könnte der Statuscode für die Dokumentenzuordnung in dem Beispiel in eine Konfigurationsdatei verschoben werden. Aber Sie müssten dann eine komplexe Beziehung zum Ausdruck bringen.
Vielleicht würden Sie auch den Hauptbuchbetrag eingeben?
Bald werden Sie feststellen, dass Sie in einer neuen Sprache programmieren, die Sie erfunden haben, und diesen Code in Konfigurationsdateien speichern, die keine Quellen- oder Änderungskontrolle haben.
Es sollte beachtet werden, dass dieser Artikel aus dem Jahr 2007 stammt, als dies ein gängiger Ansatz war.
Heutzutage würden wir wahrscheinlich das Problem mit der Abhängigkeitsinjektion (DI) lösen . Dh, du hättest eine "fest codierte"
die würdest du durch eine fest codierte oder besser konfigurierbare ersetzen
wenn sich das Gesetz oder die Geschäftsanforderungen geändert haben.
quelle
if
Anweisungen, um für jeden Client unterschiedliche Werte anzugeben? Das klingt nach etwas, das sich in einer Konfigurationsdatei befinden sollte. In der einen oder anderen Art von Datei zu sein, ist kein Grund, die Datei nicht zu kontrollieren, zu verfolgen oder zu sichern. @ewan scheint zu sagen, dass eine DSL-Datei aus irgendeinem Grund nicht als Teil des Projekts gespeichert werden kann, wenn dies auch bei nicht codierten Assets wie Bild- und Tondateien und Dokumentationen der Fall ist .Und das drückt sich aus durch (und ich könnte argumentieren, dass sogar der Kommentar überflüssig ist):
Dies wiederholt nur, was der Code tut:
Beachten Sie, dass der Autor davon ausgeht, dass die Bedeutung von 500000 an diese Regel gebunden ist. Es ist kein Wert, der an anderer Stelle wiederverwendet wird oder werden könnte:
Der Hauptpunkt des Artikels ist meiner Ansicht nach, dass eine Zahl manchmal nur eine Zahl ist: Sie hat keine zusätzliche Bedeutung, die von der im Code enthaltenen Bedeutung abweicht, und wird wahrscheinlich nicht anderswo verwendet. Daher ist es im besten Fall unnötig, umständlich zusammenzufassen, was der Code (jetzt) in einem Variablennamen tut, um hartcodierte Werte zu vermeiden.
quelle
LEDGER_AMOUNT_REQUIRING_AUTHLDG1A
, würden Sie den Kommentar nicht mehr in den Code schreiben. Kommentare werden von Programmierern nicht gut gepflegt. Sollte sich der Betrag jemals ändern, würden derif
Zustand und der Kommentar nicht mehr synchron sein. Im Gegenteil, die KonstanteLEDGER_AMOUNT_REQUIRING_AUTHLDG1A
gerät nie aus dem Takt und erklärt ihren Zweck ohne den unnötigen Kommentar.attachDocument("AUTHLDG-2B");
Zeile hinzufügt , den Konstantennamen wahrscheinlich nicht gleichzeitig aktualisieren kann , wenn die Geschäftsregel auf "Ledger von 500 KB oder mehr AUTHLDG-1A und AUTHLDG-2B" geändert wird. In diesem Fall ist der Code meiner Meinung nach ziemlich klar und enthält weder einen Kommentar noch eine Erklärungsvariable. (Obwohl es möglicherweise sinnvoll ist, den entsprechenden Abschnitt des Dokuments mit den Geschäftsanforderungen überLEDGER_AMOUNT_REQUIRING_ADDITIONAL_DOCUMENTS
(was ich wahrscheinlich an erster Stelle hätte tun sollen). Ich habe auch die Angewohnheit, die Geschäftsanforderungs-IDs in die Git-Commit-Nachrichten und nicht in den Quellcode einzufügen.attachIfNecessary()
Methode zu einer Aufzählungsinstanz machen und einfach alle durchlaufen.Die anderen Antworten sind richtig und nachdenklich. Aber hier ist meine kurz und bündig Antwort.
Wenn die Regeln und Sonderwerte an einer Stelle im Code erscheinen und sich zur Laufzeit nicht ändern, führen Sie den Hardcode wie in der Frage gezeigt aus.
Wenn die Regeln oder Sonderwerte an mehreren Stellen im Code vorkommen und sich zur Laufzeit nicht ändern, wird Softcode verwendet. Durch Softcodierung für eine Regel kann eine bestimmte Klasse / Methode definiert oder das Builder-Muster verwendet werden . Bei Werten kann Softcodierung bedeuten, eine einzelne Konstante oder Aufzählung für den Wert zu definieren, der im gesamten Code verwendet werden soll.
Wenn sich die Regeln oder Sonderwerte zur Laufzeit ändern, müssen Sie sie externalisieren. In der Regel werden dazu Werte in einer Datenbank aktualisiert. Oder aktualisieren Sie die Werte im Speicher manuell, indem Sie Daten eingeben. Dazu werden auch Werte in einer Textdatei (XML, JSON, Nur-Text, was auch immer) gespeichert, die wiederholt auf Änderungen des Datums und der Uhrzeit der Datei überprüft wird.
quelle
Dies ist die Falle, in die wir geraten , wenn wir ein Spielzeugproblem verwenden und dann nur Strawman- Lösungen vorschlagen , wenn wir versuchen, ein echtes Problem zu veranschaulichen.
In dem angegebenen Beispiel ist es nicht von Bedeutung, ob die angegebenen Werte als Inline-Werte fest codiert oder als Konstanten definiert sind.
Es ist der umgebende Code, der das Beispiel zu einem Wartungs- und Codierungsschrecken machen würde. Wenn es ist kein umgebender Code, dann ist das Snippet in Ordnung, zumindest in einem Umfeld ständigen Refactoring. In einer Umgebung, in der Refactoring in der Regel nicht stattfindet, sind die Betreuer dieses Codes aus Gründen, die in Kürze offensichtlich werden, bereits tot.
Sehen Sie, wenn es Code gibt, der ihn umgibt, dann passieren eindeutig schlechte Dinge.
Das erste Schlimme ist, dass der Wert 50000 irgendwo für einen anderen Wert verwendet wird, zum Beispiel für den Hauptbuchbetrag, über den sich der Steuersatz in einigen Staaten ändert zwei Instanzen von 50000 im Code, unabhängig davon, ob es sich um die gleichen 50.000 oder um völlig unabhängige 50.000 handelt. Und sollten Sie auch nach 49999 und 50001 suchen, falls jemand diese auch als Konstanten verwendet hat? Dies ist kein Aufruf, diese Variablen in eine Konfigurationsdatei eines separaten Dienstes zu kopieren. Es ist jedoch offensichtlich auch falsch, sie inline zu codieren. Stattdessen sollten sie Konstanten sein, die in der Klasse oder Datei, in der sie verwendet werden, definiert sind und einen Gültigkeitsbereich haben. Wenn die beiden Instanzen von 50k dieselbe Konstante verwenden, stellen sie wahrscheinlich dieselbe gesetzliche Einschränkung dar. wenn nicht, tun sie es wahrscheinlich nicht; und so oder so werden sie einen Namen haben,
Die Dateinamen werden an die Funktion attachDocument () übergeben, die Basisdateinamen als Zeichenfolge ohne Pfad oder Erweiterung akzeptiert. Die Dateinamen sind im Wesentlichen Fremdschlüssel für ein Dateisystem oder eine Datenbank oder für jeden Ort, an dem attachDocument () die Dateien abruft. Aber die Zeichenketten sagen nichts darüber aus - wie viele Dateien gibt es? Welche Dateitypen sind das? Woher wissen Sie bei der Erschließung eines neuen Marktes, ob Sie diese Funktion aktualisieren müssen? An welche Arten von Dingen können sie gebunden werden? Der Betreuer bleibt völlig im Dunkeln, und alles, was er hat, ist eine Zeichenfolge, die möglicherweise mehrmals im Code vorkommt und jedes Mal unterschiedliche Bedeutungen hat. An einer Stelle ist "SR008-04X" ein Cheat-Code. In einer anderen ist es ein Befehl, vier SR008-Trägerraketen zu bestellen. Hier ist es ist ein Dateiname? Sind diese verwandt? Jemand hat diese Funktion geändert, um eine andere Datei, "CLIENT", zu erwähnen. Dann wurde Ihnen, dem armen Betreuer, mitgeteilt, dass die "CLIENT" -Datei in "CUSTOMER" umbenannt werden muss. Aber der String "CLIENT" kommt 937 mal im Code vor ... wo fängst du überhaupt an zu suchen?
Das Spielzeugproblem ist, dass die Werte alle ungewöhnlich sind und nach vernünftigem Ermessen garantiert im Code eindeutig sind. Nicht "1" oder "10", sondern "50.000". Nicht "client" oder "report", sondern "SR008-04X".
Der Strawman ist, dass die einzige andere Möglichkeit, das Problem der undurchdringlichen undurchsichtigen Konstanten zu lösen, darin besteht, sie in die Konfigurationsdatei eines nicht verwandten Dienstes zu unterteilen.
Zusammen können Sie diese beiden Irrtümer verwenden, um zu beweisen, dass jedes Argument wahr ist.
quelle
if (...) { attachDocument(...); }
.Darin gibt es mehrere Probleme.
Eine Frage ist, ob eine Regel-Engine erstellt werden soll, damit alle Regeln außerhalb des Programms selbst leicht konfiguriert werden können. In ähnlichen Fällen lautet die Antwort meistens nein. Die Regeln ändern sich auf seltsame Weise, die schwer vorherzusagen sind. Dies bedeutet, dass die Regelengine bei jeder Änderung erweitert werden muss.
Ein weiteres Problem ist der Umgang mit diesen Regeln und deren Änderungen in Ihrer Versionskontrolle. Die beste Lösung besteht darin, die Regeln für jede Regel in eine Klasse aufzuteilen.
Dadurch hat jede Regel ihre eigene Gültigkeit, einige Regeln ändern sich jedes Jahr, andere hängen davon ab, wann eine Genehmigung erteilt oder eine Rechnung ausgestellt wurde. Die Regel selbst, die die Prüfung enthält, für welche Version sie gelten muss.
Da die Konstante auch privat ist, kann sie an keiner anderen Stelle im Code missbraucht werden.
Dann haben Sie eine Liste aller Regeln und wenden Sie die Liste an.
Ein weiteres Problem ist der Umgang mit Konstanten. 500000 mag unauffällig aussehen, aber es muss sehr sorgfältig darauf geachtet werden, dass es richtig konvertiert wird. Wenn eine Gleitkomma-Arithmetik angewendet wird, wird diese möglicherweise in 500.000.00001 konvertiert, sodass ein Vergleich mit 500.000.00000 möglicherweise fehlschlägt. Oder noch schlimmer, 500000 funktioniert immer wie vorgesehen, aber 565000 schlägt beim Konvertieren irgendwie fehl. Stellen Sie sicher, dass die Konvertierung explizit ist und nicht vom Compiler erraten wurde. Dies geschieht häufig durch Konvertieren in eine BigInteger- oder BigDecimal-Variable, bevor sie verwendet wird.
quelle
Obwohl dies in der Frage nicht direkt erwähnt wird, möchte ich darauf hinweisen, dass es nicht wichtig ist, die Geschäftslogik im Code zu verbergen .
Code wie das obige Beispiel, der extern festgelegte Geschäftsanforderungen codiert, sollte sich in einem bestimmten Teil des Quellbaums befinden , der möglicherweise benannt
businesslogic
oder ähnlich ist, und es sollte darauf geachtet werden, dass er nur die Geschäftsanforderungen so einfach, lesbar und codiert So kurz wie möglich, mit einem Minimum an Boilerplate und klaren und informativen Kommentaren.Es sollte nicht mit "Infrastruktur" -Code gemischt werden, der die für die Ausführung der Geschäftslogik erforderlichen Funktionen implementiert , z. B. die Implementierung der
attachDocument()
Methode im Beispiel oder z. B. Benutzeroberfläche, Protokollierung oder Datenbankcode im Allgemeinen. Während ein Weg , um diese Trennung zu erzwingen ist zu „soft - Code“ alle Business - Logik in einer Konfigurationsdatei, ist dies bei weitem nicht der einzige (oder die beste) Methode.Ein solcher Geschäftslogikcode sollte auch so klar geschrieben sein, dass er einen Sinn ergibt, wenn Sie ihn einem Experten für Geschäftsdomänen ohne Programmierkenntnisse zeigen. Wenn sich die Geschäftsanforderungen ändern, sollte der Code, der sie codiert, mindestens so klar sein, dass selbst ein neuer Programmierer, der nicht mit der Codebasis vertraut ist, die Geschäftslogik leicht lokalisieren, überprüfen und aktualisieren kann, sofern dies angenommen wird Es ist keine qualitativ neue Funktionalität erforderlich.
Im Idealfall wird ein solcher Code auch in einer domänenspezifischen Sprache geschrieben, um die Trennung zwischen Geschäftslogik und der zugrunde liegenden Infrastruktur zu erzwingen. Dies kann jedoch für eine grundlegende interne App unnötig kompliziert sein. Wenn Sie die Software beispielsweise an mehrere Clients verkaufen, für die jeweils eigene Geschäftsregeln erforderlich sind, ist eine einfache domänenspezifische Skriptsprache (z. B. basierend auf einer Lua-Sandbox ) möglicherweise genau das Richtige.
quelle