2009-07-01 5 views
5

Ich habe eine Anwendung, die mehrere Typen und Versionen einiger Geräte unterstützt. Es kann sich mit diesen Geräten verbinden und verschiedene Informationen abrufen.Erstellen einer erweiterbaren Eigenschaftenklasse (OOP)

Je nach Gerätetyp habe ich (unter anderem) eine Klasse, die verschiedene Eigenschaften enthalten kann. Einige Eigenschaften sind allen Geräten gemeinsam, einige sind für ein bestimmtes Gerät eindeutig.

Diese Daten werden in XML serialisiert.

Was wäre ein bevorzugter Weg, um eine Klasse zu implementieren, die zukünftige Eigenschaften in zukünftigen Versionen dieser Geräte unterstützt, sowie rückwärtskompatibel zu früheren Anwendungsversionen?

Ich kann von mehreren Möglichkeiten denken, aber ich finde keine von ihnen groß:

  • Verwenden Sie eine Auflistung von Name-Wert-Paare:
    • Profis: gute Abwärtskompatibilität (XML und frühere Versionen meiner App) und Erweiterbarkeit,
    • Nachteile: keine Art Sicherheit, keine Intellisense, erfordert die Implementierung von benutzerdefinierten XML-Serialisierung (um verschiedene value Objekte)
  • abgeleitet Erstellen Eigenschaften Klasse für jedes neue Gerät:
    • Profis: Typsicherheit
    • Nachteile: haben XmlInclude oder benutzerdefinierte Serialisierung verwenden abgeleiteten Klassen deserialisieren, nicht rückwärts Kompatibilität mit vorherigen XML-Schema (obwohl durch die Implementierung benutzerdefinierter Serialisierung I könnte überspringen unbekannte Eigenschaften?), erfordert Casting für den Zugriff auf Eigenschaften in abgeleiteten Klassen.
  • Eine andere Möglichkeit, es zu tun?

Ich benutze C#, übrigens.

+0

ist XML erforderlich? Beide Ihrer Cons sagen, dass XML behandelt werden muss. Es fühlt sich an, als ob Sie nicht XML verwenden wollen ... – tuergeist

+0

Ja, das ist die eine dieser Client-kennt-ein-Buzzword-Anforderungen. :) – Groo

+0

Ich würde wählen "Erstellen abgeleiteten Eigenschaften Klasse für jedes neue Gerät" ... – tuergeist

Antwort

2

Wie wäre es mit einer ähnlichen PropertyBag?

+0

Nun, das ist im Grunde eine Sammlung von Name-Wert-Paare, ich bin nicht so viel für die interne Implementierung so viel (eine Liste, Wörterbuch, was auch immer). Aber es hat die gleichen Nachteile. – Groo

+1

Das haben wir am Ende benutzt, danke! – Groo

+0

+1 für die Verwendung einer bewährten, gut verstanden, wenn auch nicht sehr sexy Lösung :) – MattDavey

0

Ich glaube, dass das Erstellen abgeleiteter Eigenschaften die beste Wahl ist.

Sie können Ihre neuen Klassen mithilfe des XML-Schemas entwerfen. Und dann generiere einfach den Klassencode mit xsd.exe.

Mit .net ist nicht schwer, eine generische Klasse zu entwickeln, die alle Typen zu und von XML serialisieren und deserialisieren kann.

public static String toXmlString<T>(T value) 
{ 
    XmlSerializer xmlSerializer = new XmlSerializer(typeof(T)); 
    StringWriter stringWriter = new StringWriter(); 
    try { xmlSerializer.Serialize(stringWriter, value); } 
    catch (Exception e) 
    { 
     throw(e); 
    } 
    finally { stringWriter.Dispose(); } 
    String xml = stringWriter.ToString(); 
    stringWriter.Dispose(); 
    return xml; 
} 

public static T fromXmlFile<T>(string fileName, Encoding encoding) 
{ 
    Stream stream; 
    try { stream = File.OpenRead(fileName); } 
    catch (Exception e) 
    { 

     e.Data.Add("File Name", fileName); 
     e.Data.Add("Type", typeof(T).ToString()); 
     throw(e); 
    } 

    BufferedStream bufferedStream = new BufferedStream(stream); 
    XmlSerializer xmlSerializer = new XmlSerializer(typeof(T)); 

    TextReader textReader; 
    if (encoding == null) 
     textReader = new StreamReader(bufferedStream); 
    else 
     textReader = new StreamReader(bufferedStream, encoding); 

    T value; 
    try { value = (T)xmlSerializer.Deserialize(textReader); } 
    catch (Exception e) 
    { 
     e.Data.Add("File Name", fileName); 
     e.Data.Add("Type", typeof(T).ToString()); 
     throw(e); 
    } 
    finally 
    { 
     textReader.Dispose(); 
     bufferedStream.Dispose(); 
    } 
    return value; 
} 
+0

Das Problem ist Abwärtskompatibilität - Sie können es nicht mit XmlSerializer erhalten, weil es für einen bestimmten Typ erstellt wird. Wenn Sie eine Eigenschaft auf eine Instanz einer abgeleiteten Klasse festlegen, müssen Sie [XmlInclude()] hinzufügen, um alle abgeleiteten Klassen anzugeben, auf die XmlSerializer möglicherweise trifft. – Groo

0

Programatically gesprochen, das klingt wie es ein Job für die Decorator Pattern sein könnte. Im Wesentlichen haben Sie eine Superklasse, die eine gemeinsame Schnittstelle für all diese Gerätetypen definiert. Dann haben Sie Decorator-Klassen, die andere Eigenschaften haben, die ein Gerät haben könnte. Beim Erstellen dieser Geräte können Sie diese Dekorationen dynamisch hinzufügen, um neue Eigenschaften für das Gerät zu definieren.Grafisch:

alt text

Sie auf der Wikipedia-Seite für eine detailliertere Beschreibung suchen. Danach wäre es nur eine Frage der Serialisierung, dem Programm mitzuteilen, welche Dekoratoren geladen werden sollen.

1

Wenn Sie nicht auf die Interoperabilität mit einem externen Schema beschränkt sind, sollten Sie Runtime Serialization und SoapFormatter verwenden. Das Muster für die Laufzeitserialisierung ermöglicht abgeleiteten Klassen, anzugeben, welche ihrer Eigenschaften serialisiert werden müssen und was mit ihnen bei der Deserialisierung zu tun ist.

Der XML-Serializer benötigt XmlInclude, da er das zu verwendende Schema definieren muss.

+0

Danke, das mag besser funktionieren. Aber ich würde sehen müssen, wie viel es dauern würde, um XmlSerializer spezifische Sachen zu entfernen und sie mit Runtime Serialization Attributen zu ersetzen und alle notwendigen Änderungen vorzunehmen. Es ist auch interessant, weil ich unveränderliche Typen leicht serialisieren konnte (im Moment muss ich IXmlSerializable ständig implementieren und es durch Reflexion tun). – Groo

1

Ich mag Name/Wert-Sets für diese Art von Sache.

Viele Ihrer Nachteile können behandelt werden - betrachten Sie eine Basisklasse, die als allgemeiner Name/Wert mit No-Op-Methoden für die Validierung eingehende Name/Wert-Paare fungiert. Für bekannte Mengen von Namen (d. H. Schlüssel) können Sie abgeleitete Klassen erstellen, die Validierungsmethoden implementieren.

Zum Beispiel kann der Drucker einen bekannten Schlüssel "PrintsColor" haben, der nur "wahr" oder "falsch" sein kann. Wenn jemand versucht, PrintsColor = "CMYK" zu laden, würde Ihre Druckerklasse eine Ausnahme auslösen.

Je nachdem, was Sie tun, können Sie verschiedene Wege gehen, um die Validierung bequemer zu machen - Hilfsmethoden in der Basisklasse (zB checkForValidBoolean()) oder eine Basisklasse, die Name/Typ akzeptiert Informationen in seinem Konstruktor für saubereren Code in Ihren abgeleiteten Klassen und möglicherweise eine weitgehend automatisierte XML-Serialisierung.

Für Intellisense - Ihre abgeleiteten Klassen könnten grundlegende Zugriffsmethoden haben, die in Bezug auf die Schlüsselsuche implementiert werden. Intellisense würde diese Accessor-Namen präsentieren.

Dieser Ansatz hat für mich gut funktioniert - es gibt eine gewisse Kurzsichtigkeit gegenüber dem klassischen OO-Design, besonders bei großen Systemen mit eingesteckten Komponenten. IMO, die clunkierartige Art, die hier überprüft wird, ist ein großes von einem Widerstand, aber die Flexibilität macht es lohnend.

+0

Danke.Ich würde noch einige benutzerdefinierte Serialisierung mit XmlSerializer tun müssen, aber zumindest würde ich etwas Laufzeitprüfung bekommen. Und es ist abwärtskompatibel zu allen früheren App-Versionen, was auch eine großartige Sache ist. Ich würde lieber eine Reihe von Namen-Wert abgeleiteten Klassen, die die Validierung tun würde, sonst würde ich eine Art von einem Wörterbuch für die Suche nach dem richtigen Delegat (Prädikat) nach Schlüssel innerhalb der Eltern-Eigenschaften-Klasse (wenn ich nicht t verpassen Sie hier etwas?). Und ich würde vermeiden, eine Reihe von Methoden in die Elternklasse zu setzen (checkForThis, checkForThat). – Groo

0

Die allgemeine Idee, was Sie hier erreichen wollen, ist genau das, was das EAV Muster löst. EAV ist ein Muster, das am häufigsten in der Datenbankentwicklung verwendet wird, aber das Konzept ist gleichermaßen für Anwendungen gültig.