当前位置:编程学习 > 网站相关 >>

类型安全的黑板模式(属性包)

有时候对于对象来说。在一个软件中,不直接通过互相引用而做到共享信息是非常有用的。比如像带有插件的软件。可以互相进行通信。假设我们有了很多对象。其中一些包含一些数据。而另一些对象需要消费这些数据 不同的子集,我们不通过对数据生产者和消费者的直接引用来实现,而是通过更低耦合的方式。叫做创建一个“BlackBoard”(黑板)对象。该对象允许其他对象自由对其进行读取/写入数据。这种解耦方式使得消费者不知道也不必知道数据来自哪里。如果想要了解更多关于黑板模式的信息。我们常说的。Google是你最好的朋友。

  一个最简单的黑板对象应该是 Dictionary一些简单的命名值的字典。所有的对象共享同一个字典引用。使得他们可以交换这些命名数据。这种方法有两个问题。一个是名字。一个是类型安全—数据生产者和消费者对每一个数据值都必须共享一个字符串标识。消费者也没有对字典中的值进行编译时的类型检查,比如,可能期望一个小数,结果运行时读到了字符串。本文对这两个问题演示了一种解决方案。

背景

  最近我在开发一个通用任务的异步执行的引擎。我的通用任务通常有Do/Undo方法。原则上是相互独立的,但是有一些任务需要从已经执行的任务重请求数据。比如。一个任务可以
为一个硬件设备建立一个API,随后的任务就可以使用创建好的API来操作硬件设备。但是。我不想我的执行引擎知道关于这个执行任务的任何信息。而且。我也不想直接手工的就在一个任务里引用另一个任务。

黑板类

  黑板类本质上是一个Dictionary的包装类,对外暴露Get和Set方法。黑板类允许其他对象存储并且取回数据。但是要求这些数据使用一个
BlackboardProperty 类型的标识符来表示这些数据是可存取的。BlackboardProperty 对象应该在那些准备读写黑板类的对象之间共享,因此,他应该在那些类中作为一个静态成员。(很像WPF的依赖属性。是他们所属控件的静态成员)

注意:命名安全应该可以通过同样的方式实现。但是但是依然没有解决类型安全的问题。那么。到了主要的部分了。那就是黑板类的代码了


public class Blackboard : INotifyPropertyChanged, INotifyPropertyChanging
{
    Dictionary<string, object> _dict = new Dictionary<string, object>();

    public T Get<T>(BlackboardProperty<T> property)
    {
        if (!_dict.ContainsKey(property.Name))
            _dict[property.Name] = property.GetDefault();
        return (T)_dict[property.Name];
    }

    public void Set<T>(BlackboardProperty<T> property, T value)
    {
        OnPropertyChanging(property.Name);
        _dict[property.Name] = value;
        OnPropertyChanged(property.Name);
    }

    #region property change notification

    public event PropertyChangingEventHandler PropertyChanging;

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanging(string propertyName)
    {
        if (PropertyChanging != null)
            PropertyChanging(this, new PropertyChangingEventArgs(propertyName));
    }

    protected virtual void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion
}

黑板属性(BlackBoardProperty)类

  BlackBoardProperty 类 提供了一个标识符来存取黑板对象中的数据。定义了名称和值的类型。也定义了一个默认的返回值。以防黑板类中对应属性没有值。


/// <summary>
/// 对应黑板类中的属性的强类型标识符
/// </summary>
/// <typeparam name="T">该类能识别的属性值的类型</typeparam>
public class BlackboardProperty<T>
{
    /// <summary>
    /// 属性的名称
    /// <remarks>
    /// 黑板类的属性通过名称来存储。请注意不要让相同的名字有不同的属性值。因为如果被用在同样的黑板类上。他们会互相覆盖值
    /// </remarks>
    /// </summary>
    public string Name { get; set; }

//当黑板对象没有包含对应属性的时候。该工厂方法被用来提供一个默认的值
    //   
Func<T> _createDefaultValueFunc;

    public BlackboardProperty(string name)
        : this(name, default(T))
    {
    }

    /// <summary>
    ///
    /// </summary>
    /// <param name="name"></param>
    /// <param name="defaultValue">
    /// 当黑板类不包括该属性的时候。该值会被返回。
    /// <remarks>
    /// 如果缺省的值是一个常量或是一个值类型的时候,使用该构造方法。
    /// </remarks>
    /// </param>
    public BlackboardProperty(string name, T defaultValue)
    {
        Name = name;
        _createDefaultValueFunc = () => defaultValue;
    }

    /// <summary>
    /// </summary>
    /// <remarks>
/// 如果缺省值是一个引用类型,并且,你不想要共享该实例给多个黑板对象的时候。请使用该
/// 构造函数
    /// </remarks>
    /// <param name="name"></param>
    /// <param name="createDefaultValueFunc"></param>
    public BlackboardProperty(string name, Func<T> createDefaultValueFunc)
    {
        Name = name;
        _createDefaultValueFunc = createDefaultValueFunc;
    }

    public BlackboardProperty()
    {
        Name = Guid.NewGuid().ToString();
    }

    public T GetDefault()
    {
        return _createDefaultValueFunc();
    }
}

我承认不是非常有用的代码。但是。能够模拟两个类的使用。
下一个例子会更和现实情况接近。但是肯定是被简化过了的。在下面的例子里。我定义了集中不同的任务。我用这些任务来启动对硬件设备的连接。操作设备。关闭连接。这些任务通过一个执行引擎依次执行,这些任务通过一个公用的黑板类来共享数据。至于这个任务类的和执行引擎(ExecutionEngine)类还是留到另一篇文章中把。


//一个设备的接口例子
interface IDevice
{
    void Connect();
    void Reset();
    decimal Read(string obis);
    void Close();
}

//任务实例化了设备api,设置并且启动连接
class InitiateDeviceTask : Task
{
    //这个用来定义黑板类里的DeviceAPI 变量
    public static BlackboardProperty<IDevice> DeviceAPIProperty = new BlackboardProperty<IDevice>();

    protected override void Execute(Blackboar

补充:综合编程 , 安全编程 ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,