您的位置:首页 > 其它

设计模式 工厂方法(Factory Method Pattern)

2012-03-27 11:45 881 查看

工厂方法

工厂的概念反复出现在面向对象程序设计中,在C#本身和其他设计模式(例如生成器模式)中,就能找到几个例子。在这个例子中,有一个类负责决定在单继承体系结构中实例化哪一个字类。

  工厂方法模式(Factory Method Pattern)对这种思想进行了巧妙的扩展,它不是用一个专门的类来决定实例化那一个字类,相反,超类把这种决定延迟
到没个子类。这种设计模式实际上没有决策点,即没有直接选择一个字类实例化的决策。按照这种模式编写的程序定义了一个抽象类,他去创建对象,
但让子类决定创建哪一种对象。

  这里考虑一个相当简单的例子,在游泳比赛中为运动员确定泳道。在一个赛事中,游泳选手完成几次预赛后,按照前面预赛中最慢的到最后预赛中最快的顺序,
对运动员的成绩进行排序,在接下来的比赛中,把游的最快的选手安排在中央泳道上。这种确定泳道的方式成为直接排位。
  目前,游泳选手参加锦标赛时,通常游两次,每个选手都参加预赛,前12名或16名会在决赛在比一次,为了使预赛更公平,对预赛循环排位:
最快的三名选手安排在最快的中央道上,第二快的三名选手排在头三组的邻中央泳道上,依此类推。

  我们怎样构建对象才能实现这种用到分配机制并说明工厂模式呢?首先设计一耳光抽象类Event

using System;
using System.Collections;
using CsharpPats;

namespace Seeding
{
/// <summary>
/// Summary description for Event.
/// </summary>
public abstract class Event     {
protected int numLanes;
protected ArrayList swimmers;

public Event(string filename, int lanes) {
numLanes = lanes;
swimmers = new ArrayList();
//read in swimmers from file
csFile f = new csFile(filename);
f.OpenForRead ();
string s = f.readLine();
while (s != null) {
Swimmer sw = new Swimmer(s);
swimmers.Add (sw);
s = f.readLine();
}
f.close();
}
public abstract Seeding getSeeding();
public abstract bool isPrelim();
public abstract bool isFinal();
public abstract bool isTimedFinal();
}
}


这些抽象方法表明了具体Event类应该实现的部分。接下来就有Event类派生两个具体的类,
分别为PrelimEvent类和TimedFinalEvent类。这两个类唯一的差别就是,一个类返回的是泳道分配方法方式,另一个是返回另一种泳道分配方式。我们还定义带有如下方法的抽象类Seeding

using System;
using System.Collections ;
namespace Seeding
{
/// <summary>
/// Summary description for Seeding.
/// </summary>
public abstract class Seeding     {
protected int       numLanes;
protected int[]     lanes;
public abstract IEnumerator getSwimmers();
public abstract int getCount();
public abstract int getHeats();
protected abstract void seed();
//--------------------------------
protected void calcLaneOrder() {
lanes = new int[numLanes];
int mid = numLanes / 2;
if (odd(numLanes))
mid = mid + 1;       //start in middle lane
int incr = 1;
int ln = mid;
//create array of lanes from
//center to outside
for (int i=0; i< numLanes; i++) {
lanes[i] = ln;
ln = mid + incr;
incr = - incr;
if (incr > 0)
incr=incr+1;
}
}
//--------------------------------
private bool odd(int x) {
return(((x / 2)*2) != x);
}
}
}


接下来创建两个具体的Seeding子类:StraightSeeding 类和CircleSeeding类,PrelimEvent类会返回一个CircleSeeding的实例,而TimedFinalEvent类返回一个StraightSeeding的实例。
这样我们就有两个体系结构:一个是关于Event 的一个似乎关于Seeding 的。
在Event继承结构中(如下图)会看到两个Event的派生类,他们含有getSeeding方法一个返回StraightSeeding实例,另一个返回CircleSeeding的实例。
可以看出,它与我们前面的例子不同没有实际的工厂决策点。实例化哪一个Event类的决策就是决定实例化哪一个Seeding类 的决策。
在两个类继承体系结构中,尽管看起来是一对一的对应关系,但不是必须的。可以有许多这种Event类,而只有使用几种Seeding类。



Swimmer类
除了说过的Swimmer类含有名字、俱乐部、年龄、排位、时间以及选拔赛后的分组和泳道外,Swimmer类我们并没有介绍太多。Event类从某个数据库
读入Swimmer数据,然后再某项赛事中调用getSeeding方法时,将数据传给Seeding类。

using System;
using CsharpPats;

namespace Seeding
{
/// <summary>
/// Summary description for Swimmer.
/// </summary>
public class Swimmer
{
private string firstName, lastName;
private int age;
private string club;
private float time;

private int heat, lane;
//--------------------------------------
public Swimmer(String dataline)  {
StringTokenizer st = new StringTokenizer(dataline, " ");
string lineNumber = st.nextToken();    //ignore and discard
firstName = st.nextToken();
lastName = st.nextToken();
age = Convert.ToInt32 (st.nextToken().Trim());
club = st.nextToken().Trim();

string stime = st.nextToken().Trim();
int i = stime.IndexOf(":");
if (i > 0) {
stime = stime.Substring(0, i) + stime.Substring (i+1);
}
time = Convert.ToSingle (  stime);

}

//-------------------------------
public void setLane(int ln) {
lane = ln;
}
//-------------------------------
public int getLane() {
return lane;
}
//-------------------------------
public void setHeat(int ht) {
heat = ht;
}
//-------------------------------
public int getHeat() {
return heat;
}
//-------------------------------
public int getAge() {
return age;
}
//-------------------------------
public float getTime() {
return time;
}
//-------------------------------
public string getName() {
return firstName+" "+lastName;
}
//-------------------------------
public string getClub() {
return club;
}

}
}


Event类
我们在前面已经看到了抽象基类Event。在实际中,用它读入选手数据,并将其传给Swimmer类的一个实例去进行分析。在抽象基类Event里,判断赛事是预赛、
决赛还是计时决赛的方法都是空的、在派生类中会实现这些方法。PrelimEvent返回CircleSeeding的一个实例

using System;

namespace Seeding
{
/// <summary>
/// Summary description for PrelimEvent.
/// </summary>
public class PrelimEvent:Event
{
public PrelimEvent(string filename, int lanes):base(filename,lanes) {
}
//return circle seeding
public override Seeding getSeeding() {
return new CircleSeeding(swimmers, numLanes);
}
public override bool isPrelim() {
return true;
}
public override bool isFinal() {
return false;
}
public override bool isTimedFinal() {
return false;
}
}
}


TimedFinalEvent返回StraightSeeding的一个实例

using System;

namespace Seeding {
/// <summary>
///class describes an event that will be swum twice
/// </summary>
public class TimedFinalEvent:Event  {

public TimedFinalEvent(string filename, int lanes):base(filename, lanes) {
}
//return StraightSeeding class
public override Seeding getSeeding() {
return new StraightSeeding(swimmers, numLanes);
}
public override bool isPrelim() {
return false;
}
public override bool isFinal() {
return false;
}
public override bool isTimedFinal() {
return true;
}
}
}


直接排位
实际编写这个程序时,我们发现大多数工作都是在直接排位(StraightSeeding)中完成的,为了循环排位(CircleSeeding)而进行的改动相当少。
实例化StraightSeeding类,并拷如选手和泳道号。

protected override void seed() {
//loads the swmrs array and sorts it
sortUpwards();

int lastHeat = count % numLanes;
if (lastHeat < 3)
lastHeat = 3;   //last heat must have 3 or more
int lastLanes = count - lastHeat;
numHeats = count / numLanes;
if (lastLanes > 0)
numHeats++;
int heats = numHeats;

//place heat and lane in each swimmer's object
int j = 0;
for (int i = 0; i < lastLanes; i++) {
Swimmer sw = swmrs[i];
sw.setLane(lanes[j++]);
sw.setHeat(heats);
if (j >= numLanes) {
heats--;
j=0;
}
}
//Add in last partial heat
if (j < numLanes)
heats--;
j = 0;
for (int i = lastLanes-1; i<count; i++) {
Swimmer sw = swmrs[i];
sw.setLane(lanes[j++]);
sw.setHeat(heats);
}
//copy from array back into ArrayList
swimmers = new ArrayList();
for (int i=0; i< count; i++)
swimmers.Add(swmrs[i]);
}


循环排位
CircleSeeding 类从StraightSeeding类派生类中,因此一开始先调用父类的seed方法,然后重新安排赛事

protected override void seed() {
int circle;

base.seed();        //do straight seed as default
if (numHeats >= 2 ) {
if (numHeats >= 3)
circle = 3;
else
circle = 2;
int i = 0;
for (int j = 0; j < numLanes; j++) {
for (int k = 0; k < circle; k++) {
swmrs[i].setLane(lanes[j]);
swmrs[i++].setHeat(numHeats - k);
}
}
}
}


其他工厂

我们在前面略过了一个问题:读入运动员数据程序如何决定生成那一项赛事。再读入数据时,通过正确类型的事件来巧妙的实现这一点。

private void init() {
//create array of events
events = new ArrayList ();
lsEvents.Items.Add ("500 Free");
lsEvents.Items.Add ("100 Free");
//and read in their data
events.Add (new TimedFinalEvent ("500free.txt", 6));
events.Add (new PrelimEvent ("100free.txt", 6));
}


很明显、这是一个需要EventFactory来决定生成哪一项赛事的例子,这里有涉及到前面的简单工厂了!...

什么场景下能使用工厂模式如一下情况

1、一类无法预测他要创建的对象属于那一个类。

2、一类用它的子类来指定所创建的对象。

3、把要创建哪一类的信息局部化的时候。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: