C# 工厂方法模式(Swimmer类)

Swimmer类

除了说过Swimmer类含有名字、俱乐部、年龄、排位、时间以及在选拔赛后的分组和泳道外,

对Swimmer类我们并没有介绍太多。Event类从某个数据库(在例子中用的是一个文件)读入

Swimmer数据,然后在为某项赛事调用getSeeding方法时,将数据传给Seeding类。

Event类

我们在前面已经看到了抽象基类Event在实际应用中,用它读入选手数据,并将其传给Swim-

mer类的一个实例去进行分析。在抽象基类Event里,判断赛事是预赛、决赛还是计时决赛的方法

都是空的,在派生类中会实现这些方法。

PrelimEvent类返回CircleSeeding的一个实例。

csharp 复制代码
public class PrelimEvent:PrelimEvent
{
    public PrelimEvent(string filename,int lanes);
        base(filename,lanes)
        {

        }

    public override Seeding getSeeding()
    {
        return new CircelSeeding(swimmers,numLanes);
    }

    public override bool isPrelim()
    {
        return true;
    }

    public override bool isFinal()
    {
        return false;
    }

    public override bool isTimedFinal()
    {
        return false;
    }
}

TimedFinalEvent类返回StraightSeeding的一个实例。

csharp 复制代码
public class TimedFinalEvent:Event
{
    public TimedFinalEvent(string filename,int lanes):base(filename,lanes)
    {
    
    }

    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;
    }
}

在这两段代码中,两种赛事类都包含基类Event的一个实例,用它读取数据文件。

直接排位

实际编写这个程序时,我们发现大多数工作都是在直接排位(StraightSeeding)中完成的,为

循环排位(CircleSeeding)而进行的改动相当少。实例化StraightSeeding类,并拷人选手数据和泳

道号。

csharp 复制代码
protected override void seed()
{
    sortUpwards();

    int lastHeat=count%numLanes;
    if(lastHeat<3)
        lastHeat=3;
    int lastLanes=count-lastHeat;
    numHeats=count/numLanes;
    if(lastLanes>0)
        numHeats++;

    int heats=numHeats;
}

这段程序的目的是,在调用getSwimmer方法时,使选手排位的整个数组都是可用的。

循环排位

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

比赛。

csharp 复制代码
protected override void seed()
{
    int circle;
    base.seed();
    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++].seatHeat(numHeats-k);
        }
    }
}

排位程序

在这个例子中,我们从网上拿到了参加500yd(码,1yd=0.914(m)和100yd自由泳比赛

的选手数据,井使用这些数据构建类TimedFinalEvent和PrelimEvent类。在图9.2里,会看到这

种排位方式的结果。在上面的图中,选择的是500Event事件,可以看到选手按照由快到慢的顺序直

接安排泳道。在下面的图中,选择的是事件,进行的是循环排位。

其他工厂

我们在前面略过了一个问题:读入运动员数据的程序如何决定生成哪一项赛事。在我们读入

数据时,通过创建正确类型的事件来巧妙地实现这一点。这部分代码在窗体的init方法中。

csharp 复制代码
private void init()
{
    private void init()
    {
        events=new ArrayList();
        lsEvents.Items.Add("500 Free");
        lsEvents.Items.Add("100 Free");

        events.Add(new TimedFinalEvent("500free.txt",6));
        events.Add(new PrelimEvent("100free.txt",6));
    }
}

很明显,这是一个需要EventFactory来决定生成哪一项赛事的例于,这又涉及到了我们开始时

而讨论的简单厂。

何时使用工厂方法

在下列情况下,应该考虑使用工厂方法:

  • 一个类无法预测它要创建的对象属于哪一个类。
  • 一个类用它的子类来指定所创建的对象。
  • 把要创建哪一个类的信息局部化的时候。