2010-05-27 10 views
6

Ho un metodo che dovrebbe restituire un'istantanea dello stato corrente e un altro metodo che ripristina quello stato.Restituisce un oggetto opaco al chiamante senza violare la sicurezza del tipo

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

La classe stato Snapshot dovrebbe essere completamente opaca al chiamante - metodi né proprietà visibili - ma le sue proprietà devono essere visibili all'interno della classe MachineModel. Potrei ovviamente farlo downcasting, ovvero avere CurrentSnapshot restituire un object e avere RestoreSnapshot accettare un argomento object che restituisce a Snapshot.

Ma il casting forzato mi fa sentire sporco. Qual è il miglior design alternativo che mi consente di essere al sicuro e opaco?

Aggiornamento con la soluzione:

Finii per fare una combinazione della risposta accettata e la suggestione sulle interfacce. La classe Snapshot è stata fatta una classe astratta pubblica, con un'implementazione privato all'interno 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); 
    } 
} 

Perché il costruttore e metodi di Snapshot sono internal, i chiamanti esterni alla assemblea lo vedono come un completamente opaco e non può ereditare da essa . I chiamanti all'interno dell'assemblea potrebbero chiamare Snapshot.Restore anziché MachineModel.Restore, ma non è un grosso problema. Inoltre, in pratica non è mai possibile implementare Snapshot.Restore senza accesso ai membri privati ​​di MachineModel, che dovrebbero dissuadere le persone dal provare a farlo.

risposta

2

È possibile invertire la dipendenza e rendere Snapshot un figlio (classe nidificata) di MachineModel. Quindi Snapshot ha solo un metodo pubblico (o interno) Restore() che accetta come parametro un'istanza di MachineModel. Poiché Snapshot è definito come figlio di MachineModel, può vedere i campi privati ​​di MachineModel.

Per ripristinare lo stato, sono disponibili due opzioni nell'esempio seguente. Puoi chiamare Istantanea.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); 
    } 
} 

Esempio:

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

* Sarebbe neater avere Snapshot.RestoreState() come internal e mettere tutti i chiamanti fuori del complesso, quindi l'unico modo per fare un ripristino è tramite MachineModel. RestoreState(). Ma hai detto sulla risposta di Jon che ci saranno dei chiamanti all'interno della stessa assemblea, quindi non ha molto senso.

3

Can MachineModel e Snapshot essere nello stesso assembly e chiamanti in un assembly diverso? In tal caso, Snapshot potrebbe essere una classe pubblica ma con membri interamente interni.

+0

'MachineModel' avrà chiamanti sia dallo stesso gruppo che da gruppi esterni. –

3

ho potuto ovviamente fare questo downcasting, vale a dire hanno CurrentSnapshot restituire un oggetto, e hanno RestoreSnapshot accettare un oggetto argomento che si getta di nuovo ad un Snapshot.

Il problema è che qualcuno potrebbe passare un'istanza di un oggetto che non è Snapshot.

Se si introduce un un'interfacciaISnapshot che espone alcun metodo, ed esiste solo un'implementazione, si può quasi garantire tipo di sicurezza al prezzo di un abbattuto.

Dico quasi, perché non è possibile impedire completamente a qualcuno di creare un'altra implementazione di ISnapshot e passarla, che si interromperà. Ma sento che dovrebbe fornire il livello desiderato di nascondimento delle informazioni.

+0

Divertente, ho appena scritto il codice qui sotto ed ero curioso di sapere come avrebbero scritto gli altri, quindi ho cercato su Google. Ho fatto esattamente quello che hai detto .. –

Problemi correlati