C#设计模式之3——抽象工厂模式

1. 简单工厂模式:http://blog.csdn.net/weixingstudio/article/details/7234423

2. 工厂方法模式:http://blog.csdn.net/weixingstudio/article/details/7234700

 

声明:本文章参考引用《C#设计模式》(科学出版社)一书。

 

抽象工厂(Abstract Factory)模式,是比工厂模式更高一层的抽象。在希望返回对象的几个相关类中的一个时,可以使用该模式,每个类都能够根据需要返回几个不同的对象。换句话说,抽象工厂是一个工厂对象,其返回几组类中的一组。

 

更形象的说,抽象工厂模式需要存在一个抽象工厂,然后每个具体的工厂继承这个设计好的抽象工厂,每个具体工厂可以根据需要返回自己需要的类,具体工厂通过抽象工厂返回不同的类。

 

下面举个具体的例子。

在一块土地上设计一个园地,我们可以把园地设计成一年生的植物园,也可以设计成菜园或者多年生的植物园。不论设计成哪一种园地,都会设计到这样的一些问题:哪些植物应该种在边上,哪些植物适宜种在中间,哪些植物是喜阴的特性等等。

 

我们设计一个Garden的类作为一个抽象工厂,这个类中包含了植物的可能的种类,然后实现这个类的具体工厂就可以根据需要生成不同植物的实例。

 

抽象工厂Garden类的设计:

using System;
using System.Drawing ;
namespace Gardener
{
	/// <summary>
	/// Summary description for Garden.
	/// </summary>
	public class Garden {
		protected Plant center, shade, border;
		protected bool showCenter, showShade, showBorder;
		//select which ones to display
		public void setCenter() {showCenter = true;}
		public void setBorder() {showBorder =true;}
		public void setShade() {showShade =true;}
		//draw each plant
		public void draw(Graphics g) {
			if (showCenter) center.draw (g, 100, 100);
			if (showShade) shade.draw (g, 10, 50);
			if (showBorder) border.draw (g, 50, 150);
		}
	}
}


 

在Garden类中,可以看到抽象工厂可以包含的Plant类的种类有center,shade,border三种类型。

 

Plant类的设计简单的给出如下:

using System;
using System.Drawing;

namespace Gardener
{
	/// <summary>
	/// Summary description for Plant.
	/// </summary>
	public class Plant 	{
		private string name;
		private Brush br;
		private Font font;

		public Plant(string pname) {
			name = pname;     //save name
			font = new Font ("Arial", 12);
			br = new SolidBrush (Color.Black );
		}        
		//-------------
		public void draw(Graphics g, int x, int y) {
			g.DrawString (name, font, br, x, y);
		}
	}
}


Plant类中的draw()方法是用来在显示区域绘制文字的。

 

Garden这一作为接口的类是抽象工厂,其中定义了类的方法,继承了Garden类的具体工厂能够返回Garden类中规定的类的几个,这里,Garden类规定的返回的类可以有center,shade,border三种类型。

 

在继承Garden类的子类中,可以根据自己的需要返回需要的Plant类,下面给出Garden的一个子类的定义,VeggieGarden类的定义如下:

using System;

namespace Gardener
{
	/// <summary>
	/// Summary description for VeggieGarden.
	/// </summary>
	public class VeggieGarden : Garden 	{
		public VeggieGarden() {
			shade = new Plant("Broccoli");
			border = new Plant ("Peas");
			center = new Plant ("Corn");
		}
	}
}


PerennialGarden类的定义如下:

using System;

namespace Gardener
{
	/// <summary>
	/// Summary description for PerennialGarden.
	/// </summary>
	public class PerennialGarden : Garden
	{
		public PerennialGarden() {
			shade = new Plant("Astilbe");
			border = new Plant ("Dicentrum");
			center = new Plant ("Sedum");
		}
	}
}


AnnualGarden类的定义如下:

using System;
using System.Drawing ;

namespace Gardener
{
	/// <summary>
	/// Summary description for AnnualGarden.
	/// </summary>
	public class AnnualGarden : Garden
	{
		public AnnualGarden () {
			shade = new Plant("Coleus");
			border = new Plant ("Alyssum");
			center = new Plant ("Marigold");
		}
	}
}


这三个Garden类的子类,都生成了shade ,border ,center 三个Plant类型,这就是实现了具体工厂通过抽象工厂返回不同的类,这里为了简便起见,让这三个具体工厂返回的类型都包含三种Plant类型,每一个具体工厂返回的shade都不相同,即为根据需要返回不同的对象。而且三个工厂也可以根据需要返回部分Garden类中规定的Plant类型。比如AnnualGarden 可以只返回shade和border,而不生成center的实例。

 

类的结构关系视图:

我们在图像狂内部绘制圆圈来表示阴影区,并且让各个植物绘制自己的文字,并不是在主窗口中直接绘制,而是在主窗口中包含的PictureBox类中添加一个绘图方法,这就要重写底层空间类的基础OnPaint事件。

 

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

namespace Gardener
{
	/// <summary>
	/// Summary description for GdPic.
	/// </summary>
	public class GdPic : System.Windows.Forms.PictureBox 
	{
		/// <summary> 
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.Container components = null;
		private Brush br;
		private Garden gden;
		private void init () {
			br = new SolidBrush (Color.LightGray );
		}
		public GdPic() 	{
			// This call is required by the Windows.Forms Form Designer.
			InitializeComponent();
			init();
		}
		public void setGarden(Garden garden) {
			gden = garden;
		}
		protected override void OnPaint ( PaintEventArgs pe ){
			Graphics g = pe.Graphics;
			g.FillEllipse (br, 5, 5, 100, 100);
			if(gden != null)
				gden.draw (g);	
		}

		/// <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 Component 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()
		{
			// 
			// GdPic
			// 
			
		}
		#endregion

	}
}


在单选框按钮事件被触发后,就可以根据按钮的类型生成不同的具体工厂,这样具体工厂就根据抽象工厂以及自己的需要生成不同的Plant类。

private void opAnnual_CheckedChanged(object sender, EventArgs e) {
			setGarden( new AnnualGarden ());
		}
		//-----
		private void opVegetable_CheckedChanged(object sender, EventArgs e) {
			setGarden( new VeggieGarden ());
		}
		//-----
		private void opPerennial_CheckedChanged(object sender, EventArgs e) {
			setGarden( new PerennialGarden ());
		}		
		//-----
		private void setGarden(Garden gd) {
			garden = gd;			//save current garden
			gdPic1.setGarden ( gd);	//tell picture bos
			gdPic1.Refresh ();		//repaint it
			ckCenter.Checked =false;	//clear all
			ckBorder.Checked = false;	//check
			ckShade.Checked = false;	//boxes
		}


 

当某个复选框呗选中时,设置某个Plant的可见性,然后调用重绘功能,在显示区域绘制文字。

private void ckCenter_CheckedChanged(object sender, System.EventArgs e) {
			garden.setCenter ();
			gdPic1.Refresh ();
		}
		//-----
		private void ckBorder_CheckedChanged(object sender, System.EventArgs e) {
			garden.setBorder();
			gdPic1.Refresh ();
		}
		//-----
		private void ckShade_CheckedChanged(object sender, System.EventArgs e) {
			garden.setShade ();
			gdPic1.Refresh ();
		}


抽象工厂的优点:

抽象工厂非常大的优点就是可以非常容易的添加新的子类。即具体工厂的类型可以非常容易的添加,例如可以添加玫瑰花园或者野花园。

 

抽象工厂的主要目的之一就是隔离所生成的具体类,这些类的真正类名被隐藏在工厂内部,完全不需要让客户端层面知道。

虽然抽象工厂生成的所有子类都有共同的基类,不过并不能防止这一点,即一些子类有着与其他类并不相同的一些方法。比如有些子类添加了自己的方法,这带来了在所有子类都会发生的一个问题:除非已经知道子类是否支持这些方法,否则就不能确定是否能够调用类的方法。

这一问题有两种解决方法:1.可以在基类中定义所有的方法,即使他们并不是总有实际作用。

                                           2. 提取一个新的基本接口,该接口包含所有需要的方法,然后将所有的具体工厂类作为该接口的子类,即实现该接口。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值