简单工厂模式之二-----简单工厂
在简单工厂模式中,需要由某个类充当指挥者,决定集成层次中的哪一个子类被实例化。
工厂方法(Factory Method)模式是这一想法的一个聪明而又巧妙的扩展,在工厂方法模式中,不会由单个类来决定实例化哪一个子类,相反,父类把这一决定推迟到了每一个子类中。该模式实际上并不存在一个决策点,某个子类由另一个类直接选中,依照这一模式编写的程序定义了一个抽象类,该类创建对象,但是让每个子类自己来决定创建哪一个对象。
拿游泳比赛泳道排位的算法来做个比较,由于我看到资料中游泳比赛的排位描述不清,所以我们这里不研究游泳比赛具体的排位算法,只是探讨工厂方法模式的应用。
简单的知道游泳比赛排位可以分为直接排位和循环排位。
类的层次结构视图。
我们设计一个赛事类,为Event类,类的定义如下:
<pre class="html" name="code">using System; 02.using System.Collections; 03.using CsharpPats; 04. 05.namespace Seeding 06.{ 07. /// <summary> 08. /// Summary description for Event. 09. /// </summary> 10. public abstract class Event { 11. protected int numLanes; 12. protected ArrayList swimmers; 13. 14. public Event(string filename, int lanes) { 15. numLanes = lanes; 16. swimmers = new ArrayList(); 17. //read in swimmers from file 18. csFile f = new csFile(filename); 19. f.OpenForRead (); 20. string s = f.readLine(); 21. while (s != null) { 22. Swimmer sw = new Swimmer(s); 23. swimmers.Add (sw); 24. s = f.readLine(); 25. } 26. f.close(); 27. } 28. public abstract Seeding getSeeding(); 29. public abstract bool isPrelim(); 30. public abstract bool isFinal(); 31. public abstract bool isTimedFinal(); 32. } 33.}</pre><br> 34.<pre></pre> 35.<p> </p> 36.<p>接着我们可以派生两个赛事类,都继承了Event 类,这两个派生的名字分别为PrelimEvent和 TimedFinalEvent,这两个类的不同之处仅仅在于一个返回某种排位方式而另外一个类返回另外一种排位方式。</p> 37.<p> </p> 38.<p>我们定义一个抽象的排位类Seeding:</p> 39.<pre class="html" name="code">using System; 40.using System.Collections ; 41.namespace Seeding 42.{ 43. /// <summary> 44. /// Summary description for Seeding. 45. /// </summary> 46. public abstract class Seeding { 47. protected int numLanes; 48. protected int[] lanes; 49. public abstract IEnumerator getSwimmers(); 50. public abstract int getCount(); 51. public abstract int getHeats(); 52. protected abstract void seed(); 53. //-------------------------------- 54. protected void calcLaneOrder() { 55. lanes = new int[numLanes]; 56. int mid = numLanes / 2; 57. if (odd(numLanes)) 58. mid = mid + 1; //start in middle lane 59. int incr = 1; 60. int ln = mid; 61. //create array of lanes from 62. //center to outside 63. for (int i=0; i< numLanes; i++) { 64. lanes[i] = ln; 65. ln = mid + incr; 66. incr = - incr; 67. if (incr > 0) 68. incr=incr+1; 69. } 70. } 71. //-------------------------------- 72. private bool odd(int x) { 73. return(((x / 2)*2) != x); 74. } 75. } 76.} 77.</pre> 78.<p><br> 79.接下来我们可以创建两个具体的排位子类:StraightSeeding和CircleSeeding。PrelimEvent将会返回CircleSeeding类的一个实例,而TimedFinalEvent类则返回StraightSeeding的一个实例。这样以来,我们就有两个层次结构,一个是Event类的,一个是Seeding类的。</p> 80.<p>在Event类的层次结构中,你会看到两个派生于Event类的类都包含一个getSeeding()方法,其中的一个返回StraightSeeding,而另外一个则返回CircleSeeding。这样,我们可以看出,这里不存在一个实际上的工厂决策点,而是由实例化哪一个Event类的决定来确定哪一个Seeding的子类会呗实例化。</p> 81.<p>简单的说,就是在Event类的子类中,不同的子类创建不同的Seeding类的子类对象,创建不同的Event类的子类,就对应着创建了不同的Seeding类的子类,而不是像简单工厂模式那样存在一个决策点来决定创建哪一个子类。</p> 82.<p> </p> 83.<p>程序中用到了Swimmer类,这个类包含了姓名,所属俱乐部年龄排名时间记录泳道等信息。</p> 84.<p> </p> 85.<p>在Event类的基础上,我们派生了PrelimEvent和 TimedFinalEvent两个类,Event基类中包含了判断赛事是预赛还是决赛或者计时决赛的空方法。</p> 86.<p>PrelimEvent的定义:</p> 87.<pre class="html" name="code">using System; 88. 89.namespace Seeding 90.{ 91. /// <summary> 92. /// Summary description for PrelimEvent. 93. /// </summary> 94. public class PrelimEvent:Event 95. { 96. public PrelimEvent(string filename, int lanes):base(filename,lanes) { 97. } 98. //return circle seeding 99. public override Seeding getSeeding() { 100. return new CircleSeeding(swimmers, numLanes); 101. } 102. public override bool isPrelim() { 103. return true; 104. } 105. public override bool isFinal() { 106. return false; 107. } 108. public override bool isTimedFinal() { 109. return false; 110. } 111. } 112.}</pre><pre class="html" name="code"> </pre><pre class="html" name="code">请注意,PrelimEvent类中,getSeeding()方法返回的是CircleSeeding类。</pre><pre class="html" name="code">TimedFinalEvent的定义:</pre><pre class="html" name="code"><pre class="html" name="code">using System; 113. 114.namespace Seeding { 115. /// <summary> 116. ///class describes an event that will be swum twice 117. /// </summary> 118. public class TimedFinalEvent:Event { 119. 120. public TimedFinalEvent(string filename, int lanes):base(filename, lanes) { 121. } 122. //return StraightSeeding class 123. public override Seeding getSeeding() { 124. return new StraightSeeding(swimmers, numLanes); 125. } 126. public override bool isPrelim() { 127. return false; 128. } 129. public override bool isFinal() { 130. return false; 131. } 132. public override bool isTimedFinal() { 133. return true; 134. } 135. } 136.} 137.</pre><br> 138.<pre></pre> 139.<pre class="html" name="code">在这个类中,,getSeeding()方法返回的是StraightSeeding类。</pre><pre class="html" name="code"> </pre><pre class="html" name="code">根据返回的排序类的不同,就可以在不同的赛事中做出不同的排序。</pre><pre class="html" name="code"> </pre><pre class="html" name="code">StraightSeeding和CircleSeeding两个子类的实现这里不给出了,我们不用关心具体的算法问题。</pre><pre class="html" name="code">类的层次结构视图如图:见上。</pre><pre class="html" name="code"> </pre><pre class="html" name="code"> </pre><pre class="html" name="code">在应用程序初始化的时候,调用了如下的代码:</pre><pre class="html" name="code"><pre class="html" name="code">private void init() { 140. //create array of events 141. events = new ArrayList (); 142. lsEvents.Items.Add ("500 Free"); 143. lsEvents.Items.Add ("100 Free"); 144. //and read in their data 145. events.Add (new TimedFinalEvent ("500free.txt", 6)); 146. events.Add (new PrelimEvent ("100free.txt", 6)); 147. }</pre><br> 148.<pre></pre> 149.<pre class="html" name="code">这个过程创建了两个Event类的子类的实例,每个子类一个实例,所以在工厂方法模式中,不存在一个决策点来决定创建哪个子类,而是将这一决定推迟到每一个子类中,例如我们这里的例子中,每一个Event子类对应一个排序类的引用,先是生成多个Event类子类的实例,但是到最后应用的时候只有一个实例被选中。</pre><pre class="html" name="code">事件的调用:</pre><pre class="html" name="code"><pre class="html" name="code">private void lsEvents_SelectedIndexChanged(object sender, System.EventArgs e) { 150. int index = lsEvents.SelectedIndex ; 151. Event ev = (Event)events[index]; 152. Seeding sd = ev.getSeeding(); 153. IEnumerator en = sd.getSwimmers(); 154. lsSwimmers.Items.Clear() ; 155. while(en.MoveNext ()) { 156. Swimmer sw = (Swimmer)en.Current ; 157. lsSwimmers.Items.Add(sw.getHeat()+" "+sw.getLane()+" "+sw.getName()+" "+sw.getTime()); 158. } 159. }</pre><br> 160.<pre></pre> 161.<pre class="html" name="code">这段代码中,通过获取列表框中的选取值来判断Event类的哪一个子类应该呗选中,然后通过这个选中子类调用这个子类对应的Seeding类的哪个子类,从而进行排序。</pre><pre class="html" name="code">使用工厂方法模式还是利用了面向对象的多态性,利用一个Event类的数组来保存对所有派生类实例的引用,然后确定应该选中哪一个子类,由于多态性,利用Event类的方法即可调用这个派生类的派生方法。</pre><pre class="html" name="code">使用工厂方法模式应该注意的问题:</pre><pre class="html" name="code">1. 类不能预测他必须创建哪一种类型的对象。</pre><pre class="html" name="code">2. 类使用他的子类来指定要创建的对象。</pre><pre class="html" name="code">3. 希望只有局部知晓哪个类会被创建。</pre> 162.<pre></pre> 163.<pre></pre> 164.<pre></pre> 165.<pre></pre> 166.<pre></pre> 167.<pre></pre> 168.<pre></pre> 169.<pre></pre> 170. 171.</pre></pre></pre>
地狱的镰刀