解析 IssueVision使用的OBSERVER(观察者)模式和COMMAND(命令)模式

IssueVision中的对设计模式的应用,IssueVision主要使用了OBSERVER(观察者)模式和COMMAND(命令)模式.
今天就来看看OBSERVER(观察者)模式在IssueVision中的应用,它在IssueVision中扮演着重要角色.

智能客户端应用程序是瘦客户端应用程序的强大替代产品。它们可以为用户提供内容丰富且响应迅速的用户界面,提供脱机工作的能力,并且提供利用本地硬件和软件资源的方法。智能客户端为用户提供了在强大且直观的客户端环境中访问信息和远程服务的能力,并且是一种用来开发灵活的、面向用户的应用程序以及提高用户工作效率和满意度的有效的解决方案。
本讲座首先对四天的课程进行了简要的介绍,然后介绍了智能客户端的定义、特点,与其他应用程序的比较以及智能客户端的商业价值等内容,并演示了典型的Smart Client应用程序—TaskVision。

OBSERVER 概念定义

"四人帮"GoF是这样定义OBSERVER(观察者)模式的------定义对象间的一种一对多的关系,当一个对象的状态发生改变时,所有依赖它的对象都得到通知,并被自动更新.

从定义可以看出,OBSERVER(观察者)模式逻辑上需要两组对象来实现.

  • 首先它必需要有发布者(Publish),也可称为被观察的目标(Subject)(习惯上都称它为目标Subject,后面我们都称它作目标Subject),
  • 另外就是订阅者(Subscribe),习惯上称为观察者(Observer).
  • 一个目标对象对应多个观察者对象,目标对象发生变化时,所有在目标对象中注册的观察者对象会得到通知,自动更新自己

在应用程序开发过程中,往往都要求用户界面和业务逻辑分离,划定清晰的界限.因为应用程序要求能快速的更改用户界面并且不能对应用程序其他部分产生连带影响,而且业务逻辑也会发生变化并要求这一切变化与用户界面无关.观察者(Observer)就是解决此问题最常用的设计模式,它非常有助于在系统中各个对象之间划分清晰的界限.

下图最好的展示了这种形式,观者者(observer)们(表格对象,柱状图对象和饼状图对象)都依赖数据对象Subject,所以数据对象Subject的所有改变都会通知它们.但它们互相之间并不知道对方的存在,表格对象不知道其他表格对象或者其他柱状图对象的存在.对于数据对象Subject可以有任何多的观察者,这些观察者都能在Subject对象发生改变时得到通知,使自己的状态与Subject同步:

.NET框架引入了委托和事件,它们提供了更新,功能更强大的方法来实现OBSERVER(观察者)模式.(关于委托和事件的更多内容请参考相关文档).如果你不熟悉委托和事件,实现OBSERVER(观察者)模式则需要作很多工作(像在Java中实现那样).下面我就以IssueVision中的实现来简单讲述一下.

using System;

namespace IssueVision
{
	// IObserver is a simple interface that supports the implementation of the 
	// Observer pattern in IssueVision.	
    //IObserver是一个简单的接口,支持IssueVision中的观察者模式。
	public interface IObserver
	{
		ISubject Subject
		{
			set;
		}
	}
}


namespace IssueVision
{
	// ISubject is a simple marker interface that supports the implementation
	// of the Observer pattern in IssueVision.
	  //ISubject是一个简单的标记接口,支持实现IssueVision中的观察者模式。
	public interface ISubject
	{
	}
}

大家可能发现,这两个接口几乎为空,什么都没有定义,那这两个接口的作用是什么呢?其实定义这两个接口的作用主要为编码的"规范化",只要类实现了这两个接口任何一个,那么就代表此类就实现了OBSERVER(观察者)模式,并且很明显的知道谁是Subject,谁是Observer.

这就需要使用到委托和事件了.在回来看IssueSubject

IssueSubject.cs

public class IssueSubject : Component, ISubject
 {

#region Delagate and Event Declarations
  .......

  public delegate void ConflictDataChangedEventHandler(object sender, EventArgs e);
  public delegate void LookupDataChangedEventHandler(object sender, EventArgs e);
    
    // ConflictDataChanged changes when a conflict is resolved, or new conflicts are detected.
    //冲突解决或检测到新冲突时,ConflictDataChanged会更改。
  public virtual event ConflictDataChangedEventHandler ConflictDataChanged;
  
  // LookupDataChanged is raised when lookup data is downloaded from the server
  //从服务器下载查找数据时引发LookupDataChanged
  public virtual event LookupDataChangedEventHandler LookupDataChanged;
  
  ......

在IssueSubject中申明委托和事件,观察者(Observer)对象登记这些事件,那么当IssueSubject改变后,激活一个事件,那么所有的观察者(Observer)对象都能得到这个改变的通知,从而激活相应的处理.

再看Controls/IssueTreeView.cs

namespace IssueVision
{
 // The IssueTreeView user control implements the view selection ui for IssueVision
 public class IssueTreeView : UserControl, IObserver
 {
 
  ..... 
  private TreeView trvViews;
  
  private IssueSubject m_subject = null;

  private IContainer components;
  
  public virtual ISubject Subject   //ISubject接口的方法
  {
   set
   {
    m_subject = (IssueSubject)value;

    //登记IssueSubject的事件,并交给相关方法处理事件
    m_subject.LookupDataChanged += new IssueSubject.LookupDataChangedEventHandler(this.Subject_LookupDataChanged);
    m_subject.ConflictDataChanged += new IssueSubject.ConflictDataChangedEventHandler(this.Subject_ConflictDataChanged);   
}
  }

  最后,在IssueSubject中激活这些事件.

IssueSubject.cs

  private void LoadIssueData()
  {
   .......
   m_dataSet.DataSetName = "IssueSubject";
   
   if (LookupDataChanged != null)
   {
    LookupDataChanged(this, EventArgs.Empty);
   }
  }

设计模式之 设 Observer观察者模式解决牛顿童鞋成绩问题示例

本文实例讲述了C#设计模式之Observer观察者模式解决牛顿童鞋成绩问题。分享给大家供大家参考,具体如下:

一.理论定义

观察者模式 描述了 一种 一对多的关系。 当某一对象的状态发生改变时,其他对象会得到 改变的通知。并作出相应的反应。

二.应用举例 应

需求描述:牛顿同学的期末考试成绩(Score)出来了,各科老师都想知道自己的 学生 成绩情况!

语文老师(TeacherChinese)只关心 牛顿的语文(Chinese)成绩. 
英语老师(TeacherEnglish)只关心 牛顿的英语(English)成绩. 
数学老师(TeacherMathematics)只关心 牛顿的数学(Mathematics)成绩. 
班主任想关心(TeacherTeacherHead) 牛顿的各科成绩和总成绩(TotalScore). 成绩出来后,
各科老师都得到通知(Notify).

三.具体编码

1.添加学生信息类,里面只有一个Name属性。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Com.Design.Gof.Observer
{
/// <summary>
/// 学生信息类
/// </summary>
public class Student
{
/// <summary>
/// 姓名
/// </summary>
public string Name { get; set; }
}
}
2.成绩单(Score)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Com.Design.Gof.Observer
{
public delegate void NotifyEventHandler(Score score);
public class Score
{
public Score() { }
//事件声明
public NotifyEventHandler NotifyEvent=null;
/// <summary>
/// 调用入口
/// </summary>
public void Notify() {
OnNotifyChange();
}
/// <summary>
///通知事件
/// </summary>
private void OnNotifyChange() {
if (NotifyEvent != null) {
NotifyEvent(this);
}
}
/// <summary>
/// 数学成绩
/// </summary>
public float Mathematics { get; set; }
/// <summary>
/// 英语成绩
/// </summary>
public float English { get; set; }
/// <summary>
/// 语文成绩
/// </summary>
public float Chinese { get; set; }
/// <summary>
/// 三科总成绩
/// </summary>
public float TotalScore {
get {
return Mathematics+English+Chinese;
}
}
/// <summary>
/// 学生基本信息
/// </summary>
public Student student { get; set; }
}
}
3.语文老师
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Com.Design.Gof.Observer
{
/// <summary>
/// 语文老师
/// </summary>
public class TeacherChinese
{
public void Receive(Score score) {
Console.WriteLine("我是语文老师,我只关心"+score.student.Name+"的语文成绩:"+score.Chinese+"分");
}
}
}
4.英语老师
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Com.Design.Gof.Observer
{
/// <summary>
/// 英语老师
/// </summary>
public class TeacherEnglish
{
public void Receive(Score score) {
Console.WriteLine("我是英语老师,我只关心" + score.student.Name + "的英语成绩:" + score.English + "分");
}
}
}
5.数学老师
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Com.Design.Gof.Observer
{
/// <summary>
/// 数学老师
/// </summary>
public class TeacherMathematics
{
public void Receive(Score score) {
Console.WriteLine("我是数学老师,我只关心" + score.student.Name + "的数学成绩:" + score.Mathematics + "分");
}
}
}
6.班主任
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Com.Design.Gof.Observer
{
/// <summary>
/// 班主任
/// </summary>
public class TeacherTeacherHead
{
public void Receive(Score score) {
string name=score.student.Name;
Console.WriteLine("我是班主任,我要关心 " + name + " 的各科成绩和总成绩");
Console.WriteLine(name + "的 语文 成绩: " + score.Chinese + " 分");
Console.WriteLine(name + "的 英语 成绩: " + score.English + " 分");
Console.WriteLine(name + "的 数学 成绩: " + score.Mathematics + " 分");
Console.WriteLine(name + "的 总 成绩: " + score.TotalScore + " 分");
}
}
}
7.下面是主函数调用
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Com.Design.Gof.Observer;
namespace Com.Design.Gof.Test
{
class Program
{
static void Main(string[] args)
{
#region Observer
/*牛顿同学的成绩单*/
Score score = new Score {
Chinese = 60,
Mathematics = 100,
English = 90,
student = new Student { Name = "牛顿" }
};
TeacherChinese teacherChinese = new TeacherChinese(); //语文老师
TeacherEnglish teacherEnglish = new TeacherEnglish();//英语老师
TeacherMathematics teacherMathematics = new TeacherMathematics();//数学老师
TeacherTeacherHead teacherTeacherHead = new TeacherTeacherHead();//班主任
//牛顿成绩单出来了,老师都想知道这个结果。
score.NotifyEvent += new NotifyEventHandler(teacherChinese.Receive);
score.NotifyEvent += new NotifyEventHandler(teacherEnglish.Receive);
score.NotifyEvent += new NotifyEventHandler(teacherMathematics.Receive);
score.NotifyEvent += new NotifyEventHandler(teacherTeacherHead.Receive);
//向 各 学科 老师发送 有针对性的,感兴趣的 成绩
score.Notify();
#endregion
Console.ReadKey();
}
}
}

8.运行结果

9.总结

应用C#语言提供的事件和通知,可以让观察者模式更加优雅的实现。事件的 +=操作,实在是让人So happy。

COMANND 概念定义

using System;

namespace IssueVision
{
    // The Command class is used to implement the Command pattern in IssueVision.
    // Each command manages a set of UI elements that provide a user different ways
    // of initiating the same action.  For example, an application might allow the 
    // user to Save data using the File menu, a toolbar button, and a context menu.  
    // A single Command object could manage the menu item, toolbar button and context
    // menu as a unit.
    //
    // Command objects provide a unified means of calling the action method, as well as 
    // setting the enabled state of all of the related ui elements using a single line
    // of code.

    //Command类用于实现IssueVision中的Command模式。

    //每个命令都管理一组UI元素,为用户提供不同的方式启动相同的动作。
    //例如,应用程序可能允许用户可以使用“文件”菜单、工具栏按钮和上下文菜单保存数据。

    //单个命令对象可以管理菜单项、工具栏按钮和上下文菜单作为一个单元。
    //命令对象提供了调用操作方法的统一方法,以及使用单行设置所有相关ui元素的启用状态代码
    public class Command
	{
        // The EnableChanged event is raised when the IsEnabled value of the command 
        // changes. This is handled in the Commander objects to set the Enabled property
        // of the managed controls.
        //当命令的IsEnabled值为更改。这在Commander对象中处理,以设置Enabled属性托管控件
        public delegate void EnableChangedEventHandler(object sender, Command.EnableChangedEventArgs e);
		public virtual event EnableChangedEventHandler EnableChanged;
		public delegate void Action();
		
		private Action m_action;
		private bool m_isEnabled = true;

        // The IsEnabled property is used to set/retrieve the enabled state of all UI
        // controls that the command manages
        ///IsEnabled属性用于设置/检索所有UI的启用状态命令管理的控件
        /检索;取回;找回;挽回;索回;检索数据;扭转颓势
        public bool IsEnabled
		{
			get
			{
				return m_isEnabled;
			}

			set
			{
				if (m_isEnabled != value)
				{
					m_isEnabled = value;
					if (EnableChanged != null)
					{
						EnableChanged(this, new EnableChangedEventArgs(IsEnabled));
					}
				}
			}
		}

		public Command(Action action)
		{
			m_action = action;
		}

        // Invokes the method assigned to this command.
       // 调用分配给此命令的方法。
		public void Execute()
		{
			m_action();
		}

        // Arguments passed to the EnableChanged event.
        //传递给EnableChanged事件的参数。
        public class EnableChangedEventArgs : EventArgs
		{
			private bool m_isEnabled = false;

			public bool IsEnabled
			{
				get
				{
					return m_isEnabled;
				}
			}

			public EnableChangedEventArgs(bool isEnabled)
			{
				m_isEnabled = isEnabled;
			}
		}
	}
}

using System;
using System.Windows.Forms;

namespace IssueVision
{
    // Commander-derived classes exist to hook up Windows Forms UI elements to 
    // Command objects using an adapter pattern.  The adapter approach permits a 
    // single Command object to sink events from dissimilar ui elements, and to set
    // state on dissimilar ui elements.
    //
    // This implementation provides Commanders for 
    // -menu items
    // -toolbar buttons
    //
    // Commanders exhibit the Adapter pattern in two ways:
    // - They adapt the different kinds of events fired by Clients (e.g. menu
    //   items and tool bar buttons) to invoke the Execute methods of their
    //   Commands.
    // - They adapt the EnableChanged events fired by Commands to set the
    //   Enabled property of their Clients.

    // base Commander class

    //存在Commander派生类以将Windows窗体UI元素连接到

    //使用适配器模式的命令对象。适配器方法允许

    //单个Command对象接收来自不同ui元素的事件,并设置

    //不同ui元素上的状态。

    //

    //此实现为

    //-菜单项

    //-工具栏按钮

    //

    //指挥官以两种方式展示适配器模式:

    //-它们适应客户触发的不同类型的事件(例如菜单

    //项目和工具栏按钮)调用其

    //命令。

    //-它们调整由命令触发的EnableChanged事件,以设置

    //其客户端的Enabled属性。



    //基地指挥官级
    public abstract class Commander
	{
		protected Command m_command;
		protected abstract void HandleEnableChangedEvent(object sender, Command.EnableChangedEventArgs e);

		protected Commander(Command command)
		{
			m_command = command;
			m_command.EnableChanged += new Command.EnableChangedEventHandler(this.HandleEnableChangedEvent);
		}
	}
	
	// MenuItemCommander class
	public class MenuItemCommander : Commander
	{
		private MenuItem m_item;

		protected MenuItemCommander(MenuItem item, Command command) : base(command)
		{
			m_item = item;
			m_item.Click += new EventHandler(this.HandleUIEvent);
		}

		protected override void HandleEnableChangedEvent(object sender, Command.EnableChangedEventArgs e)
		{
			m_item.Enabled = e.IsEnabled;
		}

		private void HandleUIEvent(object sender, EventArgs e)
		{
			m_command.Execute();
		}

		// Connect is a shared (static) method that performs the task of adapting a menu
		// item to a command.  The commander exists only to wire up the two objects -- 
		// it is not used further
		public static void Connect(MenuItem item, Command command)
		{
			MenuItemCommander unused = new MenuItemCommander(item, command);
		}
	}
	
	public class ToolBarButtonCommander : Commander
	{
		private ToolBarButton m_button;

		protected ToolBarButtonCommander(ToolBarButton button, Command command) : base(command)
		{
			m_button = button;
			button.Parent.ButtonClick += new ToolBarButtonClickEventHandler(this.HandleUIEvent);
		}

		protected override void HandleEnableChangedEvent(object sender, Command.EnableChangedEventArgs e)
		{
			m_button.Enabled = e.IsEnabled;
		}

		private void HandleUIEvent(object sender, ToolBarButtonClickEventArgs e)
		{
			if (m_button == e.Button)
			{
				m_command.Execute();
			}
		}

		// Connect is a shared (static) method that performs the task of adapting a toolbar
		// button to a command.  The commander exists only to wire up the two objects -- 
		// it is not used further
		public static void Connect(ToolBarButton button, Command command)
		{
			ToolBarButtonCommander unused = new ToolBarButtonCommander(button, command);
		}
	}
}

​ ​IssueVision​​ 封装类简单说明

ThreadHelper.dll: 提供线程相关组件

SplashScreen 闪现的屏幕

SingletonApp 唯一程序,第二个实例不会动作

SafeLabel – 可在多线程下安全操作的Label

BackgroundWorker 后台工作程序

RunWorkerAnsy() 开始后台工作

DoWorkEventHandler 后台执行开始事件 执行异步任务

RunWorkerCompletedEventHandler 后台执行结束事件

ReportProgress() 报告后台进度

ProgressChangedEventHandler 进度改变事件 用于画进度条

CancelAnsync() 取消后台任务

IssueVisionWeb :

IssueVisionWebservices.asmx:

IVData: 组件. 调用dataadapter实现数据操作,含业务逻辑.

SecurityHelper: 封装了安全认证操作

Common: 读取配置

EventLogHelper: 辅助操作EventLog

IssueVision:

WebServiceLayer: 提供WS访问的中介. URL从配置文件读取. CredencialHeader从登陆用户取得.

UserSettings: 当前用户的状态和状态的本地缓存

SerializationHelper: 数据到文件的加密存储

DataProtection: 调用Win32API实现的加密解密

LoginForm: 登录. 被MainForm调用. 窗体淡入淡出.

IssueSubject: ISubject 业务模型Model

SaveIssueData() 保存业务数据到本地文件

LoadIssueData() 加载业务数据

​ ​AppUpdator​​: SmartClient的关键。自动保持版本更新。

数据采用DataSet保存和传递,不论客户端还是Web服务端。

数据访问采用自动生成的DataAdapter。

IssueSubject作为整个程序的业务模型,对外提供数据、数据时间、存储、更新等动作。

窗体中的各个显示控件监听数据变化事件,有变化时调整自身需要显示的部分。

冲突检测:

客户端从Dataset.GetChanges()取得有修改的数据通过Web服务提交到服务器。

Web服务在提交用户的改动时监听DatatAdapter的UpdateError事件,把有冲突的数据放到Conflict表中返回给客户端。

实际上利用了DataAdapter的并非冲突检测功能。

WHY:

  1. 客户端为什么没有使用IsolatedStorageObject来保存临时数据?

  2. 冲突数据的保存是利用DataSet中新建的表Conflict。如果有N个业务表需要检查并发冲突,DS中就需要2N个表。是不是可以还放在原来的表中,只是设定一个RowError呢?

  3. AppUpdater是不是可以注册到WindowsUpdate中,由系统一起完成呢?重复功能,bad smell!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是刘彦宏吖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值