工厂方法 THE FACTORY METHOD 我们已经学会了简单工厂模式(Simple Factory Pattern),工厂(factory)思想贯穿于整个面向对象编程(OOP)以及其他一些设计模式的始终。如:生成器(Builder)模式。其间,一个单一的类扮演类似交通警察的角色,决定哪一个单一层次上的子类将被实例化。 工厂方法模式(Factory Method pattern)是对工厂(factory)思想进行了巧妙的延伸,它使得将超类的实例化延迟到它的每一个子类。这个模式没有具体的指出延迟到哪一个子类,而是定义一个抽象类创建对象,让其子类决定创建哪一个对象。 下面是一个简单的例子,在一个游泳比赛中如何确定游泳运动员的泳道。按照运动员的成绩排列决赛事的分组,速度越快所分的小组的决赛的次序越靠后,反之,速度越慢就越先比赛,而且在每一个小组中成绩越好、速度越快的选手也就越靠近中间的泳道。这被称为straight seeding。 当游泳运动员在锦标赛比赛过程中,他们通常要游两次。 通过在预赛中相互竞争,前12名或者16名游泳运动员将继续在决赛中继续彼此竞争。 为了预赛工作更公平, 采用circle seeded,这样使得速度最快的3名选手分别处于最迅速3个小组的中心泳道。在剩下的选手中再选出速度最好的3名选手,等等。 我们要实现这个选拔模式并且使用工厂方法。 首先,设计抽象事件类: Public Class Events Protected numLanes As Integer Protected swmmers As Swimmers '----- Public Sub New(ByVal Filename As String, ByVal lanes As Integer) MyBase.New() Dim s As String Dim sw As Swimmer Dim fl As vbFile fl = New vbFile(Filename) '打开一个文本文件 fl.OpenForRead() numLanes = lanes '保存泳道数量信息 swmmers = New Swimmers '读取游泳选手信息 s = fl.readLine While Not fl.fEOF sw = New Swimmer(s) '建立对象 swmmers.Add(sw) 'add to list s = fl.readLine End While fl.closeFile() End Sub '----- Public Function getSwimmers() As ArrayList getSwimmers = swmmers End Function '----- Public Overridable Function isPrelim() As Boolean End Function '----- Public Overridable Function isFinal() As Boolean End Function '----- Public Overridable Function isTimedFinal() As Boolean End Function '----- Public Overridable Function getSeeding() As Seeding End Function
End Class
因为所有的派生类都要从文本文件读取数据,所以,我们把Events类作为基类。其中所定义的方法均为虚方法,可以通过继承Events类来实现具体的类(PrelimEvent类、TimedFinalEvent类),这两个类之间唯一的不同就是返回的选拔的类别不同。我们也定义了一个包含以下方法的抽象选拔类:
Public MustInherit Class Seeding Protected numLanes As Integer Protected laneOrder As ArrayList Protected numHeats As Integer Private asw() As Swimmer Protected sw As Swimmers '----- Public Function getSeeding() As Swimmers getSeeding = sw End Function '----- Public Function getHeat() As Integer End Function '----- Public Function getCount() As Integer getCount = sw.Count End Function '----- Public MustOverride Sub seed() '----- Public Function getSwimmers() As ArrayList getSwimmers = sw End Function '----- Public Function getHeats() As Integer Return numHeats End Function '----- Public Function odd(ByVal n As Integer) As Boolean odd = (n / 2) * 2 <> n End Function '----- Public Function calcLaneOrder(ByVal lns As Integer) As ArrayList numLanes = lns Dim lanes(numLanes) As Integer Dim i As Integer Dim mid, incr, ln As Integer mid = (numLanes / 2) If (odd(numLanes)) Then mid = mid + 1 End If incr = 1 ln = mid For i = 0 To numLanes - 1 lanes(i) = ln ln = mid + incr incr = -incr If (incr > 0) Then incr = incr + 1 End If Next i
laneOrder = New ArrayList For i = 0 To numLanes - 1 laneOrder.Add(lanes(i)) Next i calcLaneOrder = laneOrder End Function
Public Sub New(ByVal swmrs As Swimmers, ByVal lanes As Integer) MyBase.New() sw = swmrs numLanes = lanes End Sub '------------------- Public Function sort(ByVal sw As Swimmers) As Swimmers
Dim i, j, max As Integer Dim tmp As Swimmer
Try max = sw.Count Dim asw(max) As Swimmer
For i = 0 To max - 1
asw(i) = CType(sw.Item(i), Swimmer) Next i For i = 0 To max - 1 For j = i To max - 1 If asw(i).getTime > asw(j).getTime Then tmp = asw(i) asw(j) = asw(i) asw(i) = tmp End If Next j Next i
sw = New Swimmers For i = 0 To max - 1 sw.Add(asw(i)) Next i
sort = sw Catch e As Exception Console.WriteLine("Caught " + i.ToString + " " + j.ToString + " " + max.ToString + " " + e.ToString()) Console.WriteLine(e.StackTrace) End Try End Function End Class 派生类为这个选拔基类(Seeding)分别建立一个实例来调用这些方法。我们现在建立这两个具体的选拔子类:StraightSeeding 类CircleSeeding类。PrelimEvent类将返回CircleSeeding类的实例,TimedFinalEvent类则返回StraightSeeding类实例。结构如下:
通过整个事件层次,可以看到PrelimEvent类、TimedFinalEvent类都包含getSeeding方法,PrelimEvent类将返回CircleSeeding类的实例,TimedFinalEvent类则返回StraightSeeding类实例。在这个简单的例子当中我们并没有指出真正的工厂(Factory),然而,实例化哪一个Event类决定了哪一个Seeding类将被实例化。 虽然它看起来像是在两个类之间存在一对一相应的通信。 但,这不是必需的。可以有许多不同类型的Events类但他们只使用少一部分Seeding类。
Swimmer 类 Swimmer类包含一些游泳运动员的具体信息和在选拔之后修改运动员的组以及泳道的方法。Event类在数据库(在这个例子中为文本文件)读取运动员信息。当调用Event类的getSeeding方法时将这个运动员的集合传递到Seeding类。
Events 类 我们已经定义了一个抽象基类,在实际的应用当中,我们只是使用它来读取游泳运动员的数据信息(在这个例子中为文本文件)。
Prelimevent类返回CircleSeeding实例
Public Class Prelimevent Inherits Events Dim sd As Seeding Public Overrides Function getSeeding() As Seeding Return New CircleSeeding(swmmers, numLanes) End Function
Public Overrides Function isFinal() As Boolean isFinal = False End Function
Public Overrides Function isPrelim() As Boolean isPrelim = True End Function
Public Overrides Function isTimedFinal() As Boolean isTimedFinal = False End Function
Public Sub New(ByVal Filename As String, ByVal lanes As Integer) MyBase.New(Filename, lanes) numLanes = lanes End Sub End Class
TimedFinalEvent类返回StraightSeeding实例
public Class TimedFinalEvent Inherits Events
Public Sub New(ByVal Filename As String, ByVal lanes As Integer) MyBase.New(Filename, lanes) End Sub '------ Public Overrides Function getSeeding() As Seeding Dim sd As Seeding 'create seeding and execute it sd = New StraightSeeding(swmmers, numLanes) sd.seed() getSeeding = sd End Function '------ Public Overrides Function isFinal() As Boolean isFinal = False End Function '------ Public Overrides Function isPrelim() As Boolean isPrelim = False End Function '------ Public Overrides Function isTimedFinal() As Boolean isTimedFinal = True End Function
End Class Straight Seeding类
在这个小程序在实际应用当中,会发现大部分工作在Straight Seeding类中完成的。我们实例化StraightSeeding 类,复制、修改游泳运动员的集合和所属泳道位置。
Public Class StraightSeeding Inherits Seeding Public Overrides Sub seed() Dim lastHeat As Integer Dim lastlanes As Integer Dim i, j, count, heats As Integer Dim swmr As Swimmer Try sw = sort(sw) laneOrder = calcLaneOrder(numLanes) count = sw.Count lastHeat = count Mod numLanes If (lastHeat < 3) And lastHeat > 0 Then lastHeat = 3 'last heat must have 3 or more End If count = sw.Count lastlanes = count - lastHeat numheats = lastlanes / numLanes If (lastHeat > 0) Then numheats = numheats + 1 End If heats = numheats 'place heat and lane in each swimmer's object j = 0 For i = 0 To lastlanes - 1 swmr = sw.swm(i) swmr.setLane(CType(laneOrder(j), Integer)) j = j + 1 swmr.setHeat(heats) If (j >= numLanes) Then heats = heats - 1 j = 0 End If Next i 'Add in last partial heat If (lastHeat > 0) Then If j > 0 Then heats = heats - 1 End If j = 0 For i = lastlanes To count - 1 swmr = CType(sw(i), Swimmer) swmr.setLane(CType(laneOrder(j), Integer)) j = j + 1 swmr.setHeat(heats) Next i End If
Catch e As Exception Console.WriteLine(i.ToString + j.ToString + e.ToString) Console.WriteLine(e.StackTrace) End Try End Sub '----- Public Sub New(ByVal swmrs As Swimmers, ByVal lanes As Integer) MyBase.new(swmrs, lanes) End Sub End Class 当调用getSwimmers方法时,StraightSeeding 类将创建被选拔的游泳运动员数组。 Circle Seeding类
CircleSeeding 类是从StraightSeeding 类派生的。
Public Class CircleSeeding Inherits StraightSeeding
Private circlesd As Integer '----- Public Sub New(ByVal swmrs As Swimmers, ByVal lanes As Integer) MyBase.New(swmrs, lanes)
End Sub '----- Public Overrides Sub seed()
Dim i, j, k, numHeats As Integer laneOrder = calcLaneOrder(numLanes) sw = sort(sw) '排序
MyBase.seed() numheats = MyBase.getHeats If (numheats >= 2) Then If (numheats >= 3) Then circlesd = 3 Else circlesd = 2 End If i = 0
For j = 0 To numLanes - 1 For k = 1 To circlesd sw.swm(i).setLane(CType(laneOrder(j), Integer)) sw.swm(i).setHeat(numheats - k + 1) i = i + 1 Next k Next j End If End Sub '----- End Class
运行 在这个例子当中,我们使用完成500m自由泳和100m自由泳的运动员列表来创建TimedFinalEvent 类和PrelimEvent类。
选择自 guoyan19811021 的 Blog
|
设计模式之工厂方法(FACTORY METHOD)
最新推荐文章于 2004-11-02 20:40:00 发布