2009-05-23 3 views
44

HINWEIS: Entschuldigung für die lange Frage!Sollte sich ein Objekt selbst in eine Datei schreiben oder sollte ein anderes Objekt darauf reagieren, um I/O durchzuführen?

Ich versuche einige Schlüsselbereiche hinter der Objektorientierung zu verstehen, und ich konnte mich nicht in irgendeiner Weise für meine spezielle Frage entscheiden.

Nehmen wir an, ich habe ein Objekt voller schöner Daten. Klasse Bob.

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

Lassen Sie uns sagen, dass ich den Inhalt myBob in eine XML-Datei (bob.xml)

Soll ich ein Objekt handeln bob müssen sparen möchten den Inhalt aus, zu schreiben oder sollte ich tun myBob Dies?

Fall 1: Gesetz über Objekt

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

Fall 2: Save Methode

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

Manche Menschen sind Abstellgleis mit der Option ein, da es bedeutet, wenn der Code für das Schreiben von Dateien ist geändert, muss es nicht für jede Save-Methode aktualisiert werden. Ich befürworte die Wiederverwendung von Code. Mein Problem damit ist, alle Daten aus Objekten zu bekommen, die private Daten ohne Accessor haben können.

Der Fall für Option zwei ist, dass die Methode wirkt nur auf den vom Objekt gehaltenen Daten und das ist so, wie es sein sollte. Keine Beeinträchtigung durch andere Objekte.

Oder ist die Antwort auf meine Frage eines dieser "fallabhängigen" Probleme? Wenn ja, woher weißt du, wenn eine Methode der anderen vorgezogen wird?

Antwort

35

Der richtige Ansatz, in der Regel ist Ihr Fall 1. Das für die Klasse eine einzige Verantwortung hält (was immer es auch tut), ohne sie zu einer bestimmten Persistenzmechanismus Kopplung (eine Scheibe).

Sie betrachten einen bestimmten Fall eines allgemeineren Problems: Serialisierung. Es ist gut und OK für ein Objekt, ein Mittel zu haben, um anzugeben, wie es serialisiert werden soll - es ist die einzige Entität, die weiß, was notwendig ist, um es zu deserialisieren. Wenn Sie jedoch das Objekt auf Festplatte speichern lassen, haben Sie dieses Objekt eng an eine bestimmte Implementierung gekoppelt. Statt

, betrachten eine Schnittstelle zu schaffen, die eine verallgemeinerte „writer“ verwenden kann, um „serialisiert“ das Objekt, was auch immer das Drehbuch serialisiert zu. Auf diese Weise können Sie auf dem Datenträger, im Netzwerk oder im Speicher serialisieren, was Sie tatsächlich serialisieren müssen. :)

+0

Nun, Fall 2 könnte genauso gut stimmen, es kommt wirklich darauf an ... Es gibt kein klares Ja oder Nein im OO-Design. Zum Beispiel könnten Sie auch Szenarien denken, in denen es völlig in Ordnung ist, dass Ihr Objekt nur eine IWriter-Schnittstelle implementiert. –

+1

@divo: Es gibt immer Ausnahmen, das ist die Art des Designs. Es ist nicht etwas, das explizit herausgerufen werden sollte. (Und das ist auch der Grund, warum ich meine Aussage mit "im Allgemeinen" qualifiziert habe.) –

+0

Was ist die empfohlene Strategie zur Deserialisierung des Objekts? Wäre es in Ordnung, dann ein BinaryReader-Objekt zu übergeben und die Klasse sich deserialisieren zu lassen? –

24

Ich würde Bob wissen, wie man sich selbst serialisiert, da es private Daten hat. Ein anderes Objekt (wie Ihre Writer) würde das übernehmen und auf die Festplatte legen. Bob weiß, wie man am besten mit seinen Daten umgeht, aber es muss sich nicht darum kümmern, wie oder wo das gespeichert wird. Ihr Writer weiß, wie Sie Daten am besten speichern, aber es muss sich nicht darum kümmern, wie diese Daten erstellt werden.

+0

Ich mag es. Ich dachte nicht, Serialisierung als eine Art zu verwenden, meine Frage zu behandeln. –

+4

+1. Wenn Sie das Ausblenden und Einkapseln von Informationen in Betracht ziehen, ist Bob das einzige Objekt, das Bob (im Allgemeinen) serialisieren könnte. Generalisieren Sie "Speichern" als Schreiben in einen Stream, um die Kopplung wie empfohlen von der Datei zu trennen. –

3

Ich bevorzugte Option 2; aber, wie ich begonnen habe wirklich die Domains zu verstehen und zu modellieren versucht, ich arbeite, ziehe ich Option 1.

Stellen Sie sich vor, wenn Sie Ihre Modellierung Fahrzeuge. Warum sollte ein Fahrzeug wissen, wie es sich selbst hält? Es könnte wissen, wie man sich bewegt, wie man startet und wie man stoppt, aber was ist Save im Kontext eines Fahrzeugs.

+1

Ich denke, es hängt davon ab, was Sie modellieren. Stellen Sie sich vor, Sie würden Textdokumente modellieren, die normalerweise in irgendeiner Form erhalten bleiben. Dann wäre es natürlich, eine Save-Methode zu haben (obwohl Sie immer noch mit Option 1 gehen möchten). –

-2

Ich denke, der richtige Ansatz ist der Fall 1, aber Ihre Klasse kann auf diese Weise die Vorteile beider Ansätze zu nehmen definiert werden:

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); 

    } 
    //... 
} 

Diese leicht die Schreiblogik und Initialisierung setzen in ein modifiziert werden könnte, Basisklasse, so Bob Klasse ist noch sauberer und unabhängig von der Persistenz.

+0

Warum wurde das abgelehnt? –

+0

Vielleicht ist der Tippfehler in "c: //bob.xml" Grund genug, diese Antwort zu verwerfen ...: P –

+2

Ich habe keinen Downvote gemacht, aber es gibt viele Probleme: Jeder Bob hat die zusätzlichen Kosten einer Writer-Referenz , jeder Bob hat nur einen Writer zur Konstruktionszeit zugewiesen, wenn der aufrufende Code ihn in mehrere Writer oder keinen Writer schreiben möchte, und was ein nettes reines Domain-Objekt sein sollte, ist unnötigerweise mit IO-Mechanismen verknüpft. Der Ansatz, bei dem jeder Bob weiß, wie er sich selbst zu und von einem (oder vielleicht mehr) String-Format serialisiert, erlaubt es, das IO anderswo zu definieren, für eine viel größere Modularität. –

9

Dies ist ein Beispiel dafür, wo das Strategy Design Pattern verwendet werden könnte. Ihr myBob Objekt könnte eine Instanz der Klasse haben, die es ausschreiben wird. Möglicherweise möchten Sie, dass der Writer eine Schnittstelle implementiert oder von einer abstrakten Klasse ableitet, damit die Sicherungsroutine leicht geändert werden kann.
Heute speichern Sie in XML, aber möglicherweise müssen Sie das Objekt eventuell auch in einer Datenbank beibehalten. Mit diesem Muster können Sie die Speicherroutine einfach ändern. Sie hätten sogar die Möglichkeit zu ändern, wie Sie zur Laufzeit speichern.

+0

Up vote for you !!! Guter Aufruf an Strategy Pattern, da es ohne Änderungen erweitert werden kann. –

-2

tun:

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) {...} 
} 

Offensichtlich ist dies keine vollständige Lösung, aber Sie sollten die allgemeine Idee.

3

Eine andere Methode besteht darin, das Besuchermuster zu verwenden. Lassen Sie Ihr Objekt eine Accept-Methode enthalten, die die Mitglieder durchläuft, die Sie bearbeiten/serialisieren möchten, und dass ein Besucher Ihr Serializer sein soll. Wenn Sie Ihre Serialisierung aktualisieren oder ändern (reiner Text in xml in binär, in was auch immer), müssen Sie das Objekt nicht aktualisieren.

Wir hatten gute Erfahrungen auf diesem Weg. Es ist ziemlich mächtig.

+0

IMO Visitor sollte verwendet werden, wenn Sie eine Vererbungshierarchie haben und typspezifisches Verhalten benötigen und getType und instanceof checks vermeiden. Besucher dafür zu benutzen wäre übertrieben. Strategie passt besser. –