如果你第一次听到抽象数据类型这个名词,一定会觉得摸不着头脑,其实有很多人即使不是第一次听到,也会觉得不是很熟悉,甚至天天把这个词挂在嘴边的人,也并不见得就理解其中的内涵。获取你还会听到其他一些高大上的名词,比如说面向抽象编程、面向对象编程、面向接口编程。还会有一些人说,X语言是一种面向对象的语言、而Y语言则不是。其实我想说的是,以上这些都不重要,重要的是你的思维方式,不要因为有人给某些东西起了一个听起来很玄乎的名字,就看不清事物原来的本质了。
刚才说的以上都不重要,并不代表这些说法都有问题,相反这些说法都是正确的、完全没问题,但是他们表达的都是同一个意思、同一个概念、同一种思维方式,而这种思维方式的基础就是我们这里要讲的,抽象数据类型。
你是否接触过一些编程语言呢,如果你看过多个编程语言后,就会发现有些编程语言有类这个概念,而另一些则没有,有些编程语言有抽象类这个概念,而另一些则没有,有些编程语言有接口这个概念,而另一些则没有,但是他们都号称自己是面向对象的。甚至有些编程语言没有类、没有抽象类、没有接口,也有人把它认为是一种面向对象的编程语言。这是为什么呢,因为不管是类、抽象类、接口,这些都只是一个概念、一种思维方式,我们不能说某个编程语言没有这些要素它就不是面向对象的。所以如果某个编程语言能够让你使用这种抽象的思维来进行思考,那他就是一种面向对象的语言。
上文中我们提到了很多次抽象这个词,如果你去搜素一下会发现很多相关的资料,最多的结果因该是面向对象编程、数据结构或者设计模式。这些概念我们以后都会提到,但是这次说的是他们的基础。也就是说,没有好的抽象数据类型,其他的一切都只是空谈。试想一下,如果程序中底层的数据部分都不是抽象的,那操作数据的行为层又怎么可能是抽象的呢。抽象数据类型,并不是数据的堆砌,它是使用一种合理且易于管理和扩展的方式,将数据管理起来的工具。
这里我们举一个例子来说明。在游戏中角色发动了一次攻击,被攻击的目标会收到普通伤害10点、法术火焰伤害15点、buff毒系伤害12点持续3秒、眩晕0.5秒。很显然,这里的数据不可能直接写死在程序的代码中,而是需要一个配置文件,这样的灵活性就更高。那么问题就是我们要如何来设计这个配置文件。
你读到这里会不会觉得很奇怪,这里是设计配置文件,和我写程序代码有什么关系呢。其实配置文件的设计结构很大程度上影响到程序代码中数据的结构,甚至会起到决定性的作用。很多程序代码为了能和配置文件一一对应来进行批处理,都会完全照搬配置文件的结构。
下面是一种设计
普通伤害 | 法术火焰伤害 | buff毒系伤害 | 眩晕 | |
---|---|---|---|---|
攻击x | 10 | 15 | 12-3 | 0.5 |
这是一种最简单直接的设计,如果攻击不会有眩晕效果,那么那一栏填写0就可以了。当然这里的数值可以直接通过公式计算出来,不同职业的伤害计算公式会偏重各自特有的技能类型。这个表中我直接填写的数值是为了更直观,你可以写上你的伤害计算公式。其实在某些项目里,确实会直接使用数值而不是公式,因为控制公式的曲线斜率是需要相当的数学基础的,而直接使用数值则简单得多。
像这样的设计在小型的数据应用上还是可以应付的,但当遇到有很多的数据和复杂的逻辑关系的时候,到了最后你会发现这简直是一个灾难,因为所有的数据都是平级的,他们之间没有任何的关系,纯粹是数值的堆砌。如果这时候,你想要对游戏平衡性做一个测试和调整,会非常的困难。程序员也不可能给数值策划一个灵活的方式来进行游戏创作,策划会时不时的来打扰程序,要求做各种各样的修改来达到游戏的功能。同样如果WOW3的配置是这样的,也就不可能有DOTA的诞生了。
下面来看一下怎么设计这个数据才好呢。这里我说一下如果让我来做的话会怎么设计。
攻击类型 | 减血伤害类型 | 减法力伤害类型 | buff伤害类型 |
---|---|---|---|
攻击x | 可以填多个 | ...... | ...... |
减血伤害类型 | 减血量 |
---|---|
类型1 | 10 |
减法力伤害类型 | 减法力 |
---|---|
类型1 | 10 |
眩晕类型 | 时间 |
---|---|
类型1 | 0.5 |
buff伤害类型 | 具体伤害类型 | 持续时间 |
---|---|---|
类型1 | 减血伤/减法伤/眩晕...... |
这就是主要的设计思路了,就是将各种伤害类型区分开来。这样的好处是,可以通过组合各种伤害类型,生成出新的攻击类型,并且最终的结构会非常清晰,不会像第一种解决方案一样出现最后无法维护的情况。同样,表结构合理了,那么代码中的抽象数据类型也会变得合理。所以说设计出合理的抽象数据类型,并不是只从代码着手就行的,需要在各个方面一起努力。