タイプセーフティに違反することなく、不透明なオブジェクトを発信者に返す

StackOverflow https://stackoverflow.com/questions/2922458

  •  05-10-2019
  •  | 
  •  

質問

現在の状態のスナップショットを返す方法と、その状態を復元する別の方法があります。

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

状態 Snapshot クラスは、目に見えるメソッドやプロパティではありませんが、そのプロパティを内部に見える必要がありますが、発信者に完全に不透明である必要があります MachineModel クラス。私は明らかにダウンキャスティングによってこれを行うことができました、すなわち CurrentSnapshot 返します object, 、そして持っています RestoreSnapshot 受け入れます object それがaに戻す議論 Snapshot.

しかし、そのように強制されたキャスティングは私を汚します。タイプセーフとオパークの両方を可能にする最高の代替デザインは何ですか?

ソリューションで更新します:

私は、受け入れられた答えとインターフェイスについての提案の組み合わせをしました。 Snapshot クラスは公開抽象クラスになり、内部にプライベートな実装があります 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);
    }
}

コンストラクターと方法があるからです Snapshot それは internal, 、アセンブリ外からの発信者は、それを完全に不透明であると見なし、そこから継承することはできません。アセンブリ内の発信者は電話をかけることができます Snapshot.Restore それよりも MachineModel.Restore, 、しかし、それは大きな問題ではありません。さらに、実際には、実装することはできません Snapshot.Restore アクセスなし MachineModelのプライベートメンバーは、人々がそうしようとすることを思いとどまらせるべきです。

役に立ちましたか?

解決

依存関係を逆転させ、スナップショットをマシネモデルの子供(ネストされたクラス)にすることができます。その後、スナップショットにはパブリック(または内部)のみがあります Restore() パラメーターとしてMachineModelのインスタンスを取得する方法。スナップショットはMachineModelの子供として定義されるため、MachineModelのプライベートフィールドを見ることができます。

州を復元するには、以下の例に2つのオプションがあります。 snapshot.restorestate(machinemodel)または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);
    }
}

例:

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

* snapshot.restorestate()を持っていることは厄介です。 internal すべての発信者をアセンブリの外に置くため、復元を行う唯一の方法はmachinemodel.restorestate()経由です。しかし、あなたはジョンの答えで、同じアセンブリ内に発信者がいるので、それほど意味はありません。

他のヒント

できる MachineModelSnapshot 同じアセンブリにいて、別のアセンブリの発信者になりますか?もしそうなら、 Snapshot 公開クラスかもしれませんが、完全に内部のメンバーがいます。

私は明らかにダウンキャスティングによってこれを行うことができます。つまり、currentsnapshotがオブジェクトを返し、復元してスナップショットに戻るオブジェクトの引数を受け入れることができます。

問題は、誰かがオブジェクトのインスタンスを渡すことができるということです。 Snapshot.

紹介する場合 インターフェース ISnapshot メソッドが公開されず、1つの実装のみが存在します。ダウンキャストの価格でタイプセーフティをほぼ確保できます。

誰かが別の実装を作成するのを完全に妨げることができないので、私はほとんど言います ISnapshot そしてそれを渡し、それは壊れます。しかし、私はそれが望ましいレベルの情報を隠すことを提供すべきだと思います。

これは古い質問ですが、私は非常によく似たものを探していました、そして私はここで、ここで報告された情報の間に終わりました、そして、私がこの解決策を思いついた他のいくつかの間に私がこの解決策を思いつきました、多分少し過剰ですが、この方法で状態オブジェクトは完全に不透明です、アセンブリレベルでも

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);
    }
}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top