18.指挥官只有一个——单件模式

来源:互联网 发布:eve数据地点扫描 编辑:程序博客网 时间:2024/05/25 18:09

随着游戏开发的进展,有人建议添加一个新的玩法——保护指挥官;游戏剧情是这样的,在游戏中有一位重要人物,玩家必须保护他,如果他的被干掉了,也就意味着Game Over。

创建新的游戏单位,这项工作当然又落到了你的身上。

根据你对游戏代码的了解,很快就创建了指挥官的相关代码,首先是在EUnit枚举中添加成员:

''游戏单位标识枚举

Public Enum EUnit

   

    Commander = 1004 '指挥官

   

End Enum

 

然后是CCommander类,代码如下:

''指挥官

Public Class CCommander

    Inherits CUnit

    Public Sub New()

        myBehavior = New CLandBehavior

        myWeapon = New CNoWeapon

        mySpeed = 14

        Name = "指挥官"

        UnitId = EUnit.Commander

    End Sub

End Class

 

最后,在CLandUnitCreator类中添加了此类的创建代码:

''陆地单位创建者

Public Class CLandUnitCreator

    Inherits CUnitCreator

    Public Overrides Function CreateUnit(ByVal unitType As EUnit) As IUnit

    Select Case unitType

            ...

            Case EUnit.Commander

                Return New CCommander

            ...

        End Select

    End Function

End Class

 

十分钟还不到,你就将代码提交了,使用了策略模式和工厂方法模式,效率还真是提高了不少。

但很快,项目经理就来了,“对指挥官角色的创建,你就没有一点限制?我刚才看见了一个方阵的指挥官,要知道,指挥官只能有一个!”

“哦,好的,老大,马上修改代码!”。就知道事情没这么简单!

正好是午餐时间,还是先吃饭吧,毕竟人是铁,饭是钢。午餐时,有个兄弟得意地说到了关于很多指挥官的故事;不过,更多好心的兄弟姐妹们都在帮助你考虑如何修改指挥官角色的创建,有人提到了“单件模式”,好主意!

 

应用单件模式

先来看看单件模式(Singleton Pattern)的定义:确保一个类只有一个实例,并提供一个全局访问点。

只有一个实例,这正是我们想要的。

好了,既然定义中提到了“一个全局访问点”,我们就先创建这个访问点;那么,这个访问点是什么呢?我们知道,在VB.NET中的类类型中只有一个副本的成员是它的共享成员(Shared),所以,我们就在CCommander类中创建这个共享成员,定义如下:

Public Class CCommander

    Inherits CUnit

    Public Shared Commander As IUnit = Nothing

    ...

End Class

(项目:SingletonPatternDemo    文件:CCommander.vb)

 

接下来,需要保证这个类只能创建有一个实例,我们创建一个共享方法来实现,这样,CCommander类的代码就是下面这样:

Public Class CCommander

    Inherits CUnit

    Public Shared Commander As IUnit = Nothing

    Public Sub New()

        myBehavior = New CLandBehavior

        myWeapon = New CNoWeapon

        mySpeed = 14

        Name = "指挥官"

        UnitId = EUnit.Commander

    End Sub

    Public Shared Function CreateUnit() As IUnit

        If IsNothing(Commander) Then

            Commander = New CCommander

        End If

        Return Commander

    End Function

End Class

(项目:SingletonPatternDemo    文件:CCommander.vb)

 

在创建指挥官单位实例的方法CreateUnit()中,代码很简单,我们使用IsNothing()方法判断Commander是否已经实例化,如果没有就使用New关键字实例化这个对象;当然,如果已经创建了,就不用再做什么了。最后,方法将返回Commander对象。

现在,我还需要修改CLandUnitCreator类中的代码,我们现在不能使用New CCommander语句创建指挥官角色了,而是要使用CCommander.CreateUnit()方法。代码如下:

''陆地单位创建者

Public Class CLandUnitCreator

    Inherits CUnitCreator

    Public Overrides Function CreateUnit(ByVal unitType As EUnit) As IUnit

        Select Case unitType

        ...

            Case EUnit.Commander

                Return CCommander.CreateUnit

            ...

        End Select

    End Function

End Class

(项目:SingletonPatternDemo    文件:Creator.vb)

 

现在,我们可以测试这个单件是否起作用了,代码如下:

Module Module1

    Sub Main()

        Dim landUnitCreator As New CLandUnitCreator

        Dim cmd1 As IUnit = landUnitCreator.CreateUnit(EUnit.Commander)

        cmd1.Name = "XXX将军"

        Console.WriteLine(cmd1.Name)

        cmd1.Move(100, 100)

        cmd1.Attack(110, 110)

        Dim cmd2 As IUnit = landUnitCreator.CreateUnit(EUnit.Commander)

        Console.WriteLine(cmd2.Name)

        Console.ReadLine()

    End Sub

End Module

(项目:SingletonPatternDemo    文件:Module1.vb)

 

本代码运行的结果如下图:

 

代码中,我两次调用了创建Commander的方法;第一次,创建了cmd1,我们将指挥官的名字(Name)设置为“XXX将军”(请注意,在构造方法中他的默认名称是“指挥官”);第二次,我们创建了cmd2,这时我们并没有设置他的名字,如果他和cmd1不是一个实例的话,显示的名字应该是“指挥官”,不是吗?

实际上,我们看到,cmd2显示的名字依然是“XXX将军”,这说明cmd1和cmd2实际上表示的是同一个Commander实例。我们的单件模式应用成功了,Yeah!

提交代码时记得提醒老板,一样要一如继往地要求大家使用创建者类中的CreateUnit()方法创建游戏单位;否则的话,你懂的。

 

小结

我们在本章应用了单件模式,讨论了如何对一个类只创建一个实例,我们成功地创建了唯一的指挥官角色。

此外,在使用单件模式时,还有一个需要注意的问题就是:如果我们在多线程系统中使用了单件对象,还必须考虑不同线程下对于这个单件对象的引用同步问题;这时,我们可以在使用单件对象时使用SyncLock语句对其进行同步锁定。

各种士兵和装备训练了这么久,我们终于要横渡海峡向敌人发起进攻了,登陆时注意不要让敌人的炮火击中运输机和运输船了,否则后果很严重的。

在下一章,我们将了解观察者模式。

出自:http://www.caohuayu.com/books/B0003/B0003.aspx
原创粉丝点击