zoukankan      html  css  js  c++  java
  • 设计模式 工厂方法(Factory Method Pattern)转载

    工厂方法

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

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


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

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

     1 using System;
    2 using System.Collections;
    3 using CsharpPats;
    4
    5 namespace Seeding
    6 {
    7 /// <summary>
    8 /// Summary description for Event.
    9 /// </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 }

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

     1 using System;
    2 using System.Collections ;
    3 namespace Seeding
    4 {
    5 /// <summary>
    6 /// Summary description for Seeding.
    7 /// </summary>
    8 public abstract class Seeding {
    9 protected int numLanes;
    10 protected int[] lanes;
    11 public abstract IEnumerator getSwimmers();
    12 public abstract int getCount();
    13 public abstract int getHeats();
    14 protected abstract void seed();
    15 //--------------------------------
    16 protected void calcLaneOrder() {
    17 lanes = new int[numLanes];
    18 int mid = numLanes / 2;
    19 if (odd(numLanes))
    20 mid = mid 1; //start in middle lane
    21 int incr = 1;
    22 int ln = mid;
    23 //create array of lanes from
    24 //center to outside
    25 for (int i=0; i< numLanes; i ) {
    26 lanes[i] = ln;
    27 ln = mid incr;
    28 incr = - incr;
    29 if (incr > 0)
    30 incr=incr 1;
    31 }
    32 }
    33 //--------------------------------
    34 private bool odd(int x) {
    35 return(((x / 2)*2) != x);
    36 }
    37 }
    38 }
     

    接下来创建两个具体的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类。

     1 using System;
    2 using CsharpPats;
    3
    4 namespace Seeding
    5 {
    6 /// <summary>
    7 /// Summary description for Swimmer.
    8 /// </summary>
    9 public class Swimmer
    10 {
    11 private string firstName, lastName;
    12 private int age;
    13 private string club;
    14 private float time;
    15
    16 private int heat, lane;
    17 //--------------------------------------
    18 public Swimmer(String dataline) {
    19 StringTokenizer st = new StringTokenizer(dataline, " ");
    20 string lineNumber = st.nextToken(); //ignore and discard
    21 firstName = st.nextToken();
    22 lastName = st.nextToken();
    23 age = Convert.ToInt32 (st.nextToken().Trim());
    24 club = st.nextToken().Trim();
    25
    26 string stime = st.nextToken().Trim();
    27 int i = stime.IndexOf(":");
    28 if (i > 0) {
    29 stime = stime.Substring(0, i) stime.Substring (i 1);
    30 }
    31 time = Convert.ToSingle ( stime);
    32
    33 }
    34
    35 //-------------------------------
    36 public void setLane(int ln) {
    37 lane = ln;
    38 }
    39 //-------------------------------
    40 public int getLane() {
    41 return lane;
    42 }
    43 //-------------------------------
    44 public void setHeat(int ht) {
    45 heat = ht;
    46 }
    47 //-------------------------------
    48 public int getHeat() {
    49 return heat;
    50 }
    51 //-------------------------------
    52 public int getAge() {
    53 return age;
    54 }
    55 //-------------------------------
    56 public float getTime() {
    57 return time;
    58 }
    59 //-------------------------------
    60 public string getName() {
    61 return firstName " " lastName;
    62 }
    63 //-------------------------------
    64 public string getClub() {
    65 return club;
    66 }
    67
    68 }
    69 }

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

     1 using System;
    2
    3 namespace Seeding
    4 {
    5 /// <summary>
    6 /// Summary description for PrelimEvent.
    7 /// </summary>
    8 public class PrelimEvent:Event
    9 {
    10 public PrelimEvent(string filename, int lanes):base(filename,lanes) {
    11 }
    12 //return circle seeding
    13 public override Seeding getSeeding() {
    14 return new CircleSeeding(swimmers, numLanes);
    15 }
    16 public override bool isPrelim() {
    17 return true;
    18 }
    19 public override bool isFinal() {
    20 return false;
    21 }
    22 public override bool isTimedFinal() {
    23 return false;
    24 }
    25 }
    26 }

    TimedFinalEvent返回StraightSeeding的一个实例

     1 using System;
    2
    3 namespace Seeding {
    4 /// <summary>
    5 ///class describes an event that will be swum twice
    6 /// </summary>
    7 public class TimedFinalEvent:Event {
    8
    9 public TimedFinalEvent(string filename, int lanes):base(filename, lanes) {
    10 }
    11 //return StraightSeeding class
    12 public override Seeding getSeeding() {
    13 return new StraightSeeding(swimmers, numLanes);
    14 }
    15 public override bool isPrelim() {
    16 return false;
    17 }
    18 public override bool isFinal() {
    19 return false;
    20 }
    21 public override bool isTimedFinal() {
    22 return true;
    23 }
    24 }
    25 }

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

     1         protected override void seed() {
    2 //loads the swmrs array and sorts it
    3 sortUpwards();
    4
    5 int lastHeat = count % numLanes;
    6 if (lastHeat < 3)
    7 lastHeat = 3; //last heat must have 3 or more
    8 int lastLanes = count - lastHeat;
    9 numHeats = count / numLanes;
    10 if (lastLanes > 0)
    11 numHeats ;
    12 int heats = numHeats;
    13
    14 //place heat and lane in each swimmer's object
    15 int j = 0;
    16 for (int i = 0; i < lastLanes; i ) {
    17 Swimmer sw = swmrs[i];
    18 sw.setLane(lanes[j ]);
    19 sw.setHeat(heats);
    20 if (j >= numLanes) {
    21 heats--;
    22 j=0;
    23 }
    24 }
    25 //Add in last partial heat
    26 if (j < numLanes)
    27 heats--;
    28 j = 0;
    29 for (int i = lastLanes-1; i<count; i ) {
    30 Swimmer sw = swmrs[i];
    31 sw.setLane(lanes[j ]);
    32 sw.setHeat(heats);
    33 }
    34 //copy from array back into ArrayList
    35 swimmers = new ArrayList();
    36 for (int i=0; i< count; i )
    37 swimmers.Add(swmrs[i]);
    38 }

    循环排位
    CircleSeeding 类从StraightSeeding类派生类中,因此一开始先调用父类的seed方法,然后重新安排赛事
     1     protected override void seed() {
    2 int circle;
    3
    4 base.seed(); //do straight seed as default
    5 if (numHeats >= 2 ) {
    6 if (numHeats >= 3)
    7 circle = 3;
    8 else
    9 circle = 2;
    10 int i = 0;
    11 for (int j = 0; j < numLanes; j ) {
    12 for (int k = 0; k < circle; k ) {
    13 swmrs[i].setLane(lanes[j]);
    14 swmrs[i ].setHeat(numHeats - k);
    15 }
    16 }
    17 }
    18 }

    其他工厂

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

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

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

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

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

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

    3、把要创建哪一类的信息局部化的时候。

  • 相关阅读:
    C++ 练习02 魔术师发牌问题
    C++ 入门2 类型转换
    C++ 入门1 C++简介
    一个简单计算器的实现
    C++练习01 打印杨辉三角
    数据结构01数据结构基础01
    Lesson_7 作业_1 Driver 和 Car
    Caterl Java寒假基础练习题(一) 循环相加
    Lesson_9 上课笔记 多态
    Lesson_10 作业计算工资
  • 原文地址:https://www.cnblogs.com/jameslif/p/2494201.html
Copyright © 2011-2022 走看看