Question

J'ai une méthode qui doit retourner un instantané de l'état actuel, et une autre méthode qui restaure cet état.

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

La classe Snapshot de l'Etat doit être complètement opaque à l'appelant - aucune méthode visibles ou propriétés - mais ses propriétés doivent être visibles dans la classe MachineModel. Je ne pouvais évidemment faire par coulée en descente, à savoir un retour CurrentSnapshot A des object, et ont RestoreSnapshot acceptent un argument de object lequel il jette de nouveau à un Snapshot.

Mais la coulée forcée comme ça me fait me sentir sale. Quelle est la meilleure conception alternative qui me permet d'être à la fois de type sécurisé et opaque?

Mise à jour avec une solution :

Je liquidée faire une combinaison de la réponse acceptée et la suggestion sur les interfaces. La classe Snapshot a fait une classe abstraite publique, avec une mise en œuvre privée à l'intérieur 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);
    }
}

Parce que le constructeur et les méthodes de Snapshot sont internal, les appelants de l'extérieur voir l'ensemble comme un complètement opaque et ne peuvent pas hériter de lui. Les appelants au sein de l'assemblée pourrait appeler Snapshot.Restore plutôt que MachineModel.Restore, mais ce n'est pas un gros problème. De plus, dans la pratique on ne pouvait pas mettre en œuvre Snapshot.Restore sans accès aux membres privés de MachineModel, ce qui devrait dissuader les gens d'essayer de le faire.

Était-ce utile?

La solution

Vous pourriez inverser la dépendance et faire Snapshot un enfant (classe imbriquée) de MachineModel. Puis instantané n'a qu'un public (ou interne) Méthode de Restore() qui prend comme paramètre une instance de MachineModel. Parce que Snapshot est défini comme un enfant de MachineModel, il peut voir des champs privés de MachineModel.

Pour restaurer l'état, vous avez deux options dans l'exemple ci-dessous. Vous pouvez appeler Snapshot.RestoreState (MachineModel) ou 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);
    }
}

Exemple:

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

* Il serait plus propre d'avoir Snapshot.RestoreState () comme internal et de mettre tous les appelants en dehors de l'assemblée, la seule façon de faire une restauration est via MachineModel.RestoreState (). Mais vous avez mentionné sur la réponse de Jon qu'il y aura des appels à l'intérieur du même ensemble, donc il n'y a pas beaucoup.

Autres conseils

Can MachineModel et Snapshot être dans le même ensemble, et les appelants dans un assemblage différent? Si oui, Snapshot pourrait être une classe publique, mais avec des membres entièrement internes.

  

Je pourrais évidemment faire en   coulée en descente, à savoir Have CurrentSnapshot   renvoyer un objet, et ont   RestoreSnapshot accepter un objet   argument qu'il jette de nouveau à un   Instantané.

Le problème est que quelqu'un pourrait alors passer une instance d'un objet qui n'est pas Snapshot.

Si vous introduisez un Interface ISnapshot qui expose aucune méthode, et une seule mise en œuvre existe, vous pouvez presque garantir au prix d'un baissés de type sécurité.

Je dis presque, parce que vous ne pouvez pas empêcher complètement quelqu'un de créer une autre mise en œuvre de ISnapshot et de le transmettre, qui briserait. Mais je me sens comme cela devrait fournir le niveau souhaité de se cacher de l'information.

Ceci est une vieille question, mais je cherchais quelque chose de très similaire et j'ai fini ici et entre les informations présentées ici et un autre je suis venu avec cette solution, est peut-être un peu exagéré, mais cette façon l'objet d'état est complètement opaque, même au niveau de l'assemblage

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);
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top