技术开发 频道

Observer模式应用实践

[IT168技术文档]作为行为模式中的一种,Observer模式与众不同的是它的关注重心不是对象的行为,而是两个或多个相互协作类之间的依赖关系。它之所以被称为行为模式,原因是它通过了某种行为来控制这种依赖关系,并产生消息通知进而达到修改被依赖的类的行为或状态的目的。GOF在《设计模式》一书中对Observer模式的意图有非常清晰地说明:“定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。”

Observer模式中最关键的一点是需要对观察者(Observer)角色进行抽象,唯有如此,方能解除观察者与被观察者之间的依赖关系。此外,被观察者即主体(Subject)角色,还需要维护一个集合对象,它是一个观察者对象的列表集合,在消息通知的时候,被观察者将遍历该集合内的观察者对象,并调用它们的相关方法。

.NET Framework中,由于引入了委托与事件机制,因此,在实现Observer模式时,我们有了更多的选择。委托(delegate)是一个特殊的类,我们可以将其理解为函数指针,它能够指向符合委托定义的所有方法。因此它与接口有一定的相似性,且更具有广泛性,因为它可以接受静态方法,对方法名也没有限制,仅要求方法签名符合委托的定义。此外,委托链能够很好地管理多个观察者对象(即事件的订阅者),我们不需要建立专门的集合对观察者对象进行管理维护。因而,在Observer模式的实现上,利用委托与事件会是一个更佳的方案。

在本章中,我将选取CIMS系统中的一个功能需求进行分析,它是一个典型的Observer模式的实现。为了全面地展现Observer模式在.NET框架下的实现,我在给出了传统的Observer模式实现的基础上,又通过引入委托与事件机制实现了项目的需求,事实上,后一种方式,才是我们在项目中的真实实现。

1  需求分析—监控设备状态

 

CIMSComputer Integrated Manufacture System)中,有这样一个需求。服务端通过系统实时监听设备(Equipment)的状态,如果状态发生了改变,则通过.NET Remoting技术将消息发送到客户端。在客户端,设备以控件的形式放在容器控件GroupBox中,且控件的名字就是设备的名字。当客户端收到消息后,会在GroupBox中查找符合条件的控件,然后根据设备当前的状态,更新该控件的颜色。

根据需求分析,要求客户端系统具备如下功能。

客户端系统能够侦听到服务端传递来的消息,并获取设备的相关信息;

依据设备的信息,在相关窗体界面上查找符合条件的设备控件;

找到控件后,根据设备当前状态,修改控件的颜色。

由于服务端与客户端之间的消息传递不是本章所描述的重点,我们在识别对象时暂时将该功能点排除在外,那么识别出的对象就包括:

侦听器;

消息的通知者;

接收消息的窗体;

设备控件。

侦听器是一个Timer,每隔一定时间会去查询服务端发送来的消息。当消息到达后,会激发消息的通知者发送消息。添加了设备控件的窗体,则负责接收消息,然后执行相应的操作。

如果这些对象都存在于一个主程序窗体中,那么一切就变得异常简单了。我们只需要在TimerTick事件中实现这些逻辑就可以了。

public void timer_Tick(object sender, EventArgs e)

{

    //接收服务端消息

    //根据消息获取设备信息,并判断状态是否发生改变

    //根据设备信息,查询GroupBox中符合条件的设备控件

    //根据设备状态,修改设备控件的颜色

}

然而,现实的需求并非如此简单。事实上,在客户端可能具备多个需要显示设备控件的界面。也就是说,接收消息的窗体可能会有多个。此时,消息的通知者必须作为一个单独的对象存在,它是可以被重用的,同时,它也必须是可以扩展的。相似的,作为接收消息的窗体既然存在多个,自然也存在可能的扩展点。如此一来,设计就变得复杂了。

2  引入Observer模式

 

事实上,这是一个典型的Observer模式应用案例。所谓的消息通知者,就是Subject(主体),它会在设备状态发生变化时,通知Observer(观察者);接收消息的窗体,就是Observer(观察者)角色,待收到消息后,执行修改控件颜色的任务。

由于消息的通知者必须待设备状态发生变化时,方才发出消息到相关的窗体,因此,我们可以将Observer抽象为包含ChangeState方法的接口。

public interface IStateNotifier

{

    void ChangeState(Equipment eqp);

}

其中,Equipment类以及状态枚举类型的定义如下。

public enum EQPState { Offline=0, Online }

public class Equipment

{

    private EQPState m_state;

    public EQPState State

    {

        get {return m_state;}

        set {m_state = value;}

    }

 

    //其他属性略

}

由于Subject的观察者可能不仅仅是一个,因此IStateNotifier接口还应包括添加和移除Observer对象的方法,修改如下。

public interface IStateNotifier

{

    void ChangeState(Equipment eqp);

    void AddStateChangeable(IStateChangeable stateChangeable);

    void RemoveStateChangeable(IStateChangeable stateChangeable);

}

接口IStateChangeable就是Observer对象的抽象,它包含了FindAndChangeEQPState方法,负责查找设备控件,并修改控件的颜色。

public interface IStateChangeable

{

    void FindAndChangeEQPState(Equipment eqp);

}

我们可以定义专门的类实现IStateChangeable接口,不过对于FindAndChangeEQPState方法而言,由于它的执行逻辑是在窗体的GroupBox中查找设备控件,所以,包含了这些设备控件的窗体本身就是一个Observer,可以直接实现IStateChangeable。例如,Operator的管理界面和Manufacture管理界面,它们的窗体类都必须履行Observer的职责。

public class OperatorForm:System.Windows.Forms.Form,IStateChangeable

{  

    public OperatorForm(IStateNotifier notifier)

    {

        notifier.AddStateChangeable(this);

    }

    public void FindAndChangeEQPState(Equipment eqp)

    {

        //实现略

    }

}

public class ManufactureForm:System.Windows.Forms.Form,IStateChangeable

{

    public ManufactureForm(IStateNotifier notifier)

    {

        notifier.AddStateChangeable(this);

    }

    public void FindAndChangeEQPState(Equipment eqp)

    {

        //实现略

    }

}

由于在目前的需求中,消息的通知者仅有一个,因此,我定义了StateNotifier实现IStateNotifier接口。

public class StateNotifier:IStateNotifier

{

    private List<IStateChangeable> m_list = new List<IStateChangeable>();

    public void AddStateChangeable(IStateChangeable stateChangeable)

    {

        m_list.Add(stateChangeable);

    }

    public void RemoveStateChangeable(IStateChangeable stateChangeable)

    {

        m_list.Remove(stateChangeable);

    }

    public void ChangeState(Equipment eqp)

    {

        foreach (IStateChangeable changeable in m_list)

        {

            changeable.FindAndChangeEQPState(eqp);

        }

    }

}

整个结构的类图如图1所示。

 

注意:如果为了简化设计,在不考虑对消息的通知者扩展的情况下,接口IStateNotifier的定义就显得多余,此时可以去掉IStateNotifier,直接定义StateNotifier类。由于StateNotifier类是可以被实例化的,因此不能定义为抽象类,为保留可能的扩展性,应将方法ChangeState定义为虚方法。

 

 

                                    1  引入Observer模式后的类图

作为Subject角色的主窗体,当侦听器timer发现设备的状态发生改变后,就会调用StateNotifier类的相关方法。

 

public class MainForm:System.Windows.Forms.Form

{

    private IStateNofifier m_notifier = new StateNotifier();

    private Equipment m_eqp = new Equipment();

   

    private Timer timer;

    private IContainer components;

 

    public MainForm()

    {

        OperatorForm opForm = new OperatorForm(m_notifier);

        ManufactureForm maForm = new ManufactureForm(m_notifier);

 

        opForm.Show();

        maForm.Show();

 

        InitializeComponent();     

    }

    private void InitializeComponent()

    {

        //其他略

 

        this.timer.Enabled = true;

        this.timer.Interval = 1000;

        this.timer.Tick += new System.EventHandler(this.timer_Tick);

    }

    private void timer_Tick(object sender, EventArgs e)

    {

        //获取服务端消息

        //判断状态是否改变

        if (IsChanged())

        {

            m_notifier.ChangeState(m_eqp);

        }

    }

    private bool IsChanged()

    {

        return true;

    }

}

此时,两个实现了IStateChangeable接口的窗体对象,在实例化时会将窗体类本身添加到m_notifierList对象中。当侦听器timer监测到设备状态发生变化时,执行ChangeState方法,此时就将遍历m_notifier对象,执行OperatorFormManufactureForm窗体对象的FindAndChangeEQPState方法,实现改变控件颜色的目的。

通过引入Observer模式,既利于Subject对象的重用,又能保证Observer对象的扩展,同时还解除了SubjectObserver之间的耦合,可谓一举三得。

0
相关文章