У меня есть метод, который должен возвратить снимок текущего состояния и другой метод, который восстанавливает то состояние.
public class MachineModel
{
public Snapshot CurrentSnapshot { get; }
public void RestoreSnapshot (Snapshot saved) { /* etc */ };
}
Состояние Snapshot
класс должен быть абсолютно непрозрачным вызывающей стороне - никакие видимые методы или свойства - но его свойства не должны быть видимы в MachineModel
класс. Я мог, очевидно, сделать это downcasting, т.е. иметь 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
члены парламента, не занимающие официального поста, которые должны отговорить людей от попытки сделать так.
Вы можете отменить зависимость и сделать Snapshot дочерним (вложенным классом) MachineModel. Тогда Snapshot имеет только открытый (или внутренний) метод 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
может быть открытым классом, но с полностью внутренними членами.
Очевидно, я мог бы сделать это, понижающее преобразование, т.е. иметь CurrentSnapshot вернуть объект и иметь RestoreSnapshot принимает объект аргумент, который он возвращает Снимок.
Проблема в том, что кто-то может затем передать экземпляр объекта, который не является снимком
.
Если вы представите интерфейс ISnapshot
, который не предоставляет никаких методов, а существует только одна реализация, вы можете почти гарантировать безопасность типов ценой понижающего преобразования.
Я говорю почти, потому что вы не можете полностью запретить кому-либо создать другую реализацию ISnapshot
и передать ее, что приведет к поломке. Но я чувствую, что это должно обеспечить желаемый уровень сокрытия информации.