代码之家  ›  专栏  ›  技术社区  ›  Andrew Weir

一个对象应该把自己写到一个文件中,还是另一个对象应该对它执行I/O操作?

  •  47
  • Andrew Weir  · 技术社区  · 15 年前

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

    我试图理解对象定向背后的一些关键领域,但我无法以某种方式决定我的特定问题。

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

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

    假设我想将mybob的内容保存到一个XML文件(bob.xml)

    我应该对Bob做一个宾语动作把内容写出来,还是让我的Bob做这个?

    案例1:作用于物体

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

    案例2:保存方法

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

    有些人支持选项1,因为它意味着如果用于写入文件的代码发生更改,就不需要在每个保存方法中更新;我认为这会促进代码的重用。我的问题是从对象中获取所有的数据,这些对象可能有没有访问器的私有数据。

    选项二的情况是,该方法只对对象所持有的数据起作用,这就是它应该的方式。不受其他物体的干扰。

    或者我的问题的答案是一个“案例相关”的问题吗?如果是这样,您如何知道一种方法比另一种方法更受欢迎?

    7 回复  |  直到 9 年前
        1
  •  35
  •   Greg D    15 年前

    一般来说,正确的方法是您的案例1。这维护了类的单一责任(不管它做什么),而没有将它耦合到特定的持久性机制(磁盘)。

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

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

        2
  •  25
  •   Colin Burnett    15 年前

    我会做 Bob 知道如何序列化自己,因为它有私有数据。另一个对象(例如 Writer )把它放在磁盘上。Bob知道如何最好地处理它的数据,但它不需要关心数据存储的方式和位置。您的作者知道如何最好地保存数据,但不需要关心数据是如何创建的。

        3
  •  9
  •   wingerse    9 年前

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

        4
  •  3
  •   JoshBerke    15 年前

    我以前更喜欢选项2;但是,由于我已经开始真正地尝试理解和建模我正在工作的域,所以我更喜欢选项1。

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

        5
  •  3
  •   djcouchycouch    15 年前

    另一种方法是使用访问者模式。让对象包含一个接受方法,该方法通过要处理/序列化的成员,并让一个访问者作为序列化程序。无论何时更新或更改序列化(从纯文本到XML再到二进制再到任何内容),都不需要更新对象。

    我们有很好的工作经验。它很强大。

        6
  •  -3
  •   Pau    15 年前

    这样做:

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

    很明显,这不是一个完整的解决方案,但你应该有一个大致的想法。

        7
  •  -4
  •   Gerardo Contijoch    15 年前

    我认为正确的方法是案例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类就更干净,并且独立于持久性。