Was ist eigentlich „Soft Coding“?

87

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, constoder 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?

K. Gkinis
quelle
21
Spielen Sie das Puzzle: Was wäre ein guter Name für diese Zahlen? Ich denke, Sie werden feststellen, dass entweder der Name überhaupt keinen Wert hinzufügt, oder er beschreibt alles, was der Code bereits beschreibt, und zwar häufig, während Mehrdeutigkeiten hinzugefügt werden ("LedgerLimitForAuthDlg1A"?). Ich fand den Artikel genau deshalb brillant , weil er so relevant ist. Ich habe Systeme gewartet, die beide Ansätze verwendet haben, und ich kann Ihnen sagen, dass Geschäftsregeln im Code enthalten sind. Dadurch sind sie viel einfacher zu verfolgen, zu warten und zu verstehen. Wenn Sie die Konfiguration verwenden, sollten Sie dafür sorgen, dass sie zählt - sie ist viel teurer.
Luaan
2
Die Konfiguration sollte für Dinge reserviert sein, die konfiguriert werden müssen. Wenn die Geschäftsregeln im Allgemeinen nicht konfigurierbar sind, bringt es Ihnen nichts, wenn Sie Teile davon in die Konfiguration aufnehmen.
Biziclop
Für entsprechend fortgeschrittene Sprachen erfolgt die Konfiguration in Form von tatsächlichen Unterprogrammen und nicht in Form von Zeichenfolgen.
Thorbjørn Ravn Andersen

Antworten:

100

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.

Wie kann man aus einer Konfigurationsdatei oder sogar aus einer #define, const oder einer anderen Sprache darauf verweisen, was schlimmer ist, als den Wert einzuschließen? 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 wenn sie sich ändert, garantiert Ihnen nichts, dass sie sich in beiden Dateien ändert). Das ist eindeutig schlimmer für das Debuggen.

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 500000Zahl in den Anforderungen zweimal vorkommt, weiß er auch, dass sie im Code zweimal vorkommt.

500000Stellen 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 den constWert ändert, nicht merkt , dass der Wert 500000unterschiedliche 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.

Ben Cottrell
quelle
28
Eine domänenspezifische Sprache (Domain Specific Language, DSL) kann eine gute Möglichkeit sein, den Code so zu lesen, dass er dem Anforderungsdokument ähnelt.
Ian
13
Ein weiterer Vorteil eines DSL ist, dass es auch schwieriger ist, Anwendungs-, Präsentations- oder Persistenzlogik versehentlich mit den Geschäftsregeln zu mischen.
Erik Eidt
16
Zu denken, dass Ihre Anwendung speziell genug ist, um eine eigene DSL-Verbindung zu gewährleisten, ist in der Regel eine Überheblichkeit.
brian_o
8
Those requirements are typically owned and maintained by business analysts and subject matter experts, rather than by engineersDas 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.
Kasperd
4
@BenCottrell Ich habe nicht vorgeschlagen, die Regeln zu ändern, um das Schreiben der Software zu vereinfachen. Wenn Sie jedoch viele Bedingungen in den Regeln haben, ist es durchaus möglich, dass bei der Definition der Regeln eine gewisse Interaktion zwischen diesen fehlgeschlagen ist. Wenn Sie die Spezifikation jedoch in Code umwandeln, muss der Entwickler feststellen, dass möglicherweise eine Wechselwirkung zwischen diesen Bedingungen besteht. Zu diesem Zeitpunkt ist es möglich, dass der Entwickler feststellt, dass eine strikte Interpretation der Spezifikation zu einem unbeabsichtigten Preis führt, der es dem Kunden ermöglicht, das System zu spielen.
Kasperd
44

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.

Darüber hinaus schreibt die Regierung morgen: "Ab dem 03.05.2050 müssen Sie AUTHLDG-122B anstelle von AUTHLDG-1A hinzufügen."

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.

Woher weißt du, dass du es später nicht mehr brauchst? Oder sonst jemand?

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.

JacquesB
quelle
4
Oft ist das Ändern von Code viel komplizierter als eine Konfigurationsdatei. Möglicherweise benötigen Sie einen Entwickler und einen Build-System / Release-Zyklus für den ersteren, während für den letzteren lediglich eine Nummer in einem Feld in einer benutzerfreundlichen Konfigurations-Benutzeroberfläche geändert werden muss.
OrangeDog
6
@OrangeDog Ja, so sieht es zuerst aus. Aber wenn Sie solche Dinge tun, wird die Konfigurationsoberfläche alles andere als freundlich sein, mit Hunderten von völlig bedeutungslosen Textfeldern, in denen Sie gefragt werden, wer was weiß. Und jetzt müssen Sie die Benutzeroberfläche erstellen und dokumentieren. Wohlgemerkt, das heißt nicht, dass Konfiguration niemals ein guter Weg ist - es gibt Fälle, in denen es absolut die richtige Wahl ist. Aber nicht in einem der Beispiele im Artikel. Und wann hat ein Gesetz zum letzten Mal nur die Anzahl geändert? Das letzte Mal, als sich die Mehrwertsteuerregeln hier änderten, mussten wir sowieso alle Berechnungen wiederholen.
Luaan
2
@OrangeDog: Sie gehen hier davon aus, dass Ihnen die Softwarekonfiguration die erforderlichen Hooks für die Überprüfung liefert, die Sie durchführen müssen. Beachten Sie, wie im OP jeder ifauf 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.
Matthieu M.
2
@OrangeDog Sie schlagen also vor, dass die Logik einer Softwareanwendung ohne einen Entwicklungs- / Qa- / Release-Zyklus und entsprechende Tests erheblich geändert werden sollte ?
NPSF3000
3
@OrangeDog: OK, Sie verwenden YAML zum Konfigurieren der Logik im Beispiel. Da die Logik bedingte Regeln enthält, können Sie diese Bedingungen in YAML darstellen. Herzlichen Glückwunsch, Sie haben Python neu erfunden. Warum schreibst du dann nicht die ganze App in Python?
JacquesB
26

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.

<statecode id="AZ">
    <document id="SR008-04X"/>
    <document id="SR008-04XI"/>
</statecode>

Vielleicht würden Sie auch den Hauptbuchbetrag eingeben?

<statecode id="ALL">
    <document id="AUTHLDG-1A" rule="ledgerAmt >= 50000"/>
</statecode>

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"

InvoiceRules_America2007 : InvoiceRules

die würdest du durch eine fest codierte oder besser konfigurierbare ersetzen

InvoiceRules_America2008 : InvoiceRules

wenn sich das Gesetz oder die Geschäftsanforderungen geändert haben.

Ewan
quelle
4
Vielleicht solltest du "DI" definieren. Und vielleicht ein bisschen mehr erklären.
Basil Bourque
9
Warum sollte sich diese Datei nicht in der Quellcodeverwaltung befinden?
JDługosz
2
Verfügt die codierte Version, wenn sie clientspezifisch ist, über eine Unmenge von ifAnweisungen, 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 .
JDługosz
2
Sie sollten den Wert "50000" aus Ihrem XML-Code wirklich überarbeiten und in einer separaten Konfigurationsdatei ablegen, oder? ... und es soll übrigens 500000 sein.
Wildcard
1
@jdlugosz Das Konzept eines ERE ist, dass Sie das System kaufen und dann für Ihre Bedürfnisse konfigurieren. Vielleicht, weil interne Entwickler mit diesen "flexiblen" Systemen konkurrierten, würden sie versuchen, sie zu emulieren. Die Kontrolle der Konfiguration zu ändern, selbst in Systemen großer Unternehmen wie IBM, war oft ein nachträglicher Gedanke. Das Verkaufsargument war eine schnelle Veränderung
Ewan
17

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 mehr als einer Stelle verwendet werden, aber es ist nicht die Nummer, die Sie verwenden, sondern die Idee der Grenze, unterhalb derer eine Regel gilt und oberhalb derer eine andere.

Und das drückt sich aus durch (und ich könnte argumentieren, dass sogar der Kommentar überflüssig ist):

 if (ledgerAmnt >= 500000) {
    //Ledger of 500K or more requires AUTHLDG-1A
    attachDocument("AUTHLDG-1A");
  }

Dies wiederholt nur, was der Code tut:

LEDGER_AMOUNT_REQUIRING_AUTHLDG1A=500000
if (ledgerAmnt >= LEDGER_AMOUNT_REQUIRING_AUTHLDG1A) {
    //Ledger of 500K or more requires AUTHLDG-1A
    attachDocument("AUTHLDG-1A");
}

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:

Die einzige Änderung der Geschäftsregeln, die diese vorherige Soft-Codierung jemals berücksichtigen könnte, ist eine Änderung des Ledger-Betrags, für die ein Formular AUTHLDG-1A erforderlich war. Jede andere Änderung der Geschäftsregeln würde noch mehr Arbeit erfordern - Konfiguration, Dokumentation, Code usw

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.

Thanos Tintinidis
quelle
2
Wenn Sie die Konstante eingeführt hätten 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 der ifZustand und der Kommentar nicht mehr synchron sein. Im Gegenteil, die Konstante LEDGER_AMOUNT_REQUIRING_AUTHLDG1Agerät nie aus dem Takt und erklärt ihren Zweck ohne den unnötigen Kommentar.
ZeroOne
2
@ZeroOne: Mit der Ausnahme, dass die Person, die die 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 über
Codekommentare anzugeben
@ruakh, OK, dann würde ich die aufzurufende Konstante überarbeiten LEDGER_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.
ZeroOne
1
@ ZeroOne: Aber für AUTHLDG-3C ist der Ledger-Betrag tatsächlich ein Maximum . Und für AUTHLDG-4D hängt der entsprechende Hauptbuchbetrag vom Status ab. (Verstehen Sie das schon? Für diese Art von Code soll Ihr Code die Geschäftsregeln widerspiegeln, nicht irgendeine versuchte Abstraktion der Geschäftsregeln, da es keinen Grund gibt, zu erwarten, dass die Entwicklung der Geschäftsregeln mit den Geschäftsregeln übereinstimmt Abstraktionen, die Sie übernommen haben.)
Ruakh
2
Persönlich habe ich keine Einwände dagegen, die magische Zahl in den Code einzufügen, ich habe Einwände dagegen, den Code so zu strukturieren, dass er diese Kommentare benötigt. Wenn ich es wäre, würde ich jedes Dokument mit einer eigenen attachIfNecessary()Methode zu einer Aufzählungsinstanz machen und einfach alle durchlaufen.
David Moles
8

Die anderen Antworten sind richtig und nachdenklich. Aber hier ist meine kurz und bündig Antwort.

  Rule/value          |      At Runtime, rule/value…
  appears in code:    |   …Is fixed          …Changes
----------------------|------------------------------------
                      |                 |
  Once                |   Hard-code     |   Externalize
                      |                 |   (soft-code)
                      |                 |
                      |------------------------------------
                      |                 |
  More than once      |   Soft-code     |   Externalize
                      |   (internal)    |   (soft-code)
                      |                 |
                      |------------------------------------

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.

Basil Bourque
quelle
1
Ich mag Ihre Antwort, aber ich denke, Sie sollten auch überlegen, ob sich diese bei der Implementierung ändert. Dies ist vor allem dann relevant, wenn es sich um ein Produkt handelt, das in vielen Organisationen eingesetzt wird, in denen beispielsweise unterschiedliche Regeln gelten, ob ein Vorgesetzter eine Rückerstattung über X usw. genehmigen muss.
Bloke Down The Pub,
Stimmte sowohl dieser Antwort als auch dem Kommentar zur Umsetzung zu. Dinge, an denen ich arbeite, werden von vielen Organisationen implementiert, und viele von ihnen haben subtil unterschiedliche Werte, die benötigt werden. Wir neigen dazu, diese "Einstellungen" in einer Datenbank statt in einer Konfigurationsdatei zu speichern, aber das Prinzip ist, dass wir nicht für jedes Unternehmen, das sie implementiert, unterschiedliche Builds unserer Software erstellen möchten (und diese unterschiedlichen Builds bei jedem Upgrade wiederholen). .
RosieC
7

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.

Dewi Morgan
quelle
2
Kein Spielzeugproblem, kein Strohmann. Dies ist etwas , was Sie sehen werden die ganze Zeit in dieser Art von Business - Anwendungen. Es gibt keine "Öffnung in einen neuen Markt", es gibt keine Wiederverwendung der gleichen Zahl (immerhin würde dies sowieso eine andere Bedeutung haben) und auf jeden Fall sagt der Artikel nichts gegen DRY aus - wenn es zwei Abhängigkeiten vom Wert gibt, ist es wird entweder in eine Methode oder in eine Konstante verschoben. Zeigen Sie Beispiele dafür, wie diese Konstanten (von Konfigurationseinstellungen, es ist eigentlich egal) benannt werden sollten und wo sie auf eine Weise gespeichert werden sollten, die zukunftssicher und klarer als der Code ist.
Luaan
4
Das Beispiel funktioniert nicht, da es sich um ein Spielzeugproblem handelt. Der umgebende Code wird immer schrecklich sein, da die Geschäftsregeln, die die Software ausführen muss, schrecklich sind . Versuche, diese fundamentale Herausforderung mit Rules Engines und DSLs zu umgehen, und was nicht oft Programmierer- Zögern ist , weil das Lösen von CS-Problemen mehr Spaß macht als das Lösen komplizierter Steuerformulare. Versuche, "Eleganz" zu erreichen, sind oft ein Kinderspiel, da die ultimative Aufgabe der Software darin besteht, eine komplizierte Katastrophe zu modellieren.
Whatsisname
Die Geschäftsregeln mögen Horror sein, aber das ist an sich keine Entschuldigung für das Schreiben eines solchen mittelmäßigen Verfahrenscodes. (Ich stimme eher mit Papadimoulis überein, dass es einfacher ist, die Regeln im Code zu modellieren und zu pflegen als in der Konfiguration. Ich denke, es sollte besserer Code sein.) Das DRY-Problem, das ich sehe, sind nicht die magischen Zahlen, es ist das wiederholte if (...) { attachDocument(...); }.
David Moles
2

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.

Gebogen
quelle
2

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 businesslogicoder ä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.

Ilmari Karonen
quelle
Genau das habe ich mir gedacht !!! Wie kann ein Domain- / Fachexperte oder Geschäftsbenutzer, wenn die Logik tief im Code vergraben ist, die Werte und die Logik sehen, die verwendet werden, um sicherzustellen, dass sie richtig sind, und das Systemverhalten diagnostizieren? Eine Konfigurationsdatei macht die Einstellungen sichtbar . Es muss Mittel geben, um die Sichtbarkeit von Geschäftsregeln zu fördern - auch wenn dies die Codierung "schwieriger" macht. Ich kann eine dünne Klasse oder eine Reihe von Klassen akzeptieren, die die Arbeit erledigen, ohne andere Bedenken einzubeziehen - solange der Geschäftsbenutzer die Mittel hat, auf sie zuzugreifen und sie zu verstehen.
ErikE