0
点赞
收藏
分享

微信扫一扫

代码设计的三个思想

尤克乔乔 2022-02-24 阅读 84

一、组合代替继承

1、继承

最近在做一个类杀戮之塔的unity游戏Demo,然后因为个人水平有限,导致游戏里类继承层次过深,同一属性由多个不同的平行对象持有等问题。如下面的类图:

然后这带来的问题是:
(1)修改代码麻烦:一旦继承链中的一个对象出现修改,由于它的“承上启下”作用,就不得对关联到的类也做出修改(违反开闭原则);
(2)数据更新困难:如果继承链的某一层需要对外交互,而交互数据在另一条继承链上,那么就会出现像小学试卷里的"连连看"题目一样的关系线,而且因为是继承关系,如果两个继承链需要用到同一份数据,那么你就不得不合并两条链(再次继承或者组合)或者让代码中存在两份数据源;
(3)可读性差:有时候我想在代码里找一个数据都得把几个文件翻来覆去看半天(这还是我写的代码)。

2、组合

修正上面继承带来的问题采用的方法是——组合。
把上面的对象的所有功能都抽离出来,按功能变成一个个组件,而原本的对象就变成一个总控,一个挂钩,当我们的对象需要哪个功能时,就把哪个组件挂载上去,即插即用。这样做除了解决上面的问题,还能得到下面的好处:

(1)职责分离:原本一个庞大的继承类分离成了多个彼此无耦合的组件,再也不需要为了一根香蕉而去买下整个森林了(继承税)。
(2)交互便利:因为有了“总控”的概念,所以当出现对象间的数据交互时,我不需要去翻查交互对象的所在的继承结点是否有该数据,和总控对接,只需要关心它是否持有某组件即可。

基于组合思想下的交互模式:外部事件由总控调用分发,组件之间也通过总控传递数据。

二、状态代替标记

战斗对象需要有一个方式来表示他身上的各种增减益Buff,那么可以怎么处理呢?

1、标记

第一种方式,我们采用“标记”的方式来实现


class CWarrior(object):
	def __init__(self):
		self.m_BuffDict = {}	#维护一个buff字典
		
	
	def QueryBuff(self, sBuffName):
		if sBuffName in self.m_BuffDict:
			return self.m_BuffDict[sBuffName]
		return 0
	
	def AddBuff(self, sBuffName, iValue):
		self.m_BuffDict[sBuffName] = iValue
	
	def RemoveBuff(self, sBuffName):
		if sBuffName in self.m_BuffDict:
			del self.m_BuffDict[sBuffName]

初步看起来还不错,但当你的战斗对象需要根据buff做出行为反应的时候,就很难受了。比如说,设想一下下面这个场景:

战斗对象A向战斗对象B释放了一个技能,根据B身上的buff触发各类行为:
1、流血(Bleeding):受到伤害时额外扣除100点生命值
2、感电(Electric):收到雷属性伤害时额外扣除200点生命值
3、中毒(Poisoning):收到伤害时额外扣除300 * 中毒层数 点生命值

那么收到伤害的函数就得这么写了


def GetHurt(self, oSkill):
	if self.QueryBuff("Bleeding"):								#流血
		self.SubHP(100)
	if self.QueryBuff("Electric") and oSkill.Type() == "雷":	#感电
		self.SubHP(200)
	if self.QueryBuff("Poisoning"):								#中毒
		self.SubHP(300 * self.QueryBuff("Poisoning"))	
	......
	

按这种写法的话,没添加一个buff标记,我们就得多写一个if,且单buff效果较复杂时,这个函数就会变得奇丑无比。

所以我们引入状态对象来解决这个问题。

2、状态

class CWarrior(object):
	def __init__(self):
		self.m_BuffDict = {}	#维护一个buff字典
	
	def AddBuff(self, oBuff):		#oBuff 状态对象
		self.m_BuffDict[oBuff.m_ID] = oBuff
	
	def RemoveBuff(self, iBuff):
		if iBuff in self.m_BuffDict:
			del self.m_BuffDict[iBuff]
	
	def GetHurt(self, oSkill):
		for oBuff in self.m_BuffDict.Values():
			oBuff.GetHurt(self, oSkill)
	

可以看到,虽然是同样维护一个buff字典,但他们的区别在与GetHurt()方法。

使用"状态"的形式下,我们用状态对象来描述主体对象的各种状态,状态数据也放到各个状态对象里,,并且各个状态自己实现对事件的行为反应。 上面这个例子的状态对象实现如下:

#状态基类
class CBaseBuff(object):
	def __init__(self):
		pass
	
	def GetHurt(self, oWarrior, oSkill):
		pass

#流血	
class CBleeding(CBaseBuff):
	def GetHurt(self, oWarrior, oSkill):
		oWarrior.SubHP(100)

#感电
class CElectric(CBaseBuff):
	def GetHurt(self, oWarrior, oSkill):
		if oSkill.Type() == "雷":	#感电
			oWarrior.SubHP(200)

#中毒
class CPoisoning(CBaseBuff):
	def __init__(self):
		self.m_Value = 0

	def GetHurt(self, oWarrior, oSkill):
		oWarrior.SubHP(300 * self.m_Value)	
	

三、流程代替调用

明天再写

举报

相关推荐

0 条评论