Domanda

Ho un metodo che dovrebbe restituire una fotografia dello stato attuale, e un altro metodo che restituisce quello stato.

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

La classe Snapshot stato dovrebbe essere completamente opaca al chiamante - metodi né proprietà visibili - ma le sue proprietà devono essere visibili all'interno della classe MachineModel. Potrei ovviamente fare questo downcasting, vale a dire Hanno CurrentSnapshot restituire un object, e hanno RestoreSnapshot accettare un argomento object che si getta di nuovo ad un Snapshot.

Ma fusione forzata del genere mi fa sentire sporca. Qual è il miglior design alternativo che mi permette di essere sia di tipo-sicuro e opachi?

Aggiornamento con la soluzione :

I liquidata facendo una combinazione di risposta accettata e la suggestione sulle interfacce. La classe Snapshot è stata fatta una classe astratta pubblica, con un MachineModel implementazione all'interno privato:

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

Poiché il costruttore e metodi di Snapshot sono internal, chiamanti esterni al complesso vedono come un completamente opaco e non possono ereditare da esso. I chiamanti in seno all'Assemblea potevano chiamare Snapshot.Restore piuttosto che MachineModel.Restore, ma non è un grosso problema. Inoltre, in pratica non si poteva mai implementare Snapshot.Restore senza accesso ai membri privati ??di MachineModel, che dovrebbe dissuadere le persone dal cercare di farlo.

È stato utile?

Soluzione

Si potrebbe invertire la dipendenza e fare Snapshot un bambino (classe annidata) di MachineModel. Poi istantanea ha solo pubblica (o interna) Metodo Restore() che prende come parametro un'istanza di MachineModel. Perché Snapshot è definito come un bambino di MachineModel, può vedere i campi privati ??di MachineModel.

Per ripristinare lo stato, si hanno due opzioni nel seguente esempio. È possibile chiamare 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);
    }
}

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 lei ha citato sulla risposta di Jon che ci sarà chiamanti all'interno dello stesso assieme, quindi non c'è molto senso.

Altri suggerimenti

Can MachineModel e Snapshot essere nella stessa assemblea, e chiamanti in un assembly diverso? Se è così, Snapshot potrebbe essere una classe pubblica, ma con i membri del tutto interne.

  

I potrebbe ovviamente fare questo   downcasting, cioè Avere CurrentSnapshot   restituire un oggetto, e hanno   RestoreSnapshot accettare un oggetto   argomento che si getta di nuovo ad un   Snapshot.

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

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

Dico quasi, perché non è possibile prevenire completamente a qualcuno di creare un'altra implementazione di ISnapshot e passarlo, che si rompono. Ma mi sento come che dovrebbe fornire il livello desiderato di nascondere informazioni.

Questa è una vecchia questione, ma ero alla ricerca di qualcosa di molto simile e ho finito qui e tra le informazioni ha riportato qui e qualche altra mi si avvicinò con questa soluzione, forse è un po 'eccessivo, ma in questo modo l'oggetto di stato è completamente opaco, anche a livello di assieme

class Program
{
    static void Main(string[] args)
    {
        DoSomething l_Class = new DoSomething();

        Console.WriteLine("Seed: {0}", l_Class.Seed);

        Console.WriteLine("Saving State");

        DoSomething.SomeState l_State = l_Class.Save_State();

        l_Class.Regen_Seed();

        Console.WriteLine("Regenerated Seed: {0}", l_Class.Seed);

        Console.WriteLine("Restoring State");

        l_Class.Restore_State(l_State);

        Console.WriteLine("Restored Seed: {0}", l_Class.Seed);

        Console.ReadKey();
    }
}

class DoSomething
{
    static Func<DoSomething, SomeState> g_SomeState_Ctor;

    static DoSomething()
    {
        Type type = typeof(SomeState);
        System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(type.TypeHandle);
    }

    Random c_Rand = new Random();

    public DoSomething()
    {
        Seed = c_Rand.Next();
    }

    public SomeState Save_State()
    {
        return g_SomeState_Ctor(this);
    }

    public void Restore_State(SomeState f_State)
    {
        ((ISomeState)f_State).Restore_State(this);
    }

    public void Regen_Seed()
    {
        Seed = c_Rand.Next();
    }

    public int Seed { get; private set; }

    public class SomeState : ISomeState
    {
        static SomeState()
        {
            g_SomeState_Ctor = (DoSomething f_Source) => { return new SomeState(f_Source); };
        }

        private SomeState(DoSomething f_Source) { Seed = f_Source.Seed; }

        void ISomeState.Restore_State(DoSomething f_Source)
        {
            f_Source.Seed = Seed;
        }

        int Seed { get; set; }
    }

    private interface ISomeState
    {
        void Restore_State(DoSomething f_Source);
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top