¿Debería un objeto escribirse en un archivo, o debería actuar otro objeto sobre él para realizar la E / S?

NOTA: Perdón por la larga pregunta!

Estoy tratando de entender algunas áreas clave detrás de la orientación a objetos y no pude decidir de una manera u otra acerca de mi pregunta en particular.

Digamos que tengo un objeto lleno de datos encantadores. Clase bob.

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

Digamos que quiero guardar el contenido de myBob en un archivo xml (bob.xml)

¿Debo hacer que un objeto actúe sobre bob para escribir el contenido, o debo hacer que myBob haga esto?

Caso 1: Actuar sobre el objeto.

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

Caso 2: Método de guardado

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

Algunas personas están del lado de la opción uno, ya que significa que si se cambia el código para escribir archivos, no es necesario actualizarlo en todos los métodos de Guardar; Promover la reutilización de código, supongo. Mi problema con esto es sacar todos los datos de los objetos que pueden tener datos privados sin acceso.

El caso para la opción dos es que el método solo actúa sobre los datos almacenados por el objeto y así es como debería ser. Ninguna interferencia de otros objetos.

¿O es la respuesta a mi pregunta uno de esos problemas “dependientes del caso”? Si es así, ¿cómo saber cuándo se prefiere un método sobre el otro?

El enfoque correcto, en general, es su Caso 1. Esto mantiene una responsabilidad única para la clase (haga lo que haga) sin acoplarlo a un mecanismo de persistencia específico (un disco).

Está viendo un caso específico de un problema más generalizado: la serialización. Es bueno y está bien que un objeto tenga algún medio para indicar cómo se debe serializar. Es la única entidad que sabe lo que es necesario para deserializarlo, después de todo. Pero si hace que el objeto se guarde a sí mismo en el disco, ha acoplado estrechamente ese objeto a una implementación específica.

En su lugar, considere la posibilidad de crear una interfaz que un “escritor” generalizado pueda usar para “serializar” el objeto a lo que sea que el escritor se serialice. De esta forma, podrá serializar en disco, en red, en memoria, en lo que necesite para serializar. 🙂

Haría que Bob supiera cómo serializarse ya que tiene datos privados. Otro objeto (como su Writer ) tomaría eso y lo pondría en el disco. Bob sabe cómo manejar mejor sus datos, pero no tiene que preocuparse por cómo o dónde se almacena. Su Escritor sabe cómo guardar mejor los datos, pero no es necesario que le importe cómo se crean esos datos.

Este es un ejemplo de donde se podría usar el Patrón de Diseño de Estrategia . Su objeto myBob podría tener una instancia de la clase que lo escribirá. Es posible que desee que el escritor implemente una interfaz o se derive de una clase abstracta para que la rutina de guardado se pueda cambiar fácilmente.
Hoy está guardando en xml, pero es posible que también tenga que persistir el objeto en una base de datos. Este patrón te permitirá cambiar la rutina de guardado fácilmente. Incluso tendría la opción de cambiar la forma de ahorrar en tiempo de ejecución.

Solía ​​preferir la opción 2; sin embargo, como comencé a tratar de entender y modelar los dominios en los que estoy trabajando, prefiero la opción 1.

Imagina si tus vehículos de modelado. ¿Por qué un vehículo sabría persistir? Puede saber cómo moverse, cómo comenzar y cómo detenerse, pero qué es Guardar dentro del contexto de un vehículo.

Otro método es utilizar el patrón de visitante. Haga que su objeto contenga un método de aceptación que pase por los miembros que desea procesar / serializar, y haga que un visitante sea su serializador. Siempre que actualice o cambie su serialización (texto plano a xml a binario a lo que sea), no necesita actualizar el objeto.

Hemos tenido buenas experiencias en el trabajo haciéndolo de esta manera. Es bastante poderoso.

Hacer esto:

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

Obviamente, esta no es una solución completa, pero debería tener una idea general.

Creo que el enfoque correcto es el caso 1, pero su clase puede definirse de esta manera para aprovechar ambos enfoques:

 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); } //... } 

Esto podría modificarse fácilmente para colocar la lógica de escritura y la inicialización en una clase base, de modo que la clase Bob sea aún más clara e independiente de la persistencia.