抽象工厂(Abstract Factory)模式是比工厂模式更高一层的抽象在希望返回对象的几个相关类中的一个时可以使用该模式每个类都能够根据需要返回几个不同的对象换句话说抽象工厂是一个工厂对象其返回几组类中的一组
更形象的说抽象工厂模式需要存在一个抽象工厂然后每个具体的工厂继承这个设计好的抽象工厂每个具体工厂可以根据需要返回自己需要的类具体工厂通过抽象工厂返回不同的类
下面举个具体的例子
在一块土地上设计一个园地我们可以把园地设计成一年生的植物园也可以设计成菜园或者多年生的植物园不论设计成哪一种园地都会设计到这样的一些问题哪些植物应该种在边上哪些植物适宜种在中间哪些植物是喜阴的特性等等
我们设计一个Garden的类作为一个抽象工厂这个类中包含了植物的可能的种类然后实现这个类的具体工厂就可以根据需要生成不同植物的实例
抽象工厂Garden类的设计
[html] view plaincopy using Systemusing SystemDrawing namespace Gardener { /// <summary> /// Summary description for Garden /// </summary> public class Garden { protected Plant center shade borderprotected 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) centerdraw (g )if (showShade) shadedraw (g )if (showBorder) borderdraw (g )}
在Garden类中可以看到抽象工厂可以包含的Plant类的种类有centershadeborder三种类型
Plant类的设计简单的给出如下
[html] view plaincopy using Systemusing SystemDrawing
namespace Gardener { /// <summary> /// Summary description for Plant /// </summary> public class Plant { private string nameprivate Brush brprivate Font font
public Plant(string pname) { name = pname //save name font = new Font (Arial )br = new SolidBrush (ColorBlack )} //——public void draw(Graphics g int x int y) { gDrawString (name font br x y)}
Plant类中的draw()方法是用来在显示区域绘制文字的
Garden这一作为接口的类是抽象工厂其中定义了类的方法继承了Garden类的具体工厂能够返回Garden类中规定的类的几个这里Garden类规定的返回的类可以有centershadeborder三种类型
在继承Garden类的子类中可以根据自己的需要返回需要的Plant类下面给出Garden的一个子类的定义VeggieGarden类的定义如下
[html] view plaincopy 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类的定义如下
[html] view plaincopy 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类的定义如下
[html] view plaincopy using Systemusing SystemDrawing
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事件
[html] view plaincopy using Systemusing SystemCollectionsusing SystemComponentModelusing SystemDrawingusing SystemDatausing SystemWindowsForms
namespace Gardener { /// <summary> /// Summary description for GdPic /// </summary> public class GdPic SystemWindowsFormsPictureBox { /// <summary> /// Required designer variable /// </summary> private SystemComponentModelContainer components = nullprivate Brush brprivate Garden gdenprivate void init () { br = new SolidBrush (ColorLightGray )} public GdPic() { // This call is required by the WindowsForms Form Designer InitializeComponent()init()} public void setGarden(Garden garden) { gden = garden} protected override void OnPaint ( PaintEventArgs pe ){ Graphics g = peGraphicsgFillEllipse (br )if(gden != null)
gdendraw (g)}
/// <summary> /// Clean up any resources being used /// </summary> protected override void Dispose( bool disposing )
{ if( disposing )
{ if(components != null)
{ componentsDispose()} baseDispose( 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类
[html] view plaincopy 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 gdPicsetGarden ( gd) //tell picture bos gdPicRefresh () //repaint it ckCenterChecked =false //clear all ckBorderChecked = false //check ckShadeChecked = false //boxes }
当某个复选框呗选中时设置某个Plant的可见性然后调用重绘功能在显示区域绘制文字
[csharp] view plaincopy private void ckCenter_CheckedChanged(object sender SystemEventArgs e) { gardensetCenter ()gdPicRefresh ()} //——private void ckBorder_CheckedChanged(object sender SystemEventArgs e) { gardensetBorder()gdPicRefresh ()} //——private void ckShade_CheckedChanged(object sender SystemEventArgs e) { gardensetShade ()gdPicRefresh ()}
抽象工厂的优点
抽象工厂非常大的优点就是可以非常容易的添加新的子类即具体工厂的类型可以非常容易的添加例如可以添加玫瑰花园或者野花园
抽象工厂的主要目的之一就是隔离所生成的具体类这些类的真正类名被隐藏在工厂内部完全不需要让客户端层面知道
虽然抽象工厂生成的所有子类都有共同的基类不过并不能防止这一点即一些子类有着与其他类并不相同的一些方法比如有些子类添加了自己的方法这带来了在所有子类都会发生的一个问题除非已经知道子类是否支持这些方法否则就不能确定是否能够调用类的方法
这一问题有两种解决方法可以在基类中定义所有的方法即使他们并不是总有实际作用
提取一个新的基本接口该接口包含所有需要的方法然后将所有的具体工厂类作为该接口的子类即实现该接口