C#中需要的某种创建模式
我有以下类型:
// incomplete class definition public class Person { private string name; public string Name { get { return this.name; } } }
我希望使用某种专用的控制器/生成器来创建和更新此类型,但是我希望它对于其他类型保持只读。
该对象还需要在每次由其控制器/构建器更新事件时触发一个事件。
总结一下,根据前面的类型定义框架:
- " Person"只能由特定的控制器实例化
- 该控制器可以随时更新"人员"("姓名"字段)的状态
- "人员"需要在发生时向世界其他地方发送通知
- 所有其他类型应该只能读取
Person
属性
我应该如何实施呢?我在这里谈论的是控制器/构建器,但是所有其他解决方案都可以使用。
注意:我可以依靠internal
修饰符,但是理想情况下,我所有的东西都应该放在同一程序集中。
解决方案
回答
我喜欢有一个只读界面。然后,构建者/控制器/任何人都可以直接引用该对象,但是当将此对象暴露于外部时,我们仅显示界面。
回答
使用接口IPerson和嵌套类:
public class Creator { private class Person : IPerson { public string Name { get; set; } } public IPerson Create(...) ... public void Modify(IPerson person, ...) { Person dude = person as Person; if (dude == null) // wasn't created by this class. else // update the data. } }
回答
创建一个接口IReadOnlyPerson,该接口仅公开get访问器。让Person实现IReadOnlyPerson。将对Person的引用存储在控制器中。仅向其他客户端提供只读版本。
与大多数OO功能一样,这可以防止错误,但不能防止欺诈。如果客户端碰巧知道(或者怀疑)IReadOnlyPerson由Person实现,则可以将运行时强制转换为Person。
根据评论更新:
只读接口也可以公开事件委托,就像其他任何对象一样。 C语言中通常使用的惯用法不会阻止客户端弄乱侦听器列表,但是约定只是添加侦听器,因此应该足够了。在任何具有状态改变副作用的集合访问器或者函数中,只需在事件为null(无侦听器)的情况下使用守护程序调用事件委托即可。
回答
也许是这样吗?
public class Person { public class Editor { private readonly Person person; public Editor(Person p) { person = p; } public void SetName(string name) { person.name = name; } public static Person Create(string name) { return new Person(name); } } protected string name; public string Name { get { return this.name; } } protected Person(string name) { this.name = name; } } Person p = Person.Editor.Create("John"); Person.Editor e = new Person.Editor(p); e.SetName("Jane");
不漂亮,但我认为它可行。另外,我们可以在编辑器上使用属性而不是SetX方法。
回答
我认为"内部"是最简单,最好的方法(当然,这涉及多个程序集)。只需执行一些开销大的堆栈遍历来确定属性设置器中的调用方,我们可以尝试:
interface IPerson { Name { get; set; } }
并明确实现此接口:
class Person : IPerson { Name { get; private set; } string IPerson.Name { get { return Name; } set { Name = value; } } }
然后在构建器中执行显式的接口强制转换以设置属性。尽管这样做确实可以强调意图,但这仍然不能保护实现,也不是一个好的解决方案。
在属性设置器中,我们将必须实现事件通知。我自己解决这个问题,我不会为每个属性创建单独的事件和事件处理程序,而是创建一个PropertyChanged事件并在发生更改时在每个属性中触发它(其中事件参数将包括属性名称,旧值和新值)。
回答
似乎很奇怪,尽管我不能更改Person对象的名称,但是我可以简单地抓住它的控制器并在那里进行更改。这不是保护对象数据的好方法。
但是,尽管如此,这是一种实现方法:
/// <summary> /// A controlled person. Not production worthy code. /// </summary> public class Person { private string _name; public string Name { get { return _name; } private set { _name = value; OnNameChanged(); } } /// <summary> /// This person's controller /// </summary> public PersonController Controller { get { return _controller ?? (_controller = new PersonController(this)); } } private PersonController _controller; /// <summary> /// Fires when <seealso cref="Name"/> changes. Go get the new name yourself. /// </summary> public event EventHandler NameChanged; private void OnNameChanged() { if (NameChanged != null) NameChanged(this, EventArgs.Empty); } /// <summary> /// A Person controller. /// </summary> public class PersonController { Person _slave; public PersonController(Person slave) { _slave = slave; } /// <summary> /// Sets the name on the controlled person. /// </summary> /// <param name="name">The name to set.</param> public void SetName(string name) { _slave.Name = name; } } }