2009-07-08 12 views
6

Załóżmy, że mamy obiekt reprezentujący konfigurację sprzętu. Ze względu na argument, regulator temperatury (TempController). Zawiera jedną właściwość, ustawioną temperaturę.Obiektywny sposób oddzielania modelu od jego reprezentacji

Muszę zapisać tę konfigurację do pliku, aby móc jej użyć w innym urządzeniu. Format pliku (FormatA) jest ustawiony na kamień. Nie chcę, aby obiekt TempController wiedział o formacie pliku ... po prostu nie jest odpowiedni dla tego obiektu. Robię więc kolejny obiekt, "FormatAExporter", który przekształca kontroler TempController na pożądane wyjście.

Rok później tworzymy nowy regulator temperatury, nazwijmy go "AdvancedTempController", który ma nie tylko wartość zadaną, ale także kontrolę szybkości, co oznacza jedną lub dwie dodatkowe właściwości. Nowy format pliku jest również wymyślony, aby przechowywać te właściwości ... nazwijmy to FormatB.

Oba formaty plików mogą reprezentować oba urządzenia (zakładamy, że AdvancedTempController ma rozsądne wartości domyślne, jeśli nie ma w nim ustawień).

Oto, jaki jest problem: bez użycia "isa" lub innego "oszustwa", aby dowiedzieć się, jaki typ obiektu mam, w jaki sposób FormatBExporter może obsługiwać oba przypadki?

Moim pierwszym instynktem jest posiadanie metody w każdym regulatorze temperatury, który może zapewnić eksportera klienta dla tej klasy, np. TempController.getExporter() i AdvancedTempController.getExporter(). To nie obsługuje dobrze wielu formatów plików.

Jedynym innym podejściem, które przychodzi na myśl jest posiadanie w każdym regulatorze temperatury metody, która zwraca listę właściwości i ich wartości, a następnie formater może zdecydować, jak je wyprowadzić. To zadziała, ale wydaje się zawiłe.

AKTUALIZACJA: Po dalszej pracy to drugie podejście nie działa tak naprawdę. Jeśli wszystkie twoje typy są proste, ale jeśli twoje właściwości są Obiektami, to kończysz, po prostu wypychając problem w dół ... jesteś zmuszony zwrócić parę wartości Ciąg, Obiektów, a eksporter będzie musiał wiedzieć, co Obiekty faktycznie mają z nich korzystać. Po prostu przesuwa problem na inny poziom.

Czy są jakieś sugestie, w jaki sposób mogę zachować to elastyczne?

Odpowiedz

4

To, co możesz zrobić, to pozwolić TempControllerom być odpowiedzialnymi za utrzymywanie się przy użyciu ogólnego archiwizatora.

class TempController 
{ 
    private Temperature _setPoint; 
    public Temperature SetPoint { get; set;} 

    public ImportFrom(Archive archive) 
    { 
     SetPoint = archive.Read("SetPoint"); 
    } 
    public ExportTo(Archive archive) 

    { 
     archive.Write("SetPoint", SetPoint); 
    } 
} 

class AdvancedTempController 
{ 
    private Temperature _setPoint; 
    private Rate _rateControl; 
    public Temperature SetPoint { get; set;} 
    public Rate RateControl { get; set;} 

    public ImportFrom(Archive archive) 
    { 
     SetPoint = archive.Read("SetPoint"); 
     RateControl = archive.ReadWithDefault("RateControl", Rate.Zero); 
    } 

    public ExportTo(Archive archive) 
    { 
     archive.Write("SetPoint", SetPoint); 
     archive.Write("RateControl", RateControl); 
    } 
} 

utrzymując go w ten sposób, że kontrolerzy nie obchodzi, jak rzeczywiste wartości są przechowywane, ale nadal zachowując wewnętrzne obiektu dobrze kapsułkach.

Teraz możesz zdefiniować abstrakcyjną klasę Archiwum, którą mogą zaimplementować wszystkie klasy archiwum.

abstract class Archive 
{ 
    public abstract object Read(string key); 
    public abstract object ReadWithDefault(string key, object defaultValue); 
    public abstract void Write(string key); 
} 

FormatA archiwizator może to zrobić w jeden sposób, a archiwum FormatB może zrobić to w inny sposób.

class FormatAArchive : Archive 
{ 
    public object Read(string key) 
    { 
     // read stuff 
    } 

    public object ReadWithDefault(string key, object defaultValue) 
    { 
     // if store contains key, read stuff 
     // else return default value 
    } 

    public void Write(string key) 
    { 
     // write stuff 
    } 
} 

class FormatBArchive : Archive 
{ 
    public object Read(string key) 
    { 
     // read stuff 
    } 

    public object ReadWithDefault(string key, object defaultValue) 
    { 
     // if store contains key, read stuff 
     // else return default value 
    } 

    public void Write(string key) 
    { 
     // write stuff 
    } 
} 

Można dodać inny typ kontrolera i przekazać go bez względu na format. Można również utworzyć inny formatator i przekazać go do dowolnego kontrolera.

+0

to również uczynić kod bardziej sprawdzalne, bo to, bo o wiele łatwiej drwić z kolaborantów. –

+0

To dotyk bardziej skomplikowany, niż planowałem, ale lubię go. Zapewnia elastyczność zwracania wszystkich właściwości na liście, ale przy zachowaniu informacji o typie w całym interfejsie. I zdecydowanie pomaga w testowalności. –

0

Chciałbym mieć "kontroler temp", za pomocą metody getState, zwrócić mapę (np. W Pythonie dyktować, w JavaScript obiektu, w C++ std :: map lub std :: hashmap, etc, etc) o jego właściwościach i aktualnych wartościach - co jest w niej zawiłego ?! Nie może być prostsze, jest całkowicie rozszerzalne i całkowicie odsprzęgnięte od zastosowania (wyświetlenie, serializacja, & c).

0

Jeśli FormatBExporter pobiera AdvancedTempController, można utworzyć klasę mostu, która sprawia, że ​​TempController jest zgodny z AdvancedTempController. Może być jednak konieczne dodanie funkcji getFormat() do AdvancedTempController.

Na przykład:

FormatBExporter exporterB; 
TempController tempController; 
AdvancedTempController bridged = TempToAdvancedTempBridge(tempController); 

exporterB.export(bridged); 

Istnieje również możliwość korzystania z systemu mapowania klucz do wartości. FormatAExporter eksportuje/importuje wartość dla klucza "wartość zadana". FormatBExporter eksportuje/importuje wartości dla kluczy "setpoint" i "ratecontrol". W ten sposób stary FormatAExporter może nadal czytać nowy format pliku (po prostu ignoruje "ratecontrol") i FormatBExporter może odczytać stary format pliku (jeśli "ratecontrol" nie ma, używa domyślnego).

0

Cóż, wiele z nich zależy od formatów plików, o których mówisz.

Jeśli są oparte na kombinacjach klucz/wartość (w tym zagnieżdżone, np. Xml), to dobrym sposobem na zrobienie jest luźny typ pośredniego obiektu pamięci, który można umieścić w odpowiednim pisarzu formatu pliku. to.

Jeśli nie, to masz scenariusz, w którym masz cztery kombinacje obiektów i formatów plików, z niestandardową logiką dla każdego scenariusza. W takim przypadku może nie być możliwe posiadanie pojedynczej reprezentacji dla każdego formatu pliku, który może obsługiwać dowolny kontroler. Innymi słowy, jeśli nie możesz uogólnić pisarza formatu pliku, nie możesz go uogólnić.

Nie podoba mi się pomysł kontrolerów posiadających eksporterów - po prostu nie jestem fanem obiektów, które wiedzą o mechanizmach magazynowania i tym podobnych (mogą wiedzieć o koncepcji przechowywania i mają określoną instancję je przez jakiś mechanizm DI). Ale myślę, że zgadzasz się z tym i z tych samych powodów.

0

W modelu OO metodami obiektu zbiorowego jest kontroler. Bardziej przydatne jest oddzielenie programu od M i V, a nie do C, jeśli programujesz za pomocą OO.

1

W języku C# lub innych języków, które obsługują tę można to zrobić:

class TempController { 
    int SetPoint; 
} 
class AdvancedTempController : TempController { 
    int Rate; 
} 

class FormatAExporter { 
    void Export(TempController tc) { 
     Write(tc.SetPoint); 
    } 
} 

class FormatBExporter { 
    void Export(TempController tc) { 
     if (tc is AdvancedTempController) { 
      Write((tc as AdvancedTempController).Rate); 
     } 
     Write(tc.SetPoint); 
    } 
} 
+1

To jest najprostszy sposób na krótką metę. Ale tou używał "jest" i "jak". Teraz za każdym razem trzeba utrzymywać FormatBExporter, aby obsłużyć wszystkie typy. Kiedy zostaniesz pokonany ekstremalnie, będziesz miał las zdań "jeśli", aby dowiedzieć się, jaki jest typ, a cały ich punkt był taki, by robić to samo. –

Powiązane problemy