C#设计模式之14——职责链模式

职责链模式允许多个类尝试着去处理一个请求,在这种情况下,这些类中的任何一个都不了解其他类的功能。该模式在这些类中提供一个松散耦合,唯有的共同联系是他们直接传递的请求。请求一直沿着链传递,知道有一个类能处理位置。

典型的例子就是应用软件的帮助系统,如果某个按钮有帮助信息,就会显示信息,否则则把帮助请求传递给下一模块,最终的情况是,消息被发送个一个广泛的帮助类,显示一个通用的帮助信息。这个数据结构非常像数据结构里的单链表,请求信息一直沿着链表传递,直到有能处理信息的类为止。

在职责链模式中,链的组织方式是从最具体的情形到最一般的情形;并不保证所有的请求都会产生反映。

职责链是一个很好的模式例子,有助于隔离程序中的每个对象所能完成的工作内容,降低了对象之间的耦合度。

第一个例子,一个命令解析程序,根据用户输入的命令,或者显示图像,或者显示颜色,或者显示文件, 或者直接显示用户输入的不可识别的命令。

用户界面如图:

这个应用中,用户可以输入任何命令,然后软件解析命令,如果发现用书输入"mandrill"则显示图像,如果输入的是"任何一个文件名的形式"则在中间的列表框显示当前目录下所有的文件,如果输入的是颜色,直接在下面的图像里显示颜色,如果输入的命令不可识别,则在最右侧的列表框里直接显示用户输入的不可识别的命令。

我们需要一个链表来处理这个用户输入的命令:Command--->Image File--->Color Name--->File Name--->General

为了编写职责链程序,我们从一个抽象的Chain开始:

using System;

namespace Chain
{
	public abstract class Chain	{
		//describes how all chains work
		private bool hasLink;
		protected Chain chn;
		public Chain()	{
			hasLink = false;	
		}
		//you must implement this in derived classes
		public abstract void sendToChain(string mesg);
		//-----
		public void addToChain(Chain c) {
			//add new element to chain
			chn = c;
			hasLink = true;		//flag existence
		}
		//-----
		public Chain getChain() {
			return chn;	//get the chain link
		}
		//-----
		public bool hasChain() {
			return hasLink;		//true if linked to another
		}
		//-----
		protected void sendChain(string mesg) {
			//send message on down the chain
			if(chn != null)
				chn.sendToChain (mesg);
		}
	}
}


然后按照消息在链表中的传递顺序,编写ImageChain:

using System;
using System.Windows.Forms;
using System.Drawing ;
using CsharpPats;
namespace Chain
{
	/// <summary>
	/// Summary description for ImageChain.
	/// </summary>
	public class ImageChain :Chain 	{
		PictureBox picBox;		//image goes here
		//-----
		public ImageChain(PictureBox pc) 		{
			picBox = pc;		//save reference
		}
		//-----
		public override void sendToChain(string mesg) {
			//put image in picture box
			string fname = mesg + ".jpg";	//assume jpg filename
			csFile fl = new csFile(fname);
			if(fl.exists()) 
				picBox.Image = new Bitmap(fname);
			else{
				if (hasChain()){	//send off down chain
					chn.sendToChain(mesg);
				}
			}
		
		}
	}
}


接下来是ColorChain:

using System;
using System.Collections ;
using System.Drawing ;
using System.Windows.Forms ;
namespace Chain
{
	/// <summary>
	/// receives color names in chain
	/// </summary>
	public class ColorChain : Chain {
		private Hashtable colHash;	//color list kept here
		private Panel panel;		//color goes here
		//-----
		public ColorChain(Panel pnl) 		{
			panel = pnl;			//save reference
			//create Hash table to correlate color names
			//with actual Color objects
			colHash = new Hashtable ();
			colHash.Add ("red", Color.Red);
			colHash.Add ("green", Color.Green);
			colHash.Add ("blue", Color.Blue);
		}
		//-----
		public override void sendToChain(string mesg) {
			mesg = mesg.ToLower ();
			try {
				Color c = (Color)colHash[mesg];
				//if this is a color, put it in the panel
				panel.BackColor =c;
			}
			catch (NullReferenceException e) {
				//send on if this doesn't work
				sendChain(mesg);
			}
			
		}
	}
}


接下来是FileChain:

using System;
using System.Windows.Forms ;
using System.IO ;
using System.IO.IsolatedStorage ;
using CsharpPats;
namespace Chain {
	/// <summary>
	/// Summary description for FileChain.
	/// </summary>
	public class FileChain : Chain {
		ListBox flist;
		public FileChain(ListBox lb)		{
			flist = lb;
		}
	//-----
		public override void sendToChain( string mesg) {
			//if the string matches any part of a filename
			//put those filenames in the file list box
			string[] files;
			string fname = mesg + "*.*";
			files = Directory.GetFiles(Directory.GetCurrentDirectory(), fname);
			//add them all to the listbox
			if (files.Length > 0){
				for (int i = 0; i< files.Length; i++) {
					csFile vbf = new csFile(files[i]);
					flist.Items.Add(vbf.getRootName());
				}
			}
			else {
				if ( hasChain()) {
					chn.sendToChain(mesg);
				}
			}
		}
	}
}


最后是通用的类型:

using System;
using System.Windows.Forms;
namespace Chain
{
	/// <summary>
	/// handles command that is not otherwise legal
	/// </summary>
	public class NoCmd :Chain 	{
		private ListBox lsNocmd;	//commands go here
		//-----
		public NoCmd(ListBox lb) 		{
			lsNocmd = lb;			//copy reference
		}
		//-----
		public override void sendToChain(string mesg) {
			//adds unknown commands to list box
			lsNocmd.Items.Add (mesg);
		}
	}
}


在主程序中,我们就可以构造这样一个可以沿着链传递消息的链表:

private System.ComponentModel.Container components = null;
		private Chain chn;

		public Form1()		{
			InitializeComponent();
			init();
		}
		private void init() {
			//set up chains
			ColorChain clrChain = new ColorChain(pnlColor);
			FileChain flChain = new FileChain(lsFiles);
			NoCmd noChain = new NoCmd(lsNocmd);
			//create chain links
			chn = new ImageChain(picImage);
			chn.addToChain(clrChain);
			clrChain.addToChain(flChain);
			flChain.addToChain(noChain);
		}


private void btSend_Click(object sender, System.EventArgs e) {
			chn.sendToChain (txCommand.Text );
		}


这样,就可以让消息沿着构造的链表传递,直到找到一个可以处理这个消息的类实例。

在这个例子中的类结构图如图:

另外一个例子,应用软件的帮助系统,当用户按下F1以后显示帮助信息,因为每个控件都有焦点,我们这个可以通过判断每个控件是否获得焦点来根据需要显示帮助信息。

这里处理的是空间而不是消息。

用户界面如图:

当有一个空间获得焦点以后,按下F1将显示这个控件的帮助信息,这个帮助请求将会在我们构建的链表中传递,直到有一个类发现自己的控件获得焦点,则显示相应的帮助信息。

using System;
using System.Windows.Forms;
namespace HelpChain
{
	public abstract class Chain	{
		//describes how all chains work
		private bool hasLink;
		protected Control control;
		protected Chain chn;
		protected string message;
		
		public Chain(Control c, string mesg)	{
			hasLink = false;
			control = c;		//save the control
			message = mesg;
		}
		
		public abstract void sendToChain();
		//-----
		public void addToChain(Chain c) {
			//add new element to chain
			chn = c;
			hasLink = true;		//flag existence
		}
		//-----
		public Chain getChain() {
			return chn;	//get the chain link
		}
		//-----
		public bool hasChain() {
			return hasLink;		//true if linked to another
		}
		//-----
		protected void sendChain() {
			//send message on down the chain
			if(chn != null)
				chn.sendToChain ();
		}
	}
}


using System;
using System.Windows.Forms;
namespace HelpChain
{
	/// <summary>
	/// Summary description for ControlChain.
	/// </summary>
	public class ControlChain:Chain 	{
		public ControlChain(Control c, string mesg):base(c, mesg)		{
		}
		public override void sendToChain() {
			//if it has the focus display the message
			if (control.Focused ) {
				MessageBox.Show (message);
			}
			else
				//otherweise pass on down the chain
				sendChain();
		}
	}
}


using System;
using System.Windows.Forms;
namespace HelpChain
{
	/// <summary>
	/// Summary description for EndChain.
	/// </summary>
	public class EndChain:Chain 	{
		public EndChain(Control c, string mesg):base(c, mesg){}
		public override void sendToChain() {
			MessageBox.Show (message);
		}
	}
}


然后在主程序中构建链表:

	private System.ComponentModel.Container components = null;
		private Chain chn;

		public Form1()		{
			InitializeComponent();
			init();
		}
		private void init() {
			chn = new ControlChain(btNew, "Create new files");
			Chain fl =new ControlChain (btFile, "Select a file");
			chn.addToChain (fl);
			Chain bq = new ControlChain (btQuit, "Exit from program");
			fl.addToChain (bq);
			Chain cb =new ControlChain (ckBinary, "Use binary files");
			bq.addToChain (cb);
			Chain ct =  new ControlChain (ckText, "Use text files");
			cb.addToChain (ct);
			Chain ce = new EndChain (this, "General message");
			ct.addToChain (ce);
			KeyEventHandler keyev =  new KeyEventHandler(Form1_KeyDown);
			btNew.KeyDown += keyev;
			btFile.KeyDown += keyev;
			btQuit.KeyDown += keyev;
			ckBinary.KeyDown += keyev;
			ckText.KeyDown += keyev;
		}


private void Form1_KeyDown(object sender, KeyEventArgs e) {
			if(e.KeyCode  == Keys.F1 )
				chn.sendToChain ();
		}


同许多其他模式一样,职责链模式的主要目的是降低对象之间的耦合度,一个对象只需要知道如何把请求传递给另一个对象。

链中的每个对象都是自包含的,完全不了解其他的对象,并且只需要确定自身是否满足请求,这使得每个对象的编写和构建都变得非常的容易。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
原型模式是一种创建型设计模式,其提供了一种复制已有对象的方法来生成新对象的能力,而不必通过实例化的方式来创建对象。原型模式是通过克隆(浅复制或深复制)已有对象来创建新对象的,从而可以避免对象创建时的复杂过程。 在C#中,可以通过实现ICloneable接口来实现原型模式。ICloneable接口定义了Clone方法,该方法用于复制当前对象并返回一个新对象。需要注意的是,Clone方法返回的是Object类型,需要进行强制类型转换才能得到复制后的对象。 以下是一个简单的示例代码: ```csharp public class Person : ICloneable { public string Name { get; set; } public int Age { get; set; } public object Clone() { return MemberwiseClone(); } } // 使用示例 var person1 = new Person { Name = "Tom", Age = 20 }; var person2 = (Person)person1.Clone(); person2.Name = "Jerry"; Console.WriteLine(person1.Name); // 输出 "Tom" Console.WriteLine(person2.Name); // 输出 "Jerry" ``` 在上面的示例代码中,实现了一个Person类,并实现了ICloneable接口中的Clone方法来实现原型模式。复制对象时,使用MemberwiseClone方法进行浅复制,即只复制值类型的字段和引用类型字段的引用,而不复制引用类型字段所引用的对象。在使用示例中,首先创建一个Person对象person1,然后通过Clone方法复制一个新的对象person2,修改person2的Name属性后,输出person1和person2的Name属性,可以看到person1的Name属性并没有改变,说明person2是一个全新的对象。 需要注意的是,如果要实现深复制,即复制引用类型字段所引用的对象,需要在Clone方法中手动将引用类型字段复制一份。另外,使用原型模式时,需要注意复制后的对象和原对象之间的关系,如果复制后的对象修改了原对象的状态,可能会对系统产生意想不到的影响。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值