设计模式 工厂方法(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类。
![](http://pic002.cnblogs.com/images/2012/359991/2012032710593143.png)
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、把要创建哪一类的信息局部化的时候。
相关文章推荐
- 设计模式 工厂方法(Factory Method Pattern)转载
- VB.net设计模式之工厂方法,抽象工厂方法(Factory Method Pattern,Abstract Factory Method Pattern)
- 23种设计模式-FactoryMethod(工厂方法)设计模式
- Java:设计模式之工厂方法(Factory Method)
- 设计模式学习笔记四:工厂方法(Factory Method)
- 设计模式学习之工厂方法(Factory Method,创建型模式)(2)
- JAVA设计模式——工厂方法(Factory Method)模式
- 设计模式-创建型模式之 Factory Method(工厂方法)
- 设计模式 - 工厂方法(Factory Method) C++实现
- 设计模式(八)[工厂方法(Factory Method)模式]
- 【设计模式】——工厂方法FactoryMethod
- 设计模式--工厂方法(FactoryMethod)
- 设计模式之工厂方法(Factory Method)模式
- 23设计模式之工厂方法(FactoryMethod)
- 深入浅出设计模式 ------ Factory Method(工厂方法)
- 设计模式之3 - 工厂方法Factory Method
- 设计模式------工厂方法(Factory Method)
- 设计模式-创建型-Factory Method(工厂方法)
- 设计模式--工厂方法(FactoryMethod)
- 设计模式——工厂方法Factory Method