C# 一个对象应该将自身写入文件,还是另一个对象应该对其进行操作以执行I/O?

C# 一个对象应该将自身写入文件,还是另一个对象应该对其进行操作以执行I/O?,c#,design-patterns,oop,io,C#,Design Patterns,Oop,Io,注意:很抱歉问了这么长的问题 我试图理解面向对象背后的一些关键领域,但我无法决定我的特定问题 假设我有一个充满可爱数据的对象。班长鲍勃 Bob myBob = new Bob("This string is data"); 假设我想将myBob的内容保存到一个xml文件(bob.xml) 我应该让对象作用于bob来写出内容,还是让myBob这样做 案例1:作用于对象 Writer myWriter = new Writer(myBob, "C:\\bob.xml"); 案例2:保存方法 my

注意:很抱歉问了这么长的问题

我试图理解面向对象背后的一些关键领域,但我无法决定我的特定问题

假设我有一个充满可爱数据的对象。班长鲍勃

Bob myBob = new Bob("This string is data");
假设我想将myBob的内容保存到一个xml文件(bob.xml)

我应该让对象作用于bob来写出内容,还是让myBob这样做

案例1:作用于对象

Writer myWriter = new Writer(myBob, "C:\\bob.xml");
案例2:保存方法

myBob.Save("C:\\bob.xml");
有些人支持选项一,因为这意味着,如果编写文件的代码发生更改,则不需要跨每个保存方法进行更新;我想应该是促进代码重用。我的问题是从对象中获取所有数据,这些对象可能有没有访问器的私有数据

选项二的情况是,该方法只作用于对象所持有的数据,这是它应该采取的方式。没有来自其他对象的干扰


还是我的问题的答案属于“案例依赖”问题?如果是这样,您如何知道何时一种方法优于另一种方法?

我想让
Bob
知道如何序列化自己,因为它有私有数据。另一个对象(例如您的
编写器
)将获取该对象并将其放在磁盘上。Bob知道如何最好地处理数据,但不必关心数据的存储方式或存储位置。您的作者知道如何最好地保存数据,但不必关心数据是如何创建的。

通常,正确的方法是案例1。这将维护类的单一职责(无论它做什么),而不将其耦合到特定的持久性机制(磁盘)

您看到的是一个更一般化问题的具体案例:序列化。对于一个对象来说,有一些方法来指示它应该如何序列化是很好的——毕竟,它是唯一一个知道反序列化它需要什么的实体。但是,如果您让对象将自身保存到磁盘,那么您已经将该对象与特定实现紧密耦合


相反,考虑创建一个接口,一个通用的“写入器”可以用来将对象“序列化”到作者序列化到的任何对象。通过这种方式,您将能够序列化到磁盘、网络、内存,以及实际需要序列化到的任何内容。:)

我过去更喜欢选项2;然而,由于我已经开始真正尝试理解和建模我正在研究的领域,我更喜欢选项1


想象一下,如果你的模型车。为什么车辆会知道如何保持自身?它可能知道如何移动、如何启动和如何停止,但在车辆的上下文中保存什么

我认为正确的方法是案例1,但您的类可以这样定义,以利用这两种方法:

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

    }
    //...
}

这可以很容易地修改,以便将编写逻辑和初始化放在基类中,这样Bob类就更干净,并且独立于持久性。

这是一个可以使用策略设计模式的示例。您的
myBob
对象可以有一个类的实例来写出它。您可能希望编写器实现接口或从抽象类派生,以便可以轻松更改保存例程。
现在,您正在保存为xml,但最终可能还需要将对象持久化到数据库。此模式将允许您轻松更改保存例程。您甚至可以选择更改运行时的保存方式。

执行以下操作:

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

显然,这不是一个完整的解决方案,但您应该了解总体思路。

另一种方法是使用访问者模式。让对象包含一个Accept方法,该方法遍历要处理/序列化的成员,并让访问者成为序列化程序。无论何时更新或更改序列化(从纯文本到xml再到二进制再到其他格式),都不需要更新对象


我们在这样做的工作中有很好的经验。它很强大。

我喜欢它。我没想到用序列化来处理我的问题。当您考虑信息隐藏和封装时,可以(通常)序列化鲍伯的唯一对象是鲍伯。将“保存”概括为写入流,以按照建议抽象出与文件的耦合。。。OO设计中没有明确的是或否。例如,您可能还认为在某些情况下,您的对象只实现一个IWriter接口是完全正确的。@divo:总会有例外,这是设计的本质。这不是必须明确指出的事情。(这也是为什么我用“总体上”来限定我的陈述)什么是反序列化对象的推荐策略?然后传入一个BinaryReader对象并让类本身反序列化,这样可以吗?@joe这应该对您有所帮助;因为它帮助了我。我只想再次感谢格雷格和科林。这是一个很好的解决问题的方法。这个模式叫什么!?我想这取决于你在做什么。想象一下,如果您正在为文本文档建模,这些文档通常以某种形式持久化。然后,自然会有一个保存方法(尽管你可能仍然想选择选项1)。投票给你!!!很好的访问策略模式,因为它允许在不修改的情况下进行扩展。也许“c://bob.xml”中的输入错误足以让我们否决这个答案PI没有否决,但这里有很多问题:每个Bob都有一个Writer引用的额外成本,当调用代码可能希望将其写入多个Writer或没有Writer时,每个Bob在构造时只分配了一个Writer,并且一个好的纯域对象不必要地与IO机制互连。每个Bob都知道如何在一个(或多个)Bob之间序列化自己的方法