6 Stimmen

Der Zugriff auf eine statische Eigenschaft eines Kindes in einer Elternmethode - Designüberlegungen

Ich habe ein ähnliches Problem wie im Beitrag Zugriff auf eine statische Eigenschaft eines Kindes in einer Elternmethode. Die bevorzugte Antwort deutet darauf hin, dass das Design der Klassen fehlerhaft ist und weitere Informationen benötigt werden, um das Problem zu diskutieren.

Hier ist die Situation, über die ich mit Ihnen diskutieren möchte.

Ich möchte einige datentypen für Einheiten implementieren, wie Länge, Masse, Strom, ... Es sollte einen impliziten Cast geben, um die Instanzen aus einem gegebenen String zu erstellen. Zum Beispiel sollte "1,5 m" dasselbe wie "150 cm" ergeben oder "20 in" korrekt behandelt werden.

Um zwischen verschiedenen Einheiten konvertieren zu können, benötige ich spezifische Konvertierungskonstanten für jede Größe. Meine Idee war es, eine abstrakte Basisklasse mit einigen statischen Übersetzungsmethoden zu erstellen. Diese sollten Klassen-spezifische statisch definierte Wörterbücher verwenden, um ihre Arbeit zu erledigen. Schauen Sie sich also das Beispiel an.

public class PhysicalQuantities
{
    protected static Dictionary myConvertableUnits;

    public static double getConversionFactorToSI(String baseUnit_in)
    {
        return myConvertableUnits[baseUnit_in];
    }
}

public class Length : PhysicalQuantities
{
    protected static Dictionary myConvertableUnits = new Dictionary()
    {
        { "in", 0.0254 }, { "ft", 0.3048 }
    };
}

class Program
{
    static void Main(string[] args)
    {
        Length.getConversionFactorToSI("in");
    }
}

Ich denke, dies bietet eine recht intuitive Verwendung und hält den Code kompakt und ziemlich lesbar und erweiterbar. Aber natürlich bin ich auf die gleichen Probleme gestoßen, die im referenzierten Beitrag beschrieben werden.

Jetzt meine Frage: Wie kann ich diese Probleme durch Design vermeiden?

0 Stimmen

Ich frage mich, ob eine Konvertierung, die als f(double) definiert ist, jemals Probleme bereiten wird. Es könnte sein, dass eine Konvertierung etwas anderes erfordert. Func oder Func wären vielleicht besser.

0 Stimmen

Können Sie einfach von statisch zu nicht statisch wechseln - Ich glaube nicht, dass Sie viel verlieren würden

4voto

Marino Šimić Punkte 7256

Ich denke, das könnte mit Generics gelöst werden, um immer noch lesbar auszusehen. Verfeinert gemäß Slaks Vorschlag, um die Registrierung in den statischen Konstruktor zu passen, um sie von Haus aus thread-sicher zu machen.

Also, wenn ich mich nicht irre:

  • thread-sicher (alle Arbeiten am Dictionary im statischen Konstruktor)
  • Syntax immer noch einfach zu verwenden und lesbar SIConversion.GetFactor() (1 Zeichen mehr)
  • Code, der in abgeleiteten Klassen implementiert werden muss, sehr boilerplate register(string,double); (eigentlich kürzer als Ihre Dictionary-Definition)

    interface ISIConversionSubscriber
    {
        void Register(Action regitration);
    }
    
    static class SIConversion where T : ISIConversionSubscriber, new()
    {
    
        private static Dictionary myConvertableUnits = new Dictionary();
    
        static SIConversion() {
            T subscriber = new T();
            subscriber.Register(registrationAction);
        }
    
        public static double GetFactor(string baseUnit)
        {
            return myConvertableUnits[baseUnit];
        }
    
        private static void registrationAction(string baseUnit, double value)
        {
            myConvertableUnits.Add(baseUnit, value);
        }
    
    }
    
    abstract class PhysicalQuantities : ISIConversionSubscriber
    {
        public abstract void Register(Action register);
    }
    
    class Length : PhysicalQuantities
    {
        public override void Register(Action register)
        {
            // für jeden abgeleiteten Typ die typspezifischen Werte in diesem Override registrieren
            register("in", 1);
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(SIConversion.GetFactor("in"));
        }
    }

Ergebnis: 1

Falls Sie sich wundern, warum ich PhysicalQuantities abstrakt gemacht habe: um zu vermeiden, es mit SIConversion.GetFactor() zu verwenden, da wir keine Konvertierung für eine Basisklasse haben. Wahrscheinlich benötigen Sie sowieso keine Instanzen einer Basisklasse wie dieser - es ist keine vollständige Darstellung einer Menge, also wird sie wahrscheinlich nur wiederverwendbare Methoden enthalten.

Ein weiterer Vorschlag wäre, ein Enum für die baseUnit anstelle eines Strings zu verwenden. Da alle nach Typsicherheit streben und über magische Strings schimpfen, ist es wahrscheinlich ein guter Weg zu folgen :))

3voto

Reed Copsey Punkte 536986

Die beste Option hier besteht typischerweise darin, die statische Natur Ihres Designs zu vermeiden und stattdessen mit Instanzen zu arbeiten. Ich habe eine ähnliche Bibliothek entwickelt, aber die Verwendung tendiert eher dazu:

static void Main()
{
    // Da ich mit Instanzen arbeite, neige ich dazu, den tatsächlichen
    // gemessenen Betrag ebenfalls mitzuliefern... Sie könnten dies jedoch auslassen (oder 1 übergeben)
    var len = Length.Create(42, "in"); 
    double conversionFactory = len.ConversionFactorToSI;
}

Ich habe es auf diese Weise entwickelt, damit ich pro Typ ein Wörterbuch statisch definieren konnte, aber eine Factory-Methode verwende, die dies dem Konstruktor der Basisklasse übergibt (der geschützt ist). Dies ermöglicht es der Basisklasse, mit einem Verweis auf das Wörterbuch des spezifischen Typs instanziiert zu werden, was sehr sauber funktioniert.

2voto

n8wrl Punkte 19091

Ich habe festgestellt, dass testgetriebene Entwicklung mich oft auch zu besseren Designs geführt hat. In Ihrem Fall sind die 'Übersetzungsmethoden' ein wichtiger Bestandteil, den Sie unabhängig von ihrem Einsatz in Ihren Klassen testen möchten. Ich würde vorschlagen, diese Logik in einen eigenen Typ zu kapseln.

Dann können Sie sich, wie Reed vorschlägt, auf Instanzen konzentrieren, mit dem Wissen, dass Ihre Übersetzungslogik gut getestet ist. Ihre abstrakte Basisklasse dient dann einfach als gemeinsamer Ursprung, der weiß, wie man den richtigen Übersetzer bekommt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X