2009-07-08 11 views
6

Supongamos que tenemos un objeto que representa la configuración de una pieza de hardware. Por el bien de la discusión, un controlador de temperatura (TempController). Contiene una propiedad, la temperatura de consigna.La forma orientada a objetos para separar el modelo de su representación

Necesito guardar esta configuración en un archivo para usar en otro dispositivo. El formato de archivo (FormatA) está configurado en piedra. No quiero que el objeto TempController sepa sobre el formato de archivo ... simplemente no es relevante para ese objeto. Así que hago otro objeto, "FormatAExporter", que transforma TempController en el resultado deseado.

Un año más tarde fabricamos un nuevo controlador de temperatura, llamémoslo "AdvancedTempController", que no solo tiene un punto de referencia sino que también tiene control de velocidad, lo que significa una o dos propiedades más. También se inventa un nuevo formato de archivo para almacenar esas propiedades ... llamémoslo FormatB.

Ambos formatos de archivo son capaces de representar ambos dispositivos (supongamos que AdvancedTempController tiene valores predeterminados razonables si carece de configuraciones).

Este es el problema: sin utilizar 'isa' o alguna otra forma de "trampa" para averiguar qué tipo de objeto tengo, ¿cómo puede FormatBExporter manejar ambos casos?

Mi primer instinto es tener un método en cada controlador de temperatura que pueda proporcionar un cliente exportador para esa clase, por ejemplo, TempController.getExporter() y AdvancedTempController.getExporter(). Esto no es compatible con varios formatos de archivo.

El único otro enfoque que me viene a la mente es tener un método en cada controlador de temperatura que devuelva una lista de propiedades y sus valores, y luego el formateador puede decidir cómo generarlos. Funcionaría, pero parece complicado.

ACTUALIZACIÓN: En el futuro, este último enfoque no funciona bien. Si todos sus tipos son simples, podría ser así, pero si sus propiedades son Objetos, termina empujando el problema a un nivel inferior ... se ve obligado a devolver un par de valores de Cadena, Objeto, y el exportador tendrá que saber qué los Objetos en realidad deben hacer uso de ellos. Entonces solo lleva el problema a otro nivel.

¿Hay alguna sugerencia de cómo puedo mantener esto flexible?

Respuesta

4

Lo que puede hacer es dejar que los TempControllers sean responsables de persistir utilizando un archivador genérico.

class TempController 
{ 
    private Temperature _setPoint; 
    public Temperature SetPoint { get; set;} 

    public ImportFrom(Archive archive) 
    { 
     SetPoint = archive.Read("SetPoint"); 
    } 
    public ExportTo(Archive archive) 

    { 
     archive.Write("SetPoint", SetPoint); 
    } 
} 

class AdvancedTempController 
{ 
    private Temperature _setPoint; 
    private Rate _rateControl; 
    public Temperature SetPoint { get; set;} 
    public Rate RateControl { get; set;} 

    public ImportFrom(Archive archive) 
    { 
     SetPoint = archive.Read("SetPoint"); 
     RateControl = archive.ReadWithDefault("RateControl", Rate.Zero); 
    } 

    public ExportTo(Archive archive) 
    { 
     archive.Write("SetPoint", SetPoint); 
     archive.Write("RateControl", RateControl); 
    } 
} 

Al mantener de esta manera, los controladores no les importa cómo se almacenan los valores reales pero todavía se están manteniendo los componentes internos del objeto así encapsulado.

Ahora puede definir una clase de archivo abstracta que todas las clases de archivo pueden implementar.

abstract class Archive 
{ 
    public abstract object Read(string key); 
    public abstract object ReadWithDefault(string key, object defaultValue); 
    public abstract void Write(string key); 
} 

Formata archivador puede hacerlo de una manera, y archivo FormatB puede hacerlo de otra.

class FormatAArchive : Archive 
{ 
    public object Read(string key) 
    { 
     // read stuff 
    } 

    public object ReadWithDefault(string key, object defaultValue) 
    { 
     // if store contains key, read stuff 
     // else return default value 
    } 

    public void Write(string key) 
    { 
     // write stuff 
    } 
} 

class FormatBArchive : Archive 
{ 
    public object Read(string key) 
    { 
     // read stuff 
    } 

    public object ReadWithDefault(string key, object defaultValue) 
    { 
     // if store contains key, read stuff 
     // else return default value 
    } 

    public void Write(string key) 
    { 
     // write stuff 
    } 
} 

Puede agregar otro tipo de Controlador y pasarlo a cualquier formateador. También puede crear otro formateador y pasarlo a cualquier controlador.

+0

Esto también hacer el código más comprobable, ya que a causa mucho más fácil burlarse de los colaboradores. –

+0

Es un toque más elaborado de lo que había planeado, pero me gusta. Proporciona la flexibilidad de devolver todas las propiedades de una lista, pero conservando la información de tipo en toda la interfaz. Y definitivamente ayuda a la capacidad de prueba. –

0

Tendría el "controlador de temperatura", a través de un método getState, devuelve un mapa (por ejemplo, en Python a dict, en Javascript un objeto, en C++ a std :: map o std :: hashmap, etc., etc.) de sus propiedades y valores actuales, ¿qué tiene de complicado? Difícilmente podría ser más simple, es totalmente extensible y totalmente desacoplado del uso que se le da (visualización, serialización, & c).

0

Si FormatBExporter toma un AdvancedTempController, puede hacer una clase puente que haga que TempController se conforme con AdvancedTempController. Sin embargo, es posible que necesite agregar algún tipo de función getFormat() a AdvancedTempController.

Por ejemplo:

FormatBExporter exporterB; 
TempController tempController; 
AdvancedTempController bridged = TempToAdvancedTempBridge(tempController); 

exporterB.export(bridged); 

También existe la opción de usar un esquema de asignación de clave-valor. FormatAExporter exporta/importa un valor para la clave "setpoint". FormatBExporter exporta/importa valores para las claves "setpoint" y "ratecontrol". De esta forma, el antiguo FormatAExporter aún puede leer el nuevo formato de archivo (simplemente ignora "ratecontrol") y FormatBExporter puede leer el formato de archivo anterior (si falta "ratecontrol", usa un formato predeterminado).

0

Bueno, mucho de eso depende de los formatos de archivo de los que estás hablando.

Si se basan en combinaciones de clave/valor (incluidas anidadas, como xml), tener algún tipo de objeto de memoria intermedia que se escriba libremente y pueda escribirse en el formato de archivo apropiado es una buena manera de hacerlo eso.

Si no es así, tiene un escenario en el que tiene cuatro combinaciones de objetos y formatos de archivo, con lógica personalizada para cada escenario. En ese caso, puede que no sea posible tener una sola representación para cada formato de archivo que pueda tratar con cualquiera de los controladores. En otras palabras, si no puede generalizar el escritor de formato de archivo, no puede generalizarlo.

No me gusta la idea de que los controladores tengan exportadores. Simplemente no soy fanático de los objetos que saben sobre mecanismos de almacenamiento y otras cosas (pueden saber sobre el concepto de almacenamiento, y tienen una instancia específica dada a ellos a través de algún mecanismo DI). Pero creo que estás de acuerdo con eso, y por las mismas razones.

0

En el modelo OO, el método del objeto como colectivo es el controlador. Es más útil separar su programa en M y V y no tanto en C si está programando con OO.

1

En C# y otros lenguajes que soportan esto se puede hacer esto:

class TempController { 
    int SetPoint; 
} 
class AdvancedTempController : TempController { 
    int Rate; 
} 

class FormatAExporter { 
    void Export(TempController tc) { 
     Write(tc.SetPoint); 
    } 
} 

class FormatBExporter { 
    void Export(TempController tc) { 
     if (tc is AdvancedTempController) { 
      Write((tc as AdvancedTempController).Rate); 
     } 
     Write(tc.SetPoint); 
    } 
} 
+1

De acuerdo, esta es la forma más fácil a corto plazo. Pero tou usó 'es' y 'como'. Ahora debe mantener FormatBExporter cada vez para manejar todos los tipos. Cuando se lo lleve al extremo, tendrá un bosque de afirmaciones 'if' para descubrir cuál es el tipo, y el objetivo de estos tipos fue hacer este tipo de cosas ellos mismos. –

Cuestiones relacionadas