2009-05-23 12 views
44

UWAGA: Przepraszam za długie pytanie!Czy obiekt powinien wypisać się do pliku, czy inny obiekt powinien na nim działać, aby wykonać operacje wejścia/wyjścia?

Próbuję zrozumieć niektóre kluczowe obszary leżące za orientacją obiektową i nie mogłem zdecydować w ten czy inny sposób o moim konkretnym pytaniu.

Załóżmy, że mam obiekt pełen uroczych danych. Boba klasy.

Bob myBob = new Bob("This string is data"); 

Powiedzmy, że chcemy zapisać zawartość myBob do pliku XML (bob.xml)

Powinienem akt obiekt na bob do zapisu zawartości na zewnątrz, czy też nie mam myBob to?

Przypadek 1: ustawa o obiekcie

Writer myWriter = new Writer(myBob, "C:\\bob.xml"); 

Przypadek 2: Zapisz metoda

myBob.Save("C:\\bob.xml"); 

Niektórzy ludzie są siding z opcją jednej jak to znaczy jeśli kod do zapisu plików jest zmieniony, nie musi być aktualizowany dla każdej metody składowania; promowanie ponownego użycia kodu, jak przypuszczam. Moim problemem jest pobieranie wszystkich danych z obiektów, które mogą mieć prywatne dane bez akcesora.

Sprawą dla opcji drugiej jest to, że metoda działa tylko na dane przechowywane przez obiekt i tak powinno być. Brak zakłóceń od innych obiektów.

Czy odpowiedź na moje pytanie jest jedną z tych "zależnych od sprawy"? Jeśli tak, to skąd wiadomo, że jedna metoda jest preferowana w stosunku do drugiej?

Odpowiedz

35

Właściwe podejście, ogólnie rzecz biorąc, to Przypadek 1. Utrzymuje on jedną odpowiedzialność za klasę (cokolwiek robi) bez powiązania jej z określonym mechanizmem trwałości (dysk).

Patrzysz na konkretny przypadek bardziej uogólnionego problemu: Serializacja. To dobrze i dobrze, bo obiekt ma jakiś sposób na wskazanie, jak powinien być serializowany - to jedyny byt, który wie, co jest niezbędne do jego deserializacji. Ale jeśli sprawisz, że obiekt sam się zapisze na dysk, to ściśle powiązałeś ten obiekt z konkretną implementacją.

Zamiast tego rozważ utworzenie interfejsu, którego uogólniony "program piszący" może użyć do "serializowania" obiektu do tego, do którego serializowany jest pisarz. W ten sposób będziesz mógł przekształcić się na dysk, do sieci, do pamięci, do wszystkiego, czego potrzebujesz do serializacji. :)

+0

Cóż, przypadek 2 równie dobrze może być poprawny, to naprawdę zależy ... Nie ma wyraźnego "tak" lub "nie" w projekcie OO. Na przykład, możesz także pomyśleć o scenariuszach, w których jest to całkowicie w porządku, że twój obiekt implementuje interfejs IWriter. –

+1

@divo: Zawsze są wyjątki, taka jest natura projektu. Nie jest to coś, co powinno być jawnie wywoływane. (I to jest także powód, dla którego zakwalifikowałem moje oświadczenie z "ogólnie".) –

+0

Jaka jest zalecana strategia deserializacji obiektu? Czy w takim razie dobrze byłoby przekazać obiekt BinaryReader i dokonać deserializacji samej klasy? –

24

Zrobiłbym Bob wiedzieć, jak serializować się, ponieważ ma prywatne dane. Inny obiekt (taki jak Twój Writer) weźmie to i umieści go na dysku. Bob wie, jak najlepiej radzić sobie z danymi, ale nie musi przejmować się tym, jak i gdzie jest przechowywany. Twój programista wie, jak najlepiej zapisać dane, ale nie musi dbać o to, jak tworzone są dane.

+0

Podoba mi się. Nie myślałem, aby użyć serializacji jako sposobu na obsłużenie mojego pytania. –

+4

+1. Kiedy zastanawiasz się nad ukrywaniem informacji i enkapsulacją, jedynym obiektem, który może (ogólnie) serializować Boba jest Bob. Uogólniaj "zapisywanie" jako zapis do strumienia, aby oddzielić połączenie do pliku zgodnie z zaleceniami. –

3

Kiedyś wolałem opcję 2; jednak, jak zacząłem naprawdę próbować zrozumieć i modelować domeny, nad którymi pracuję, wolę opcję 1.

Wyobraź sobie, że Twoje pojazdy modelujące. Dlaczego pojazd wiedziałby, jak przetrwać? Może wiedzieć, jak się poruszać, jak zacząć i jak się zatrzymać, ale czym jest Save w kontekście pojazdu.

+1

Myślę, że to zależy od tego, co modelujesz. Wyobraź sobie, że modelujesz dokumenty tekstowe, które zazwyczaj zachowują się w jakiejś formie. Wtedy naturalnie byłoby mieć metodę Save (chociaż nadal możesz chcieć skorzystać z opcji 1). –

-2

myślę prawidłowe podejście jest przypadek 1, ale klasa może być określony w taki sposób, aby skorzystać z obu podejść:

class Bob { 

    IWriter _myWriter = null; 

    public Bob(){ 
     // This instance could be injected or you may use a factory 
     // Note the initialization logic is here and not in Save method 
     _myWriter = new Writer("c://bob.xml") 
    } 

    //... 
    public void Save(){ 

     _myWriter.Write(this);  

    } 
    // Or... 
    public void Save(string where){ 

     _myWriter.Write(this, where); 

    } 
    //... 
} 

To może być łatwo modyfikowane, aby umieścić logikę pisania i inicjalizacji w sposób podstawowa klasa, więc klasa Boba jest jeszcze czystsza i niezależna od trwałości.

+0

Dlaczego zostało to odrzucone? –

+0

Być może literówka w "c: //bob.xml" jest wystarczającym powodem, aby zlekceważyć tę odpowiedź ...: P –

+2

Nie przesłałem odpowiedzi, ale jest tu wiele problemów: każdy Bob ma dodatkowy koszt odniesienia do pisarza , każdy Bob ma tylko jednego Writera przydzielonego w czasie budowy, kiedy kod wywołujący może chcieć zapisać go do wielu Writersów lub bez Writera, a to, co powinno być miłym czystym obiektem domeny, jest niepotrzebnie powiązane z mechanizmami IO. Podejście, w którym każdy Bob wie, jak przekształcić się do iz jednego (lub może nawet więcej) formatów String, pozwala na zdefiniowanie IO w innym miejscu, w celu uzyskania znacznie większej modułowości. –

9

Jest to przykład miejsca, w którym można użyć wzoru projektowania strategii . Twój obiekt myBob może mieć instancję klasy, która ją zapisze. Możesz chcieć, aby program piszący implementował interfejs lub wywodził się z klasy abstrakcyjnej, aby można było łatwo zmienić procedurę składowania.
Dzisiaj zapisujesz w formacie xml, ale może być konieczne, aby obiekt był nadal przechowywany w bazie danych. Ten wzór pozwoli ci łatwo zmienić procedurę zapisu. Mógłbyś nawet zmienić sposób zapisywania w środowisku wykonawczym.

+0

Głosujcie za wami !!! Dobre wezwanie do wzoru strategii, ponieważ pozwala na rozszerzenie bez modyfikacji. –

-2

Wykonaj:

public interface Writable { 
    public void Save(Writer w); 
} 

public interface Writer { 
    public void WriteTag(String tag, String cdata); 
} 

public class Bob : Writable { 
    private String ssn = "123-23-1234"; 
    public void Save(Writer w) { 
     w.WriteTag("ssn", ssn); 
    } 
} 

public class XmlWriter : Writer { 
    public XmlWriter(Sting filename) {...} 
    public void WriteTag(String tag, Sting cdata) {...} 
} 

Oczywiście nie jest to kompletne rozwiązanie, ale należy uzyskać ogólne pojęcie.

3

Jedną z innych metod jest użycie wzoru gościa. Niech twój obiekt zawiera metodę Accept, która przechodzi przez członków, których chcesz przetworzyć/serializuje, i niech odwiedzający będzie twoim serializatorem. Za każdym razem, gdy aktualizujesz lub zmieniasz serializację (zwykły tekst na xml na binarny), nie musisz aktualizować obiektu.

Mieliśmy dobre doświadczenia w pracy, robiąc to w ten sposób. Jest całkiem potężny.

+0

Użytkownik IMO powinien być używany, gdy posiadasz hierarchię dziedziczenia i potrzebujesz określonego zachowania i unikniesz kontroli getType i instanceof. Używanie tego do odwiedzania byłoby nadmierną inżynierią. Strategia pasuje lepiej. –

Powiązane problemy