龙盟编程博客 | 无障碍搜索 | 云盘搜索神器
快速搜索
主页 > web编程 > asp.net编程 >

学习.NET架构设计系列:学用设计模式(4)

时间:2009-12-21 11:47来源:未知 作者:admin 点击:
分享到:
我们可以在设备中处理Signal子类的创建,这样也不是不可以。但是,如果我们采用一个工厂,由他来负责Signal对象的建立,这样就完全隔离了设备和信号的

我们可以在设备中处理Signal子类的创建,这样也不是不可以。但是,如果我们采用一个工厂,由他来负责Signal对象的建立,这样就完全隔离了设备和信号的每个子类的关系。设备在调用信号对象的时候,完全不需要知道这个实例是属于哪个类型。工厂的代码如下:

 

class SignalFactory
{
    
public static Signal CreateSignal(string dev, string sig)
    {
        
//获取信号的定义
        string sql = 
            
"SELECT * FROM SIG WHERE DEV='" + dev + "' AND SIG='" + sig + "'";
        
//查数据库表
        
        
//判断需要创建的类型
        Signal signal = null;
        
if (type == 'A'
        {
            signal 
= new AnalogSignal();
        }
        
else if (type == 'S'
        {
            signal 
= new StateSignal();
        }
        
else if (type == 'P')
        {
            signal 
= new PresumeSignal();
        }
        
else 
        {
            
return null;
        }
        
        
//设置Signal的配置参数
        signal.SetUnit(unit);
        signal.SetStateDescription(state_description);
        signal.SetPresumeFormule(presume_formule)
        
        
return signal;
    }
}

如果我们需要显示设备上的某个信号,这样就可以了:

 

Signal sig = SignalFactory.CreateSignal(dev, sig);
string s = sig.GetDisplayString();

其实,我们还可以采用一些小手段,比如利用反射的方式,彻底的把Signal的各个子类与其他的代码隔离开,甚至连SignalFactory都不需要和子类产生联系。我们可以把信号配置的数据修改一下:

TYPE字段原先设计的是一个标志(A、S和P),现在直接记录类型的命名空间和名称。SignalFactory在创建实例的时候,直接查出TYPE字段的内容,然后按照这个类的名称,就可以用反射的方式创建需要的实例。这样,无论是Signal的创建者,还是调用者,都不需要知道他们创建和调用的实际类型是哪一个,各种信号的数据和显示处理完全是由Signal的每个子类负责,程序就很好的符合了开放闭合原则。假如以后出现了一些很独特的信号采集和计算方式,甚至不得不采用硬编码的方式去实现,也不会对其他代码造成不良影响影响,维护起来非常的方便。

我们利用一个工厂解决了信号数据采集的问题,并且为下一步可能发生的变化留下了扩展的可能。下面看看告警应该怎样处理。我们先简单的考虑一下告警的形成:首先是在设备上采集到最新的实时数据,然后按照某个规则去判断这些数据是不是符合了告警的条件。在符合条件的情况下,在设备上面产生告警。在大部分情况下,一个告警只和一个设备有关,但是也有这样的情况:某个告警条件需要同时判断多个设备上的多个信号。于是我们设计出下面这样的结构:

告警的定义保存在告警定义数据表里面,如下:

表里面的CONDITIONA字段表示告警条件,这是一个公式,判断的时候把信号的数值代入,然后判断公式条件是否得到满足。如果满足条件,就产生一个告警。

程序的运行时序是这样:设备对象得到自己所包含的信号上的实时数据,然后找到与自己相关的每一个告警对象,依次调用他们的Judge方法。Judge方法根据告警条件公式判断是否有告警存在,如果存在的话,就把相关设备的HasAlarm属性设置为True。就这样,程序的主要功能实现了。

下面是这个程序的一个客户端界面:

界面的左侧是一个树,表示设备的分类关系,每个叶子表示一个设备;当鼠标在树上点击一个设备,右侧的列表视图上显示这个设备上的信号和采集数据;如果设备上有告警,对应的树节点要标示一个显著的颜色,并且状态栏上要显示最近的告警。

要实现界面的刷新,最简单的方式莫过于在窗体上设置一个定时器,每隔一段时间检查一下所有设备的HasAlarm属性,发现有告警的设备,就把这个设备在树上的图标换掉,然后再把告警的内容显示在状态栏上。但是这样做有一个缺点,定时器的时间间隔无论怎样设置都是不合适的,时间太长了,可能会有一些告警要很久才能显示出来;时间太短的话,可能很多次刷新都没有告警,白白的消耗资源。这种刷新的机制是不合理的。要解决这个问题,可以采用观察模式(Observer)。一个对象需要等待另一个对象发出一个消息,然后再采取响应措施,等待消息的对象不需要知道消息如何发生、何时发生,发出消息的对象也不需要知道谁会关注这个消息、如何响应。这种情况就可以采用观察模式。

使用C#实现观察模式有一种非常简单的方法,那就是事件。我们可以在设备上定义一个事件:告警。当设备的HasAlarm属性被设置的时候,他会检查参数,如果发现参数为True,就发出告警事件。设备的代码片段如下:

 

class Device
{
    
public event System.EventHandler Alarm;//定义告警事件
    
    
public Device()
    {
        
this.Alarm += new System.EventHandler(this.Device_Alarm);
    }
    
    
public void SetHasAlarm(bool has)
    {
        
if (has == true)
        {
            Alarm(
thisnull);//发出告警事件
        }
    }
    
    
private void Device_Alarm(object sender, EventArgs e)
    {
    }
}

当设备上产生告警的时候,设备对象会发出Alarm事件。界面上的树视图可以捕获这个事件,将对应的树节点图标设为红色;状态栏也可以捕获这个事件,把设备上的告警显示出来。比起定时轮循,这是一种更加合理高效的方式。

对象设计是不是合理,所参照的标准是这个设计是不是反映了业务需求的实际概念。要做到真实的反映业务需求,最根本的方法是要深刻的去理解需求,深入的探索业务人员的工作和思想,甚至去留意他们自己都无法用语言表达的思维环节。在一个涉及者众多的企业支撑系统中,这种情况很常见的。有经验的业务人员肯定会积累很多这样的思想,体现了这些思想的对象模型才是最优秀的。要让软件系统来帮助业务人员进行工作,也就必然无法回避这样的问题。合理的使用设计模式可以最大限度的降低系统的复杂程度,但是归根到底,复杂程度是由业务需求所决定的。当对象设计基本清晰之后,设计模式可以帮助设计人员更好的处理对象之间的复杂关系,建立一个更加简单、稳定的对象模型。同时,设计人员也可以从设计模式中得到启发,去发现一些原本没有留意的细节。


精彩图集

赞助商链接