Frage

Ähnlich Hard-coding-Literale jemals akzeptabel?,, aber ich bin besonders denken "magic strings" hier.

Ein großes Projekt, wir haben eine Tabelle von Konfigurations-Optionen wie diese:

Name         Value
----         -----
FOO_ENABLED  Y
BAR_ENABLED  N
...

(Hunderte von Ihnen).

Die gemeinsame Praxis ist zu fordern, eine generische Funktion zu testen, eine option wie diese:

if (config_options.value('FOO_ENABLED') == 'Y') ...

(Natürlich, diese option kann geprüft werden müssen, in vielen Orten in der system-code.)

Beim hinzufügen einer neuen option, die ich erwäge das hinzufügen einer Funktion zum ausblenden der "magic string" wie folgt aus:

if (config_options.foo_enabled()) ...

Aber, Kolleginnen und Kollegen dachte, ich würde über Bord gegangen und widersprach dies zu tun, lieber die harte Codierung, weil:

  • Das ist, was wir normalerweise tun
  • Es macht es einfacher zu sehen, was passiert, wenn Sie das Debuggen der code

Das Problem ist, ich sehe Ihren Punkt!Realistisch gesehen, werden wir nie benennen Sie die Optionen für jede Grund, so ungefähr der einzige Vorteil, den ich mir denken kann für meine Funktion ist, dass der compiler würde fangen Tippfehler wie fo_enabled(), aber nicht 'FO_ENABLED'.

Was denkst du?Habe ich verpasst, andere Vorteile/Nachteile?

War es hilfreich?

Lösung

if (config_options.isTrue('FOO_ENABLED')) {...
}

Schränken Sie Ihren hart codierte Y Scheck an einen Ort, auch wenn es bedeutet, eine Wrapper-Klasse für Ihre Karte zu schreiben.

if (config_options.isFooEnabled()) {...
}

Könnte in Ordnung scheinen, bis Sie 100 Konfigurationsoptionen und 100 Methoden (so hier Sie ein Urteil über die zukünftige Anwendung Wachstum leisten kann und muss, bevor auf die Implementierung der Entscheidung). Ansonsten ist es besser, eine Klasse von statischen Strings für Parameternamen haben.

if (config_options.isTrue(ConfigKeys.FOO_ENABLED)) {...
}

Andere Tipps

Wenn ich eine Zeichenfolge einmal im Code verwenden, ich generell keine Sorgen darüber machen es irgendwo konstant.

Wenn ich eine Zeichenfolge zweimal im Code verwenden, werde ich betrachtet so dass es eine Konstante ist.

Wenn ich eine Zeichenfolge dreimal im Code verwendet wird, werde ich mit ziemlicher Sicherheit es eine Konstante machen.

Ich begreife die Frage ist alt, aber es kam bis auf meine Marge.

AFAIC, das Problem hier ist nicht genau identifiziert, die entweder in der Frage oder den Antworten.Vergessen Sie 'harcoding strings" oder nicht, für einen moment.

  1. Die Datenbank hat eine Referenz-Tabelle, die config_options.Die PK ist eine Zeichenfolge.

  2. Es gibt zwei Arten von PKs:

    • Sinnvolle Bezeichner, dass der Benutzer (und Entwickler) sehen und zu verwenden.Diese PKs werden soll stabil sein, können Sie sich darauf verlassen.

    • Bedeutungslos Id Spalten, die der Benutzer nie sehen sollten, ist, dass die Entwickler haben sich bewusst zu sein, und code.Diese kann nicht verlassen werden.

  3. Es ist gewöhnlich, normal, code zu schreiben, verwenden Sie den absoluten Wert einer sinnvollen PK IF CustomerCode = "IBM" ... oder IF CountryCode = "AUS" etc.

    • die Referenzierung der absolute Wert eines sinnlosen PK ist nicht akzeptabel sind (wegen auto-increment;Lücken geändert wird;Werte ersetzt Großhandel).
      .
  4. Ihre Referenz Tabelle verwendet aussagekräftige PKs.Verweisen auf diese Zeichenketten im code ist unvermeidlich.Verstecken Sie den Wert wird die Wartung erschwert;der code ist nicht mehr literal;Ihre Kollegen Recht.Plus, es ist die zusätzliche redundante Funktion, kaut Zyklen.Wenn es ein Tippfehler in der wörtlichen, werden Sie bald feststellen, dass sich während der Dev-Test, lange bevor UAT.

    • Hunderte von Funktionen, die für Hunderte von literalen ist absurd.Wenn Sie implementieren eine Funktion, dann Normalisieren Sie Ihren code, und geben Sie eine einzelne Funktion, dass kann verwendet werden für jede der Hunderte von literalen.In die Fall, wir werden zurück zu eine Nackte wörtlich, und die Funktion verzichtet werden kann.

    • der Punkt ist, den Versuch zu verbergen, die wörtliche keinen Wert hat.
      .

  5. Es kann nicht so ausgelegt werden, als "hardcoding", das ist etwas ganz anderes.Ich denke, das ist, wo dein Problem ist, identifiziert diese Konstrukte als "hardcoded".Es ist nur verweisen auf eine Aussagekräftige PK wahrsten Sinne des Wortes.

  6. Jetzt aus der Perspektive von code-segment nur, wenn Sie den gleichen Wert ein paar mal, Sie können verbessern Sie den code, indem er den literalen string in einer Variablen, und verwenden Sie dann die variable in den rest des code-Blocks.Sicherlich nicht eine Funktion.Aber das ist eine Effizienz-und gute-Praxis-Problem.Auch das ändert nichts an der Wirkung IF CountryCode = @cc_aus

Nach meiner Erfahrung ist diese Art von Problem ist ein tiefer liegendes Problem maskieren. Ausfall tatsächlichen OOP zu tun und das DRY-Prinzip zu folgen

Auf den Punkt gebracht, fangen die Entscheidung beim Start durch eine geeignete Definition für jede Aktion innerhalb die if Aussagen und dann wegzuwerfen sowohl die config_options und die Laufzeittests.

Details unten.

Die Probe Nutzung war:

if (config_options.value('FOO_ENABLED') == 'Y') ...

, die die offensichtliche Frage aufwirft: „Was in der Auslassungs los?“, Zumal die folgende Anweisung:

  

(Natürlich ist diese gleiche Option muß an vielen Stellen in dem Systemcode überprüft werden.)

Nehmen wir an, dass jeder dieser config_option Werte wirklich zu einem einzigen Problembereich entsprechen nicht (oder Umsetzungsstrategie) Konzept.

Statt dies zu tun (immer wieder, an verschiedenen Stellen in dem Code):

  1. Nehmen Sie eine Zeichenfolge (Tag),
  2. Bestimmen Sie seine entsprechende andere Zeichenfolge (Wert),
  3. Test dieser Wert als boolean-Äquivalent,
  4. an diesem Test Basierend entscheiden, ob eine Aktion auszuführen.

Ich schlage vor, Einkapseln das Konzept einer „konfigurierbare Aktion“.

Lassen Sie uns als Beispiel nehmen (natürlich genauso hypthetical wie FOO_ENABLED ... ;-), dass Ihr Code hat entweder in englischen Einheiten oder metrischen Einheiten zu arbeiten. Wenn METRIC_ENABLED „true“ ist, konvertiert vom Benutzer eingegebenen Daten von metrischen auf Englisch für die interne Berechnung und zurück zu konvertieren, bevor Ergebnisse angezeigt werden.

Definieren Sie eine Schnittstelle:

public interface MetricConverter {
    double toInches(double length);
    double toCentimeters(double length);
    double toPounds(double weight);
    double toKilograms(double weight);
}

, die identifiziert an einem Ort alle das Verhalten mit dem Begriff des METRIC_ENABLED verbunden.

Dann schreiben Sie konkrete Implementierungen aller Möglichkeiten, diese Verhaltensweisen durchgeführt werden:

public class NullConv implements MetricConverter {
    double toInches(double length) {return length;}
    double toCentimeters(double length) {return length;}
    double toPounds(double weight)  {return weight;}
    double toKilograms(double weight)  {return weight;}
}

und

// lame implementation, just for illustration!!!!
public class MetricConv implements MetricConverter {
    public static final double LBS_PER_KG = 2.2D;
    public static final double CM_PER_IN = 2.54D
    double toInches(double length) {return length * CM_PER_IN;}
    double toCentimeters(double length) {return length / CM_PER_IN;}
    double toPounds(double weight)  {return weight * LBS_PER_KG;}
    double toKilograms(double weight)  {return weight / LBS_PER_KG;}
}

Beim Start Zeit, statt eine Reihe von config_options Werten laden, initialisieren eine Reihe von konfigurierbaren Aktionen, wie in:

MetricConverter converter = (metricOption()) ? new MetricConv() : new NullConv();

(wobei der Ausdruck metricOption() oben ist ein Stand-in für jede Art einmalige nur überprüfen Sie vornehmen müssen, auch auf den Wert von METRIC_ENABLED suchen; -)

Dann, wo der Code hätte gesagt:

double length = getLengthFromGui();
if (config_options.value('METRIC_ENABLED') == 'Y') {
    length = length / 2.54D;
}
// do some computation to produce result
// ...
if (config_options.value('METRIC_ENABLED') == 'Y') {
    result = result * 2.54D;
}
displayResultingLengthOnGui(result);

umschreiben als:

double length = converter.toInches(getLengthFromGui());
// do some computation to produce result
// ...
displayResultingLengthOnGui(converter.toCentimeters(result));

Da alle Implementierungsdetails im Zusammenhang mit diesem ein Konzept jetzt sauber verpackt werden, werden alle zukünftigen Wartung METRIC_ENABLED Zusammenhang kann an einem Ort durchgeführt werden. Darüber hinaus ist die Laufzeit Kompromiss ein Gewinn; die „Overhead“ von Aufruf eine Methode trivial ist, verglichen mit dem Overhead einen String-Wert von einer Karte holen und String Durchführung # entspricht.

Ich glaube, dass die beiden Gründe, die Sie erwähnt haben, mögliche Rechtschreibfehler in Zeichenfolge, die Ihre Idee kann nicht (obwohl schlank) bis zur Laufzeit und die Möglichkeit erkannt wird von einer Namensänderung rechtfertigen würden.

Hinzu kommt, dass Sie Funktionen eingegeben bekommen können, jetzt scheint es nur speichern booleans, was ist, wenn Sie benötigen einen int zu speichern, eine Zeichenfolge, etc. Ich würde eher verwenden get_foo () mit einem Typ, als get_string ( "FOO ") oder get_int (" foo ").

Ich sollte wirklich Konstanten verwenden und keine hart codierte Literale.

Sie können sagen, dass sie nicht verändert werden, aber man kann nie wissen. Und es ist am besten, es sich zur Gewohnheit zu machen. Um symbolische Konstanten zu verwenden.

Ich denke, es gibt zwei verschiedene Themen hier:

  • Im aktuellen Projekt, die Konvention hartcodierte Strings verwendet, ist bereits gut etabliert, so dass alle Entwickler an dem Projekt arbeiten, sind damit vertraut. Es könnte eine suboptimale Konvention für alle Gründe, die aufgeführt wurden, aber jeder vertraut mit dem Code kann es auch dreht und instinktiv weiß, was der Code tun soll. Ändern Sie den Code, so dass in bestimmten Teilen, verwendet es die „neue“ Funktionalität wird der Code etwas schwieriger zu lesen machen (weil die Leute denken müssen und erinnern, was die neue Konvention der Fall ist) und damit ein wenig schwieriger zu pflegen. Aber ich würde vermuten, dass möglicherweise auf die neue Konvention über das gesamte Projekt zu ändern wäre unerschwinglich teuer, es sei denn Sie schnell Skript die Konvertierung.
  • Auf eine neue Projekt, symbolischen Konstanten ist der Weg IMO, für alle Gründe aufgeführt. Besonders , weil alles, was der Compiler fängt Fehler bei der Kompilierung macht, die sonst von einem Menschen zur Laufzeit gefangen werden wäre eine sehr nützliche Konvention zu etablieren ist.

Ich ziehe es auch eine stark typisierte Konfigurationsklasse, wenn sie durch-aus dem Code verwendet wird. Mit richtig genannten Methoden verlieren Sie keine Lesbarkeit. Wenn Sie Konvertierungen von Zeichenketten in einen anderen Datentyp (dezimal / float / int) tun müssen, müssen Sie den Code nicht wiederholen, der die Konvertierung in mehreren Orten funktioniert und das Ergebnis zwischenspeichern kann, so dass nur die Umwandlung einmal stattfindet. Sie haben schon die Grundlage für diese an Ort und Stelle schon so würde ich nicht denke, es viel, um auf die neue Art, die Dinge zu gewöhnen.

Eine andere Sache zu prüfen, ist Absicht. Wenn Sie an einem Projekt sind die Lokalisierung hart codierte Strings benötigt, kann mehrdeutig sein. Betrachten Sie das folgende:

const string HELLO_WORLD = "Hello world!";
print(HELLO_WORLD);

Die Absicht des Programmierers ist klar. eine Konstante verwendet bedeutet, dass diese Zeichenfolge nicht lokalisiert werden muss. Nun ein Blick auf dieses Beispiel:

print("Hello world!");

Hier sind wir nicht so sicher. Hat der Programmierer wirklich diese Zeichenfolge lokalisiert wird nicht wollen, oder haben über die Lokalisierung der Programmierer vergessen, während er diesen Code schreibt?

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top