2009-07-01 10 views
5

Mam aplikację, która obsługuje wiele typów i wersji niektórych urządzeń. Może łączyć się z tymi urządzeniami i pobierać różne informacje.Tworzenie klasy właściwości rozszerzalnych (OOP)

W zależności od rodzaju urządzenia posiadam (między innymi) klasę, która może zawierać różne właściwości. Niektóre właściwości są wspólne dla wszystkich urządzeń, niektóre są unikalne dla konkretnego urządzenia.

Dane te są serializowane do xml.

Jaki byłby preferowany sposób wdrożenia klasy, która obsługiwałaby przyszłe właściwości w przyszłych wersjach tych urządzeń, a także była kompatybilna wstecz z poprzednimi wersjami aplikacji?

mogę myśleć kilka sposobów, ale uważam, że żaden z nich świetne:

  • Użyj zbiór par nazwa-wartość:
    • Plusy: dobrą kompatybilność wsteczną (XML oraz poprzednie wersje mojej aplikacji) i rozszerzalność,
    • przeciw: brak bezpieczeństwa typu, brak intellisense, wymaga implementacji niestandardowej serializacji xml (do obsługi różnych value obiekty)
  • Tworzenie pochodzący właściwości klasy dla każdego nowego urządzenia:
    • plusy: Typ bezpieczeństwa
    • minusy: trzeba użyć XmlInclude lub niestandardową serializacji deserializacji klas pochodnych, nie wstecz zgodność z poprzednim schematem XML (mimo że implementacja niestandardowej serializacji I może pomiń nieznane właściwości?), wymaga rzutowania w celu uzyskania dostępu do właściwości w klasach pochodnych.
  • Inny sposób na zrobienie tego?

Używam C#, przy okazji.

+0

czy XML jest wymagany? Oba pasze twierdzą, że XML musi być obsługiwany. Wydaje się, że nie chcesz używać XML ... – tuergeist

+0

Tak, to jeden z tych wymagań klienta-zna-a-modne. :) – Groo

+0

Wybrałabym "Utwórz klasy właściwości pochodnych dla każdego nowego urządzenia" ... – tuergeist

Odpowiedz

2

Co powiesz na coś podobnego do PropertyBag?

+0

Cóż, to jest po prostu zbiór par nazwa-wartość, nie dbam o wewnętrzną implementację tak bardzo (lista, słownik, cokolwiek). Ale ma te same minusy. – Groo

+1

To jest to, czego użyliśmy na końcu, dzięki! – Groo

+0

+1 za korzystanie z wypróbowanego i zaufanego, dobrze zrozumiałego, aczkolwiek niezbyt seksownego rozwiązania :) – MattDavey

0

Wierzę, że tworzenie właściwości pochodnych jest najlepszym wyborem.

Możesz zaprojektować swoje nowe zajęcia za pomocą schematu xml. A następnie po prostu wygeneruj kod klasy z xsd.exe.

Z .net nie jest trudne do opracowania ogólnej klasy, która może serializować i deserializować wszystkie typy do iz xml.

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

Problemem jest kompatybilność wsteczna - nie można go zachować przy pomocy XmlSerializer, ponieważ jest on stworzony dla określonego typu. Jeśli ustawisz właściwość dla instancji klasy pochodnej, będziesz musiał dodać [XmlInclude()], aby określić wszystkie klasy pochodne, które może napotkać XmlSerializer. – Groo

0

Wygląda na to, że może to być praca dla Decorator Pattern. Zasadniczo masz super klasę, która definiuje wspólny interfejs dla wszystkich tych typów urządzeń. Następnie masz klasy dekoratorów, które mają inne właściwości, które może posiadać urządzenie. Podczas tworzenia tych urządzeń można dynamicznie dodawać te dekoracje, aby zdefiniować nowe właściwości urządzenia.Graficznie:

alt text

Możesz zajrzeć na strony Wikipedia na bardziej szczegółowym opisem. Po tym, będzie to tylko kwestia zignorowania niektórych serializacji, aby powiedzieć programowi, które dekoratory załadować.

1

Jeśli nie ograniczasz się do współpracy z zewnętrznym schematem, powinieneś użyć Serializacji Runtime i SoapFormatter. Wzorzec do serializacji w czasie wykonywania pozwala klasom pochodnym określić, które z ich właściwości wymagają serializacji i co zrobić z nimi po deserializacji.

Serializator XML wymaga XmlInclude, ponieważ w rzeczywistości musi zdefiniować schemat do użycia.

+0

Dzięki, że może działać lepiej. Ale musiałbym się przekonać, ile zajmie usunięcie konkretnych rzeczy XmlSerializer i zastąpienie ich atrybutami Serializacja Runtime i wykonanie wszystkich niezbędnych zmian. Jest to również interesujące, ponieważ mogłem łatwo serializować niezmienne typy (teraz muszę zaimplementować IXmlSerializable przez cały czas i zrobić to poprzez odbicie). – Groo

1

Lubię zestawy nazw/wartości do tego typu rzeczy.

Wiele z twoich wad można rozwiązać - należy rozważyć klasę bazową, która działa jak ogólny zestaw nazw/wartości z metodami bez reguł do sprawdzania przychodzących par nazwa/wartość. W przypadku znanych zestawów nazw (tj. Kluczy) można tworzyć klasy pochodne, które implementują metody sprawdzania poprawności.

Na przykład drukarka może mieć znany klucz "PrintsColor", który może być tylko "true" lub "false". Jeśli ktoś spróbuje załadować PrintsColor = "CMYK", twoja klasa Printer rzuci wyjątek.

Zależnie od tego, co robisz, możesz zrobić kilka różnych sposobów, aby zwiększyć wygodę sprawdzania poprawności - metody użytkowe w klasie bazowej (np. CheckForValidBoolean()) lub klasa bazowa akceptująca nazwę/typ informacje w swoim konstruktorze dla czystszego kodu w twoich klasach pochodnych i być może w większości zautomatyzowanej serializacji XML.

Dla intellisense - Twoje klasy pochodne mogą mieć podstawowe elementy dostępu, które są implementowane pod kątem wyszukiwania kluczy. Intellisense przedstawiłby te nazwy akcesorów.

To podejście sprawdziło się dobrze - jest coś w rodzaju krótkowzroczności w klasycznym projekcie OO, szczególnie w przypadku dużych systemów z podłączonymi komponentami. IMO, to sprawdzanie tutaj jest dużym oporem, ale elastyczność sprawia, że ​​warto.

+0

Dzięki.Nadal wymagałbym specjalnej serializacji z XmlSerializer, ale przynajmniej uzyskałbym pewne sprawdzanie w czasie wykonywania. Jest kompatybilny wstecz ze wszystkimi poprzednimi wersjami aplikacji, co również jest świetną rzeczą. Wolałbym mieć grupę klas wyprowadzonych z wartości nazwa-wartość, które dokonałyby sprawdzania poprawności, w przeciwnym razie musiałbym umieścić jakiś słownik do wyszukiwania dla właściwego delegata (predykatu) przez klucz wewnątrz rodzicielskiej klasy właściwości (gdybym nie zrobił ' T miss coś tutaj?). I chciałbym uniknąć umieszczenia zestawu metod w klasie nadrzędnej (checkForThis, checkForThat). – Groo

0

Ogólną ideą tego, co próbujesz osiągnąć, jest właśnie to, co rozwiązuje wzór EAV. EAV to wzorzec najczęściej używany w tworzeniu baz danych, ale koncepcja jest równie ważna dla aplikacji.