it-swarm.com.de

Logik innerhalb von Klasseneigenschaften Setter & Getter

Wir versuchen, einen Fall dagegen zu erstellen, da wir einen Entwickler haben, der Klasseneigenschaften anstelle von Methoden verwendet. Ich denke, das ist eine schlechte Idee. In einigen Fällen greift er auf die Datenschicht im Setter und Getter zu.

hier ist ein Codeausschnitt. Was denken Sie darüber?

private Dictionary<string, List<string>> _activeFilters;
    public Dictionary<string, List<string>> ActiveFilters
    {
        get
        {
            if (_activeFilters == null)
            {
                _activeFilters = new Dictionary<string, List<string>>();
                var qs = HttpContext.Current.Request.QueryString;
                foreach (var widget in SrpDto.Widgets)
                {
                    _activeFilters[widget.SearchParameter] = qs[widget.SearchParameter] != null ? qs[widget.SearchParameter].Split(',').Skip(1).ToList() : new List<string>();
                }
            }

            return _activeFilters;
        }
    }

    private Constants.SearchBarLayout? _searchBarLayout;
    public Constants.SearchBarLayout SearchBarLayout
    {
        get
        {
            if (_searchBarLayout == null)
            {
                var searchBarLayoutType = typeof(Constants.SearchBarLayout);

                // Use the first enum value as the default if no module setting is present
                _searchBarLayout = (Constants.SearchBarLayout)Enum.Parse(searchBarLayoutType,
                    this.ModuleContext.Settings[Constants.SRPSearchBarLayoutKey] as string ?? Enum.GetName(searchBarLayoutType, 0));
            }

            return _searchBarLayout.Value;
        }
    }

    public string SearchBarTemplate
    {
        get
        {
            switch (this.SearchBarLayout)
            {
                case Constants.SearchBarLayout.SimpleSearchBar:
                    return Constants.SRPSimpleSearchTemplateName;
                    break;
                case Constants.SearchBarLayout.FacetedSearchBar:
                    return Constants.SRPFacetSearchTemplateName;
                    break;
                default:
                    return string.Empty;
            }
        }
    }
9
JBeckton

Ich werde sagen, dass dies in einigen Fällen genau das ist, was Sie wollen. Eine Logik in der Eigenschaft get/set zu haben, ist keine Seltenheit, und tatsächlich die Microsoft-Richtlinien für Eigenschaften zeigen, dass eine Methode aufgerufen wird, um ein Steuerelement zu aktivieren, wenn sich ein Wert ändert .

Ich kann mir viele Szenarien vorstellen, in denen Sie beim Aufrufen von get die Werte aus der Datenbank abrufen möchten. In den oben genannten Fällen sind die Eigenschaften schreibgeschützt, da für sie keine festgelegte Methode definiert ist, sodass Sie dies nicht versehentlich tun würden Aktualisieren Sie etwas in Ihrem Code und brechen Sie das Prinzip der geringsten Überraschung.

Auf der anderen Seite, wenn jede Aktion, die die Datenbank aufruft oder eine Logik zum Bestimmen eines Werts enthält, eine Methode sein muss, wie würden Sie die Methode aufrufen? GetActiveFilters()? Das klingt so, als ob es eine Eigenschaft sein sollte.

Auf jeden Fall können Sie einen anderen Codierungsstandard für Ihr eigenes Team festlegen, wenn dies nicht das ist, was Sie bevorzugen. Angesichts der Tatsache, dass Microsoft selbst Möglichkeiten zur Bündelung von Logik in Property Getter und Setter übernimmt und demonstriert, würde ich sagen, gehen Sie dem Kerl leicht. und sag ihm, dass du es lieber nicht so machst.

8
Maurice Reeves

Wenn es keine Logik in Gettern und Setzern gäbe, welche Eigenschaften wären das? SearchBarLayout ist eine einfache Bedingung, und ich denke, es ist in Ordnung. Die anderen könnten umstrittener sein, aber dies ist ein allgemeines Muster:

private T _value;
public T Value {
    get {
        if (_value == null) {
            _value = [initialization logic for the field]
        }
        return _value;
     }
}

Dies bedeutet, dass, wenn jemand diese Klasse instanziiert, aber nie auf Value zugreift, Zeit gespart wurde, indem die Initialisierungslogik übersprungen wurde. Und wenn mehrmals darauf zugegriffen wird, wird es immer noch nur einmal verarbeitet (außer in Fällen, in denen die Initialisierung null zurückgeben könnte). Außerdem wird der Code sequenzieller organisiert, als wenn die Felder/Eigenschaften in einem Abschnitt aufgelistet und XML-dokumentiert und in anderen Methoden initialisiert würden.

Sie müssen entscheiden, ob diese Leistungen tatsächlich gelten. Vielleicht führen sie separate Aufrufe an die Datenschicht durch, die als ein Aufruf effizienter sein könnten, sodass Sie beide Felder gleichzeitig initialisieren sollten? Ich kenne die Details nicht, aber ich persönlich hätte kein Problem damit, Code zu schreiben, wie Sie ihn gepostet haben.

6
nmclean

Ich würde eine breitere Sicht auf das Klassenmodell haben wollen, wenn ich tatsächlich eine vollständige Codeüberprüfung durchführen würde, aber all dies sieht nach sehr offensichtlichen Fällen aus, in denen man definitiv eine Eigenschaft anstelle einer Methode verwenden sollte.

Betrachten wir zuerst ActiveFilters. Wenn ich eine schreibgeschützte Eigenschaft für ein Objekt mit dem Namen ActiveFilters sehe, erwarte ich, dass "Ruft die aktiven Filter für das Objekt ab" das ist, was ich sehen würde, wenn ich in der Dokumentation nachschaue. Das Prinzip der geringsten Überraschung besagt daher, dass, wenn dies das ist, was diese Eigenschaft tut, alles gut ist, sonst kann es nicht sein.

Und es ist.

Es ist zufällig eine auswendig gelernte Eigenschaft; Das heißt, es berechnet den Wert, der beim ersten Aufruf zurückgegeben werden soll, und speichert das Ergebnis für nachfolgende Aufrufe zwischen. Dies ist eine Optimierung für den Fall, dass eine Eigenschaft wahrscheinlich mehrmals aufgerufen wird (andernfalls wird nichts gespart). Dies ist am nützlichsten in Fällen, in denen auf die Eigenschaft manchmal nicht zugegriffen werden kann (da in solchen Fällen der Code nie ausgeführt wird). Auch wenn der Getter der Eigenschaft jedes Mal aufgerufen wird, ist es dennoch hilfreich, die Logik zum Abrufen des Werts in der Nähe des Ortes zu halten, an dem es wird zugegriffen; Unterstützung der Fähigkeit, den Code zu verstehen.

Natürlich weiß der externe Aufrufcode nicht, ob er gespeichert ist, aus einem Feld ohne weitere Logik gelesen wird oder ob er das Ergebnis bei jedem Aufruf berechnet.

Der aufrufende Code weiß auch nicht, ob diese Memoisierung dauerhaft ist oder ob es an anderer Stelle in der Klasse eine Methode oder einen Setter gibt, die entweder _activeFilters Neu füllen oder auf null setzen kann, um eine Neuberechnung zu erzwingen.

Dies ist einer der Hauptgründe für die Verwendung von Eigenschaften, anstatt nur Felder verfügbar zu machen. Wenn sie ein Feld verfügbar gemacht hätten (und sichergestellt hätten, dass es bei der Erstellung korrekt ausgefüllt wurde), wäre es eine bahnbrechende Änderung, später zu so etwas zu wechseln.

Es sieht ziemlich robust aus (ich möchte die Details von SrpDto.Widgets Usw. untersuchen und wissen, warum ein Element übersprungen wird, bevor ich etwas fester als "vernünftig" sagen würde).

Alles in allem ein Lehrbuchfall eines auswendig gelernten Property Getter.

Wenn ich es überhaupt kritisieren würde, wäre es vielleicht besser, als einen Dictionary zurückzugeben, der Lists enthält, der dann geändert werden könnte, einen schreibgeschützten IDictionary oder ReadOnlyCollection oder Kopien der Listen zurückzugeben. Ein anderes alternatives Design wäre, es zu einem indizierten Getter zu machen, der schreibgeschützte Versionen der Liste basierend auf einem Zeichenfolgenschlüssel zurückgibt. Jedoch:

  1. Dies kann eine angemessene Ersparnis für den Entwickler, der es schreibt, die Entwickler, die es verwenden, und den Verarbeitungscomputer sein, insbesondere wenn es sich um eine interne Klasse handelt.
  2. Es könnte absolut perfekt sein; Ich kann nicht wissen, dass es nicht ohne mehr zu sehen ist.
  3. Die Implementierung wäre ohnehin immer noch ähnlich wie oben, jedoch mit einigen zusätzlichen Arbeiten, die für die Erstellung eines schreibgeschützten Wörterbuchs oder die Indizierung erforderlich sind.

(Ich möchte auch, dass der Name Widgets gerechtfertigt ist. Wenn er sich auf ein funktionales UI-Element bezieht, ist es vielleicht in Ordnung, aber manchmal wird er ohne weitere Begründung verwendet als "Oh, ich wollte nur ein dummes Stück davon verwenden." Slang der 1930er Jahre, um dies weniger lesbar zu machen ").

SearchBarLayout ist ein weiterer im Lehrbuch gespeicherter Property Getter. Ich bin mir nicht so sicher über die Namenskonvention, die uns Constants.SearchBarLayout gibt (das wäre wirklich besser eine interne Klasse oder Aufzählung), aber was die Eigenschaft angeht, geht es darum; natürlich sollte es, was wäre es sonst?

SearchBarTemplate ist noch offensichtlicher etwas, das eine Eigenschaft sein sollte; Es ordnet eine Reihe von Werten eines Typs einer Reihe von Werten eines anderen Typs zu. Vielleicht sollte diese Logik woanders sein, aber vielleicht auch nicht (ich kann es auch hier nicht sagen; Hauptsache, ob diese Logik überall dupliziert wird). Dies ist jedoch für die Frage hier nicht relevant. In Bezug auf Getter vs. Methode ist dies ein offensichtlicher Getter.

Die Verwendung eines Schalters, der ein beliebig aussehendes Mapping durchführt, riecht etwas schlecht, da dies oft auf andere Weise besser gemacht werden kann. Jedoch;

  1. Manchmal ist es einfach der beste Weg, dies zu tun.
  2. Wenn es verbessert werden kann, kann es als nicht brechende Änderung an dieser Stelle verbessert werden.

Insgesamt gibt es zwar ein gewisses Maß an Urteilsvermögen in der Frage, ob etwas eine Eigenschaft oder eine Methode sein soll, aber es gibt einige allgemeine Prinzipien:

  1. Wenn wir Klassen und Strukturen als Substantive, Objekte als Instanzen dieser Substantive und Methoden als Verben (und statische Klassen als abstrakte Substantive) betrachten, sollten Eigenschaften Dinge sein, die wir uns vernünftigerweise als Adjektive oder Präpositionen vorstellen können (die eine Beziehung zwischen den beschreiben) Objekt, das wir aufgerufen haben, und das Objekt, das wir erhalten oder gesetzt haben).
  2. Getter sollten im Allgemeinen nicht werfen, weil der Anrufer eine falsche Wahl getroffen hat (Werfen, weil beispielsweise eine Datenbankverbindung unterbrochen wird, ist eine andere Sache). Wenn sie dies tun, sollten sie dies auf eine Weise tun, die aus der Kenntnis der Klasse/Struktur ersichtlich ist und auf die getestet werden kann (z. B. wie default(int?).ValueInvalidOperationException auslöst) oder weil die Eigenschaft indiziert ist und der Fehler sich auf die bezieht Schlüssel.
  3. Getter sollte den Zustand nicht von außen gesehen ändern, sondern nur über den Zustand berichten. (Das Auswendiglernen ändert den internen Zustand, aber von außen gesehen unterscheidet es sich nicht davon, das Ergebnis jedes Mal zu berechnen oder es bereits berechnet zu haben.).
  4. Ein Setter muss auf ungültige Werte setzen, die gesetzt werden. Dies ist ihr Hauptgrund für das Vorhandensein (und nicht nur das Offenlegen von Feldern). Ein weiterer wichtiger Grund ist, dass sie mehr als einen Status gleichzeitig aktualisieren können.
  5. Im Idealfall sollten Getter und Setter ziemlich schnell sein (wie alle oben gezeigten), insbesondere wenn sie über mehrere Anrufe abgeschrieben werden (wiederum wie die ersten beiden der oben genannten). Es gibt keinen semantischen Grund dafür, aber wenn es so aussieht, als wäre es eine Eigenschaft, die tatsächlich eine Methode ist, kann dies eine subtile Warnung sein, dass etwas teuer ist (z. B. DateTime.UtcNow Ruft alle Methoden auf, die das zugrunde liegende Betriebssystem zum Abrufen des aktuellen Betriebssystems hat Zeit wie 'GetSystemTime followed by SystemTimeToFileTime` unter Windows. Es gibt also keinen Grund, der nicht naheliegend wäre, eine Eigenschaft zu erstellen, sondern eine Methode, um mehrere Atomuhren auf der ganzen Welt zu kontaktieren und dann Netzwerkverzögerungen abzuleiten eine gute Schätzung der aktuellen Zeit geben, könnte besser als Methode sein).

Eine andere Sichtweise auf die Frage besteht darin, Eigenschaften als "intelligente Felder" zu betrachten, wie alle Beispiele, die Sie geben.

Ich sehe nicht ein, wie jemand gegen diese Getter protestieren würde. Ich wäre misstrauisch, dass jeder, der dies tat, dazu neigt, Methoden zu verwenden, wenn er wirklich Eigenschaften verwenden sollte.

6
Jon Hanna