2010-05-27 12 views
6

Tengo un método que debe devolver una instantánea del estado actual y otro método que restablece ese estado.Devolver un objeto opaco a la persona que llama sin violar la seguridad de tipo

public class MachineModel 
{ 
    public Snapshot CurrentSnapshot { get; } 
    public void RestoreSnapshot (Snapshot saved) { /* etc */ }; 
} 

La clase Snapshot estado debe ser completamente opaca a la persona que llama - no hay métodos o propiedades visibles - pero sus propiedades tienen que ser visibles dentro de la clase MachineModel. Obviamente, podría hacer esto mediante downcasting, es decir, tener CurrentSnapshot devolver un object, y tener RestoreSnapshot aceptar un argumento object que devuelve a Snapshot.

Pero la fundición forzada de esa manera me hace sentir sucio. ¿Cuál es el mejor diseño alternativo que me permite ser seguro y opaco?

actualización con solución:

terminé haciendo una combinación de la respuesta aceptada y la sugerencia acerca de las interfaces. La clase Snapshot se hizo una clase abstracta pública, con una puesta en práctica privada en el interior MachineModel:

public class MachineModel 
{ 
    public abstract class Snapshot 
    { 
     protected internal Snapshot() {} 
     abstract internal void Restore(MachineModel model); 
    } 

    private class SnapshotImpl : Snapshot 
    { 
     /* etc */ 
    } 

    public void Restore(Snapshot state) 
    { 
     state.Restore(this); 
    } 
} 

Debido a que el constructor y los métodos de Snapshot son internal, las personas que llaman desde el exterior del conjunto lo ven como una completamente opaca y no puede heredar de ella . Las personas que llaman dentro del conjunto pueden llamar al Snapshot.Restore en lugar de a MachineModel.Restore, pero eso no es un gran problema. Además, en la práctica, nunca podría implementar Snapshot.Restore sin acceso a los miembros privados de MachineModel, lo que debería disuadir a las personas de tratar de hacerlo.

Respuesta

2

Puede invertir la dependencia y hacer que Snapshot sea un elemento secundario (clase anidada) de MachineModel. Entonces Snapshot solo tiene un método público (o interno) Restore() que toma como parámetro una instancia de MachineModel. Como Snapshot se define como hijo de MachineModel, puede ver los campos privados de MachineModel.

Para restablecer el estado, tiene dos opciones en el siguiente ejemplo. Puedes llamar a Snapshot.RestoreState (MachineModel) o MachineModel.Restore (Snapshot) *.

public class MachineModel 
{ 
    public class Snapshot 
    { 
     int _mmPrivateField; 

     public Snapshot(MachineModel mm) 
     { 
      // get mm's state 
      _mmPrivateField = mm._privateField; 
     } 

     public void RestoreState(MachineModel mm) 
     { 
      // restore mm's state 
      mm._privateField = _mmPrivateField; 
     } 
    } 

    int _privateField; 

    public Snapshot CurrentSnapshot 
    { 
     get { return new Snapshot(this); } 
    } 

    public void RestoreState(Snapshot ss) 
    { 
     ss.Restore(this); 
    } 
} 

Ejemplo:

MachineModel mm1 = new MachineModel(); 
    MachineModel.Snapshot ss = mm1.CurrentSnapshot; 
    MachineModel mm2 = new MachineModel(); 
    mm2.RestoreState(ss); 

* Sería más ordenado tener Snapshot.RestoreState() como internal y poner todas las llamadas fuera de la asamblea, por lo que la única manera de hacerlo es a través de una restauración MachineModel. RestoreState(). Pero mencionó en la respuesta de Jon que habrá personas que llaman dentro de la misma asamblea, por lo que no tiene mucho sentido.

3

¿Pueden MachineModel y Snapshot estar en el mismo conjunto y las personas que llaman en un conjunto diferente? Si es así, Snapshot podría ser una clase pública pero con miembros completamente internos.

+0

'MachineModel' tendrá llamantes tanto desde dentro del mismo conjunto como desde ensamblajes externos. –

3

pude hacer esto, obviamente, por downcasting, es decir, tiene CurrentSnapshot devolver un objeto, y tienen RestoreSnapshot aceptar un objeto argumento que se proyecta hacia atrás a una instantánea .

El problema es que alguien podría pasar una instancia de un objeto que no es Snapshot.

Si se introduce una interfazISnapshot que expone ningún método, y sólo existe una implementación , casi se puede garantizar la seguridad de tipos en el precio de un abatido.

Digo casi, porque no se puede evitar completamente que alguien cree otra implementación de ISnapshot y la pase, lo que se rompería. Pero siento que eso debería proporcionar el nivel deseado de ocultación de información.

+0

Es curioso, acabo de escribir el siguiente código y tenía curiosidad por saber cómo lo escribirían los demás, así que busqué en Google. Hice exactamente lo que dijiste ... –

Cuestiones relacionadas