22.装设提高战斗力——装饰者模式
“滚滚长江东逝水,浪花淘尽英雄。是非成败转头空:青山依旧在,几度夕阳红。白发渔樵江渚上,惯看秋月春风。一壶浊酒喜相逢:古今多少事,都付笑谈中。”
真的是三国故事!不过,这也只是我们在本书虚构的游戏项目。“如有雷同 纯属巧合”,又是这句话,这也算是模式了吧!J
本章,我们已经进入了以一个三国为背景的角色扮演游戏(RPG)的开发项目;在这个项目中,由于你在上个项目中出色的完成了角色创建的工作,所以,老板决定让你继续担任游戏角色的创建工作,并担任角色团队负责人;当然,美工方面依然有专门的团队负责,你只是创建这些角色的逻辑类代码。
在项目启动后的几个头脑风暴会议上,我们已经了解到,这个项目中的角色和上一个战争游戏项目中的角色有些区别,这其中就包括:战争游戏中的单位(Unit)虽然有不同的行为和武器,但这些都不会影响角色的生命值,而攻击力也是武器决定好了的;然而,在这个角色扮演游戏中,不同的装备会影响到角色的生命值、攻击力,以及一些特殊功能等特性。所以,我们必须要重新思考这个游戏中角色的创建问题。
现在,根据游戏剧本需求,我们需要创建很多的三国人物、武器和其它装备,项目组决定同样使用EUnit枚举来设置这些游戏单位的类型,枚举成员就是不同的人物、武器和其它装备,代码如下:
''游戏角色类型 Public Enum EUnit '人物 Superman = 0 '隐然人物 LiuBei = 1 '刘备 GuanYu = 2 '关羽 ZhangFei = 3 '张飞 LvBu = 4 '吕布 '武器 Weapon1 = 1001 '双股剑 Weapon2 = 1002 '青龙偃月刀 Weapon3 = 1003 '丈八蛇矛 Weapon4 = 1004 '方天画戟 '物品 Equipper1 = 2001 '黄金铠甲 Equipper2 = 2002 '银质铠甲 End Enum |
(项目:DecoratorPatternDemo 文件:Enums.vb)
现在,我们创建游戏角色(单位)的基本接口(IUnit):
''游戏角色接口 Public Interface IUnit Property Name As String '名称 Property UnitId As EUnit '游戏类型ID Property Life As Integer '生命值 Property Power As Integer '攻击力 Sub Move(ByVal x As Integer, ByVal y As Integer) Sub Attack(ByVal x As Integer, ByVal y As Integer) End Interface |
(项目:DecoratorPatternDemo 文件:Interfaces.vb)
接下来是游戏单位的基类,同上一个游戏一样,我们将它设计为必须继承的(MustInherit)。代码如下:
''游戏单位基类 Public MustInherit Class CUnit Implements IUnit Protected myLife, myPower As Integer Protected myName As String Protected myUnitId As EUnit '生命值 Public Property Life As Integer Implements IUnit.Life Get Return myLife End Get Set(ByVal value As Integer) myLife = value End Set End Property '名称 Public Property Name As String Implements IUnit.Name Get Return myName End Get Set(ByVal value As String) myName = value End Set End Property '攻击力 Public Property Power As Integer Implements IUnit.Power Get Return myPower End Get Set(ByVal value As Integer) myPower = value End Set End Property '单位类型ID Public Property UnitId As EUnit Implements IUnit.UnitId Get Return myUnitId End Get Set(ByVal value As EUnit) myUnitId = value End Set End Property '攻击 Public Sub Attack(ByVal x As Integer, ByVal y As Integer) _ Implements IUnit.Attack Console.WriteLine("{0}攻击位置({1}, {2})", Me.myName, x, y) End Sub '移动 Public Sub Move(ByVal x As Integer, ByVal y As Integer) _ Implements IUnit.Move Console.WriteLine("{0}移动到({1}, {2})", Me.myName, x, y) End Sub End Class |
(项目:DecoratorPatternDemo 文件:CUnit.vb)
接下来怎么样呢?我们要让人物装备不同的武器和物品以提高攻击力和生命值,使用在上一个游戏中组合单位的方法显然有些问题;那么,我们只好……
使用装饰者模式
装饰者模式(Decorator Pattern)的定义是这样的:动态地将功能添加到对象上,相比生成子类更灵活、更富有弹性。
使用装饰者模式时,我们必须有装饰者类和被装饰者类;很显然,我们的CUnit类就是被装饰者,我们必须使用一个装饰者类将这些人物、武器和装备组合起来;下面就是我们创建的装饰者基类代码:
''单位装饰者类 Public MustInherit Class CUnitDecorator Inherits CUnit '添加装备 Public Sub AddUnit(ByVal unit As IUnit) myName = myName & ", " & unit.Name myLife = myLife + unit.Life myPower = myPower + unit.Power End Sub '显示信息 Public Sub ShowInfo() Console.WriteLine("名称:{0}", myName) Console.WriteLine("生命值:{0}", myLife) Console.WriteLine("攻击力:{0}", myPower) Console.WriteLine("角色类型:{0}", myUnitId) End Sub End Class |
(项目:DecoratorPatternDemo 文件:CUnitDecorator.vb)
代码中,我们只是在CUnit类的基础上添加了AddUnit()和ShowInfo()两个方法;其中,AddUnit()方法是应用装饰者模式的关键所在,它用于将新增的装备相关信息动态添加到原有的角色上;比如,在名称上标注完整的人物、武器和装备名称,然后将装备带来的生命值和攻击力的增加量累加到角色原有的数值上。ShowInfo()方法则用于显示角色的相关信息,在我们测试时会用到。
下面,我们创建关羽角色相关的单位以便进行测试,这包括关羽(CGuanYu)、青龙偃月刀(CWeapon2)和黄金铠甲(CEquipper1)。代码如下:
''关羽 Public Class CGuanYu Inherits CUnitDecorator Public Sub New() myName = "关羽" myLife = 200 myPower = 100 UnitId = EUnit.GuanYu End Sub End Class ''青龙偃月刀 Public Class CWeapon2 Inherits CUnit Public Sub New() myName = "青龙偃月刀" myLife = 0 myPower = 50 UnitId = EUnit.Weapon2 End Sub End Class ''黄金铠甲 Public Class CEquipper1 Inherits CUnit Public Sub New() myName = "黄金铠甲" myLife = 60 myPower = 0 UnitId = EUnit.Equipper1 End Sub End Class |
(项目:DecoratorPatternDemo 文件:Units.vb)
请注意,在这三个类中,只有CGuanYu类继承了CUnitDecorator;而CWeapon2和CEquipper1类都继承于CUnit类。这是因为,在这个游戏项目中只有人物角色才能添加装备(AddUnit),而装备不能与装备单独组合,青龙偃月刀和黄金铠甲合在一起没人用也没有什么意义,你说呢?
不过真的让武器和其它装备也继承CUnitDecorator类也没有问题,运行结果是一样的。这种情况在玩变形金刚时可能会有用。J
下面,我们就从三个阶段查看关羽的战斗力相关信息,测试代码如下:
Module Module1 Sub Main() Dim GuanYu As New CGuanYu Console.WriteLine(">>>>卖绿豆时的关羽") GuanYu.ShowInfo() Console.WriteLine() ' GuanYu.AddUnit(New CWeapon2) Console.WriteLine(">>>>用上青龙偃月刀的关羽") GuanYu.ShowInfo() Console.WriteLine() ' GuanYu.AddUnit(New CEquipper1) Console.WriteLine(">>>>穿上黄金铠甲的关羽") GuanYu.ShowInfo() ' Console.ReadLine() End Sub End Module |
(项目:DecoratorPatternDemo 文件:Module1.vb)
代码运行结果如下图:
小结
装饰者模式的关键在于,当有着相同接口的各种对象时,我们可以通过使用装饰者模式将它们有机的组合起来而不改变对象的本质;也就是说,一个人穿上雨衣可以防雨,骑上车子可以提高速度,但他无论怎么变,他还是他,再快也不会变成飞机。就像在本章的示例中,关羽就是关羽,无论用没用青龙偃月刀,或者穿没穿黄金铠甲。
出自:http://www.caohuayu.com/books/B0003/B0003.aspx