C#设计模式之20——观察者模式

本文探讨了观察者模式在C#中的应用,用于数据的多角度实时展示。通过创建主题(Subject)和观察者(Observer),当数据发生变化时,主题能通知所有观察者自动更新界面。示例中,主窗口作为主题,多个观察者(如颜色显示面板)响应主题的事件,实现界面同步更新。这种模式降低了数据与界面之间的耦合,但需要注意在大量连续变更时可能引发的性能问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

观察者模式可以用来同时以多种方式表示数据。我们可能有一组数据,然后希望同时在用户界面用多种表示方式显示这组数据,比如用表格和绘图的两种方式。并且当数据发生变化的时候我们希望数据的显示能够自动的更新,这就需要我们使用观察者模式。

观察者模式假设包含数据的对象与显示的对象是分离的,显示对象就需要观察数据中的变化。

在实现观察者模式的时候,我们通常把数据成为主题(Subject),把每种显示方式成为观察者(Observer).

观察者都有一个为其他对象所知的接口,这样当数据放生变化的时候主题就可以通过调用这个接口告知观察者数据的变化。

观察者统一的接口定义为:

using System;

namespace Observer
{
	/// <summary>
	/// Summary description for Observer.
	/// </summary>
	public interface Observer 	{
		void sendNotify(string message);
	}
}


主题的接口:

using System;

namespace Observer
{
	/// <summary>
	/// Summary description for Subject.
	/// </summary>
	public interface Subject 	{
		 void registerInterest(Observer obs);
	}
}


我们的一个简单例子,在主程序面板中有三个颜色选择按钮,单击一个按钮在另外两个面板中会显示对应的颜色文字和对应的颜色。这样就需要两个显示面板作为观察者观察主面板这个主题中的数据的变化。

程序的截图如下:

 

这样,主窗口在这里就是我们的主题,主题的定义如下:

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace Observer
{
	/// <summary>
	/// Summary description for Form1.
	/// </summary>
	public class Form1 : System.Windows.Forms.Form, Subject
	{
		private System.Windows.Forms.GroupBox groupBox1;
		private System.Windows.Forms.RadioButton opRed;
		private System.Windows.Forms.RadioButton opGreen;
		private System.Windows.Forms.RadioButton opBlue;
		private ArrayList observers;
		/// <summary>
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.Container components = null;

		public Form1()
		{
			InitializeComponent();
			init();
		}
		private void init() {
			EventHandler evh = new EventHandler (opButton_Click);
			opRed.Click += evh;
			opBlue.Click += evh;
			opGreen.Click += evh;
			observers =  new ArrayList ();
			ListObs lobs = new ListObs (this);
			lobs.Show ();
			ColObserver colObs = new ColObserver (this);
			colObs.Show();
		}
		public void registerInterest(Observer obs ) {
			observers.Add (obs);
		}
		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if (components != null) 
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		#region Windows Form Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
            this.groupBox1 = new System.Windows.Forms.GroupBox();
            this.opBlue = new System.Windows.Forms.RadioButton();
            this.opGreen = new System.Windows.Forms.RadioButton();
            this.opRed = new System.Windows.Forms.RadioButton();
            this.groupBox1.SuspendLayout();
            this.SuspendLayout();
            // 
            // groupBox1
            // 
            this.groupBox1.Controls.Add(this.opBlue);
            this.groupBox1.Controls.Add(this.opGreen);
            this.groupBox1.Controls.Add(this.opRed);
            this.groupBox1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.groupBox1.Location = new System.Drawing.Point(38, 26);
            this.groupBox1.Name = "groupBox1";
            this.groupBox1.Size = new System.Drawing.Size(192, 155);
            this.groupBox1.TabIndex = 0;
            this.groupBox1.TabStop = false;
            this.groupBox1.Text = "Select colors";
            // 
            // opBlue
            // 
            this.opBlue.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.opBlue.ForeColor = System.Drawing.Color.Blue;
            this.opBlue.Location = new System.Drawing.Point(38, 95);
            this.opBlue.Name = "opBlue";
            this.opBlue.Size = new System.Drawing.Size(96, 17);
            this.opBlue.TabIndex = 2;
            this.opBlue.Text = "Blue";
            // 
            // opGreen
            // 
            this.opGreen.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.opGreen.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(192)))), ((int)(((byte)(0)))));
            this.opGreen.Location = new System.Drawing.Point(38, 69);
            this.opGreen.Name = "opGreen";
            this.opGreen.Size = new System.Drawing.Size(116, 17);
            this.opGreen.TabIndex = 1;
            this.opGreen.Text = "Green";
            // 
            // opRed
            // 
            this.opRed.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.opRed.ForeColor = System.Drawing.Color.Red;
            this.opRed.Location = new System.Drawing.Point(38, 34);
            this.opRed.Name = "opRed";
            this.opRed.Size = new System.Drawing.Size(125, 26);
            this.opRed.TabIndex = 0;
            this.opRed.Text = "Red";
            // 
            // Form1
            // 
            this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
            this.ClientSize = new System.Drawing.Size(293, 218);
            this.Controls.Add(this.groupBox1);
            this.Name = "Form1";
            this.Text = "Observer demo";
            this.groupBox1.ResumeLayout(false);
            this.ResumeLayout(false);

		}
		#endregion

		/// <summary>
		/// The main entry point for the application.
		/// </summary>
		[STAThread]
		static void Main() 
		{
			Application.Run(new Form1());
		}

		private void opButton_Click(object sender, System.EventArgs e) {
			RadioButton but = (RadioButton)sender;
			for(int i=0; i< observers.Count ; i++ ) {
				Observer obs = (Observer)observers[i];
				obs.sendNotify (but.Text );
			}
		}
	}
}


主题中定义了两个观察者的实例,并把自身的引用传递给观察者,这样在观察者构造的时候可以在主程序的主题中注册观察者。这里的主题在处理按钮事件的时候,找到对应的按钮事件,然后给注册的所有观察者发送数据变化通知,这样观察者就可以做出相应的反映。

两个观察者的定义:

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;

namespace Observer
{
	/// <summary>
	/// Summary description for ListObs.
	/// </summary>
	public class ListObs : System.Windows.Forms.Form, Observer
	{
		private System.Windows.Forms.ListBox lsColors;
		/// <summary>
		/// Adds text of color to list box
		/// </summary>
		private System.ComponentModel.Container components = null;

		public ListObs(Subject subj) 		{
			InitializeComponent();
			init(subj);
		}
		//------
		public void init(Subject subj) {
			subj.registerInterest (this);
		}
		//------
		public void sendNotify(string message){
			lsColors.Items.Add(message);
		}
		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		#region Windows Form Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
            this.lsColors = new System.Windows.Forms.ListBox();
            this.SuspendLayout();
            // 
            // lsColors
            // 
            this.lsColors.ItemHeight = 12;
            this.lsColors.Location = new System.Drawing.Point(19, 17);
            this.lsColors.Name = "lsColors";
            this.lsColors.Size = new System.Drawing.Size(259, 184);
            this.lsColors.TabIndex = 0;
            // 
            // ListObs
            // 
            this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
            this.ClientSize = new System.Drawing.Size(299, 222);
            this.Controls.Add(this.lsColors);
            this.Name = "ListObs";
            this.Text = "List observer";
            this.ResumeLayout(false);

		}
		#endregion
	}
}


 

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;

namespace Observer
{
	/// <summary>
	/// Summary description for ColObserver.
	/// </summary>
	public class ColObserver : System.Windows.Forms.Form, Observer 	{
		private System.ComponentModel.Container components = null;
		private Brush bBrush;
		private System.Windows.Forms.PictureBox pic;
		private Font fnt;
		private Hashtable colors;
		private string colName;
		//-----
		public ColObserver(Subject subj) 		{
			InitializeComponent();
			init(subj);
		}
		//-----
		private void init(Subject subj) {
			subj.registerInterest (this);
			fnt = new Font("arial", 18, FontStyle.Bold);
			bBrush = new SolidBrush(Color.Black);
			pic.Paint+= new PaintEventHandler (paintHandler);
			colors = new Hashtable ();
			colors.Add("red", Color.Red );
			colors.Add ("blue", Color.Blue );
			colors.Add ("green", Color.Green );
			colName = "";
		}
		//-----
		public void sendNotify(string message) {
			colName = message;
			message = message.ToLower ();
			Color col = (Color)colors[message];
			pic.BackColor = col;
		}
		//-----
		private void paintHandler(object sender, PaintEventArgs e) {
			 Graphics g = e.Graphics ;
			 g.DrawString(colName, fnt, bBrush, 20, 40);
		}
		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		#region Windows Form Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
            this.pic = new System.Windows.Forms.PictureBox();
            ((System.ComponentModel.ISupportInitialize)(this.pic)).BeginInit();
            this.SuspendLayout();
            // 
            // pic
            // 
            this.pic.BackColor = System.Drawing.SystemColors.ActiveCaptionText;
            this.pic.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
            this.pic.Location = new System.Drawing.Point(29, 26);
            this.pic.Name = "pic";
            this.pic.Size = new System.Drawing.Size(221, 164);
            this.pic.TabIndex = 0;
            this.pic.TabStop = false;
            // 
            // ColObserver
            // 
            this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
            this.ClientSize = new System.Drawing.Size(287, 218);
            this.Controls.Add(this.pic);
            this.Name = "ColObserver";
            this.Text = "Color observer";
            ((System.ComponentModel.ISupportInitialize)(this.pic)).EndInit();
            this.ResumeLayout(false);

		}
		#endregion
	}
}


 

这两个观察者在收到数据变化通知以后,就做出相应的反映,改变用户界面,改变数据的显示。

 

观察者促进了到主题的抽象耦合。主题并不知道其任何观察者的细节。不过,观察者模式也有一个缺点,当数据发生了一些列的增量改变的时候,观察者就会收到连续的数据变化通知并做出反复的更新。如果这些更新的成本很高,则引入某种变更管理就是很有必要的了,这样观察者就不会太快或者太过于频繁的收到数据变化通知。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值