Верните непрозрачный объект к абонеру, не нарушая безопасность типа

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 аргумент, который он бросает обратно к 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.

Чтобы восстановить состояние, у вас есть два варианта в примере ниже. Вы можете позвонить на 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 (). Но вы упомянули на ответе Джона, что в той же сборке будут звонящие в то же самое, поэтому нет особой точки.

Другие советы

Могу MachineModel а также Snapshot быть в одной и той же сборке, а также абоненты в другой сборке? Если так, Snapshot может быть публичным классом, но с совершенно внутренними членами.

Я мог бы, очевидно, сделать это путем Dreaking, т.е. есть CURRORTSNAPSHOT вернуть объект, и у вас RESTORESSNAPSHOT принять аргумент объекта, который он бросил обратно к снижению.

Проблема в том, что кто-то может затем пройти экземпляр объекта, который не является Snapshot.

Если вы представите интерфейс ISnapshot Что не дает никаких методов, и существует только одна реализация, вы можете почти обеспечить безопасность типа по цене нисходящего.

Я почти говорю, потому что вы не можете полностью помешать кому-то создать еще одну реализацию 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