工厂模式一般都是根据传递给创建方法的参数来返回不同的几个子类的一个,不过如果我们需要的不仅仅是一个算法,还需要显示数据,不同的子类要有不同的显示数据方法,需要一个完全不同的用户界面,这样我们就可能需要生成器模式了。
生成器模式中,返回的并不是基本显示对象的子类简单代码,而是显示对象的不同组合构成的完全不同的用户界面。生成器模式根据数据,以多种方式把注入显示控件一类的许多对象组装在一起,通过使用类表示数据,用窗体表示显示,可以吧数据和显示方式清晰的分离到简单的对象中。
换言之,生成器模式返回的用户界面不同,就是生成器中不同生成器根据数据的不同逐步构造最终的产品,并且最后返回这个生成器构造出来的产品,但是这个最后返回的产品却有着不同的用户界面。由不同的生成对象构造完全不同的用户界面。
简单的说就是不同类返回不同的用户界面。
上面的描述可能有点费解,可以参考下面的例子。
有三种类型的投资:股票,债券,共同基金。我们的程序中点击其中的一个,就可以看到这个类别下面的多种选择,比如选择股票以后就可以看到很多公司的股票信息;这样就是说,有些投资可以有很多的种类,但是有些投资比如 基金可能就只有少数的几种类别。因为这个差异,我们的程序中就需要用不同的方式给用户显示不同投资下的类别信息,这样就需要不同的显示信息。例如,某种投资下的类别很多,需要用列表框来显示;如果某种投资下的类别少,就用复选框来表示。
如图:第二列就是两种不同的显示方式,第一列中则表示了多种可以选择的投资方式。
首先定义一个接口,用来作为所有的显示方式的接口:
using System;
using System.Collections ;
using System.Windows.Forms;
/// <summary>
/// Summary description for MultiChoice.
/// </summary>
public interface MultiChoice
{
ArrayList getSelected();
void clear();
Panel getWindow();
}
接口中规定了返回一个Panel的方法,Panel是C#中的一个容器,可以用来显示一些窗体控件。Panel类可以放在窗体中指定的位置。当显示发生改变以后,我们把旧的Panel移除,然后添加新的Panel.
然后我们需要的两种显示方式分别为:CheckChoice和ListChoice,这两种显示方式应该都从接口中继承而来,这样也可以通过接口来访问两个类的实例。
创建一个所有投资类型的抽象基类:
using System;
using System.Collections ;
/// <summary>
/// Summary description for Equities.
/// </summary>
public abstract class Equities {
protected ArrayList array;
public abstract string ToString();
//----------
public ArrayList getNames() {
return array;
}
//----------
public int count() {
return array.Count ;
}
}
然后就可以继承这个抽象类来实现每个具体的投资类型的类了,每个投资类型的类在构造函数中添加这个类中包含的类型。
股票类:
using System;
using System.Collections ;
/// <summary>
/// Summary description for Stocks.
/// </summary>
public class Stocks:Equities {
public Stocks() {
array = new ArrayList();
array.Add ("Cisco");
array.Add ("Coca Cola");
array.Add ("GE");
array.Add ("Harley Davidson");
array.Add ("IBM");
array.Add ("Microsoft");
}
public override string ToString() {
return "Stocks";
}
}
债券类:
using System;
using System.Collections ;
/// <summary>
/// Summary description for Bonds.
/// </summary>
public class Bonds:Equities
{
public Bonds()
{
array = new ArrayList();
array.Add ("CT GO 2005");
array.Add ("NY GO 2012");
array.Add ("GE Corp Bonds");
}
public override string ToString() {
return "Bonds";
}
}
共同基金类:
using System;
using System.Collections ;
/// <summary>
/// Summary description for Mutuals.
/// </summary>
public class Mutuals:Equities
{
public Mutuals()
{
array = new ArrayList();
array.Add ("Fidelity Magellan");
array.Add ("Lindner");
array.Add ("T Rowe Price");
array.Add ("Vanguard Primecap");
}
public override string ToString() {
return "Mutuals";
}
}
这三个子类都重写了父类的ToString()方法,可以通过多态性进行访问。
然后我们需要一个很小的类来决定返回一个CheckChoice类还是ListChoice类,我们把这个类称为StockFactory,不过我们这里永远都只需要一个这个类的实例,所以把这个决定返回什么类的方法定义为静态的,这样就只用通过类名调用就可以了。这里用到了简单工厂模式。
using System;
/// <summary>
/// Summary description for StockFactory.
/// </summary>
public class StockFactory
{
public static MultiChoice getBuilder(Equities stocks)
{
if (stocks.count ()<=3) {
return new CheckChoice (stocks);
}
else {
return new ListChoice(stocks);
}
}
}
这一简单工厂被成为是导向器,而派生于MultiChoice的CheckChoice和ListChoice两个类才是真正的生成器。 这里的生成器根据传送过来的数据逐步的构造最终的产品,然后把这个最终的返回返回给调用者,呈现给用户的则是完全不同的用户界面。
CheckChoice类:
using System;
using System.Collections ;
using System.Windows.Forms ;
using System.Drawing ;
//returns a panel of 0 to 3 check boxes
public class CheckChoice:MultiChoice {
private ArrayList stocks;
private Panel panel;
private ArrayList boxes;
//------
public CheckChoice(Equities stks) {
stocks = stks.getNames ();
panel = new Panel ();
boxes = new ArrayList ();
//add the check boxes to the panel
for (int i=0; i< stocks.Count; i++) {
CheckBox ck = new CheckBox ();
//position them
ck.Location = new Point (8, 16 + i * 32);
string stk = (string)stocks[i];
ck.Text =stk;
ck.Size = new Size (112, 24);
ck.TabIndex =0;
ck.TextAlign = ContentAlignment.MiddleLeft ;
boxes.Add (ck);
panel.Controls.Add (ck);
}
}
//------
//uncheck all check boxes
public void clear() {
for(int i=0; i< boxes.Count; i++) {
CheckBox ck = (CheckBox)boxes[i];
ck.Checked =false;
}
}
//------
//return list of checked items
public ArrayList getSelected() {
ArrayList sels = new ArrayList ();
for(int i=0; i< boxes.Count ; i++) {
CheckBox ck = (CheckBox)boxes[i];
if (ck.Checked ) {
sels.Add (ck.Text );
}
}
return sels;
}
//------
//return panel of checkboxes
public Panel getWindow() {
return panel;
}
}
ListChoice类:
using System;
using System.Collections ;
using System.Windows.Forms ;
using System.Drawing ;
/// <summary>
/// Summary description for ListChoice.
/// </summary>
/// creates a Panel containing a list box
public class ListChoice:MultiChoice {
private ArrayList stocks;
private Panel panel;
private ListBox list;
//------
//constructor creates and loads the list box
public ListChoice(Equities stks) {
stocks = stks.getNames ();
panel = new Panel ();
list = new ListBox ();
list.Location = new Point (16, 0);
list.Size = new Size (120, 160);
list.SelectionMode =SelectionMode.MultiExtended ;
list.TabIndex =0;
panel.Controls.Add (list);
for(int i=0; i< stocks.Count ; i++) {
list.Items.Add (stocks[i]);
}
}
//returns the Panel
//------
public Panel getWindow() {
return panel;
}
//returns an array of selected elements
//------
public ArrayList getSelected() {
ArrayList sels = new ArrayList ();
ListBox.SelectedObjectCollection coll = list.SelectedItems ;
for(int i=0; i< coll.Count; i++) {
string item = (string)coll[i];
sels.Add (item );
}
return sels;
}
//------
//clear selected elements
public void clear() {
list.Items.Clear();
}
}
C#中的ListBox不仅可以使用字符串来填充,还可以使用对象类型来填充。
在主窗体的初始化过程中,就给投资类型的列表中填充不同的对象:
private Container components = null;
private Button btPlot;
private Panel pnl;
private MultiChoice mchoice;
private void init() {
lsEquities.Items.Add (new Stocks());
lsEquities.Items.Add (new Bonds());
lsEquities.Items.Add (new Mutuals());
}
这个时候,如果单击左侧列表,就会出发相应的事件,然后在事件处理程序中,判断选择的对象类型,然后根据这个类型进行生成器创建,这样就最终给用户显示不同的界面。
private void lsEquities_SelectedIndexChanged(object sender, System.EventArgs e) {
int i = lsEquities.SelectedIndex ;
Equities eq = (Equities)lsEquities.Items[i];
mchoice= StockFactory.getBuilder (eq);
this.Controls.Remove (pnl);
pnl = mchoice.getWindow ();
setPanel();
}
private void setPanel() {
pnl.Location = new Point(152, 24);
pnl.Size = new Size(128, 168);
pnl.TabIndex = 1;
Controls.Add(pnl);
}
Equities eq = (Equities)lsEquities.Items[i]; // 这里根据用户选择的投资类型,通过一个抽象基类的引用指向实际的投资类型实例。
mchoice= StockFactory.getBuilder (eq); // 这里通过简单工厂调用不同的生成器构造最终的产品。
绘制图标则根据用户选择的某种投资类型下的列别进行信息输出
类别之间的关系图:
总结: 生成器模式主要的意思就是, 导向器调根据数据用不同的生成器,不同的生成器则根据数据逐步的构造最终产品,然后把这个最终产品返回给用户,呈现出完全不同的仙湖界面。
生成器允许改变其生成的产品的内部表示,并且隐藏了产品组装的过程的细节。
每个特定的生成器都能独立于其他的生成器和程序的其他部分,提高了模块化程度并且使得添加其他的生成器变得简单。
因为每个生成器都是根据数据来逐步的构造出最终的产品,因此对生成器构造的每个最终产品可以拥有更多的控制。