抽象工厂模式,比工厂模式具有更高层次的抽象。当要返回一系列相关类中的某一个,而每个类都能根据需要返回不同的对象时,可以使用这种模式。换句话说,抽象工厂是一个工厂对象,他能返回一系列相关类中的一个类。可以用简单工厂决定返回那个类。通常认为,实验式的例子应该引用汽车制造产,我们希望丰田汽车完全使用丰田配件,而福特汽车完全使用福特配件。可以把每个汽车制造厂设想为抽象工厂,配件作为一组相关的类。
花园规划工厂
我们可以考虑一下实际的例子,字应用程序里要用到抽象工厂。假设读者编写一个程序来规划花园,这可能是季生植物型花园、蔬菜型花园、
或者多年植物花园。不管规划哪种类型的花园,都会遇到如下问题:
1.边缘种植什么植物
2.中央种植什么植物
3.阴凉部分种植什么植物
(可能还会有许多类似的问题,我们这里假设以上三种)这里想设计一个基类Garden(花园),用类中的方法回答这些问题。
1 using System;
2 using System.Drawing ;
3 namespace Gardener
4 {
5 /// <summary>
6 /// Summary description for Garden.
7 /// </summary>
8 public class Garden {
9 protected Plant center, shade, border;
10 protected bool showCenter, showShade, showBorder;
11 //select which ones to display
12 public void setCenter() {showCenter = true;}
13 public void setBorder() {showBorder =true;}
14 public void setShade() {showShade =true;}
15 //draw each plant
16 public void draw(Graphics g) {
17 if (showCenter) center.draw (g, 100, 100);
18 if (showShade) shade.draw (g, 10, 50);
19 if (showBorder) border.draw (g, 50, 150);
20 }
21 }
22 }
Plant对象设置植物的名字,在draww方法被调用时绘制自己
1 using System;
2 using System.Drawing;
3
4 namespace Gardener
5 {
6 /// <summary>
7 /// Summary description for Plant.
8 /// </summary>
9 public class Plant {
10 private string name;
11 private Brush br;
12 private Font font;
13
14 public Plant(string pname) {
15 name = pname; //save name
16 font = new Font ("Arial", 12);
17 br = new SolidBrush (Color.Black );
18 }
19 //-------------
20 public void draw(Graphics g, int x, int y) {
21 g.DrawString (name, font, br, x, y);
22 }
23 }
24 }
用设计模式术语来讲,Garden接口就是抽象工厂。它定义了具体类中的方法,并返回一系列相关类中的某个类。这里将中央植物,边缘植物
喜阴植物作为三个相关类。抽象工厂也能返回更具体的花园信息,例如土壤的PH值或灌溉需等。
在实际中,规划每一种类型的花园都要查阅一个详尽的植物信息库,而在这个简单的例子里,没类植物只给出一种。例如,对蔬菜花园,只给出下列几种植物
1 using System;
2
3 namespace Gardener
4 {
5 /// <summary>
6 /// Summary description for VeggieGarden.
7 /// </summary>
8 public class VeggieGarden : Garden {
9 public VeggieGarden() {
10 shade = new Plant("Broccoli");
11 border = new Plant ("Peas");
12 center = new Plant ("Corn");
13 }
14 }
15 }
我们用类似的方法创建Garden类的两个子类:PerennialGarden和AnnualGarden。因为每个具体类都实现了父类中的方法,所以都可以看做一个
具体的工厂。现在我们有了一系列的Garden对象,每一个对象都能创建一类Plant对象。在下图中说明了这一点
我们很容易就构建出了工厂的驱动程序,他根据用户选的单选按钮返回一个Garden对象,用户界面如下。每一次选择一种新的花园类型时,都要屏幕,将复选框设置为未选中状态。然后选择一个复选框,画出相应的植物类型。
由于每种花园(及植物)都要知道如何绘制自己,所以应该有一个draw方法,在屏幕上画出相应的植物名,由于我们用复选框来说明要话的植物类型,所以设置了一个布尔型变量,用它指出要画的每种植物类型,Garden对象包含了三个设置方法,用于指出绘制的每一种类型的植物。
1 public void setCenter() {showCenter = true;}
2 public void setBorder() {showBorder =true;}
3 public void setShade() {showShade =true;}
我们在图片框(pictureBox)里用元表示阴影区域,在图片框中还要给出植物的名字。从PictureBox类派生出一个新类GardenPic,并将画圆和
花园植物名称等信息传递给它,这是完成上述任务最好的方法,因此,不仅需要一耳光GardenMake窗口类中添加一个Paint的方法,还要在该窗口所
包含的PictureBox类中添加该方法。它覆盖了基类Control中的OnPaint事件。
1 public class GdPic : System.Windows.Forms.PictureBox
2 {
3 /// <summary>
4 /// Required designer variable.
5 /// </summary>
6 private System.ComponentModel.Container components = null;
7 private Brush br;
8 private Garden gden;
9 private void init () {
10 br = new SolidBrush (Color.LightGray );
11 }
12 public GdPic() {
13 // This call is required by the Windows.Forms Form Designer.
14 InitializeComponent();
15 init();
16 }
17 public void setGarden(Garden garden) {
18 gden = garden;
19 }
20 protected override void OnPaint ( PaintEventArgs pe ){
21 Graphics g = pe.Graphics;
22 g.FillEllipse (br, 5, 5, 100, 100);
23 if(gden != null)
24 gden.draw (g);
25 }
处理单选按钮盒按钮事件,单机三个单选按钮中某一个,就会创建一个该类型的花园,并把它传给图片框,还要清除所有的复选框。
1 private void opAnnual_CheckedChanged(object sender, EventArgs e) {
2 setGarden( new AnnualGarden ());
3 }
4 //-----
5 private void opVegetable_CheckedChanged(object sender, EventArgs e) {
6 setGarden( new VeggieGarden ());
7 }
8 //-----
9 private void opPerennial_CheckedChanged(object sender, EventArgs e) {
10 setGarden( new PerennialGarden ());
11 }
12 //-----
13 private void setGarden(Garden gd) {
14 garden = gd; //save current garden
15 gdPic1.setGarden ( gd); //tell picture bos
16 gdPic1.Refresh (); //repaint it
17 ckCenter.Checked =false; //clear all
18 ckBorder.Checked = false; //check
19 ckShade.Checked = false; //boxes
20 }
单击一个复选框曲线是相应的植物名,要调用对应的花园方法去设置要显示的名称,然后调用图片框的Refresh方法重画。
1 private void ckCenter_CheckedChanged(object sender, System.EventArgs e) {抽象工厂效果
2 garden.setCenter ();
3 gdPic1.Refresh ();
4 }
5 //-----
6 private void ckBorder_CheckedChanged(object sender, System.EventArgs e) {
7 garden.setBorder();
8 gdPic1.Refresh ();
9 }
10 //-----
11 private void ckShade_CheckedChanged(object sender, System.EventArgs e) {
12 garden.setShade ();
13 gdPic1.Refresh ();
14 }
抽象工厂的一个主要目的是要能隔离要生成的工具类。这些类的实际类名隐藏在工厂里,在客户端根本不知道。由于类的隔离,可以自由改动或交换这些
生成类系列。此外由于只要生成一类具体的类,系统会避免读者误用不同生成系类中的类。但是添加新的类系列要费一些心思 ,因为读者要定义一些
心的、无二义性的条件使工厂返回新的类系列。尽管抽象工厂生成的所有类都是有相同的基类,但还是无法避免某些子类具有额外的、与其他类不同的方法,
这中问题有两种解决方案:或者在基类中定义所有的方法,或者在是再派生出一个新的基类接口,它包含读者需要的所有方法和所有花园类型的子类。