例程实作
----庖丁解羊(上)
作者:HolyFire
在《庖丁解羊----划分》我们曾经将一只可爱的小羊拆成了零件,不知道是否还能否记得那道全羊谱。呵呵,说得口水都要流下来了。这里呢,没有美味可口的羊肉,只有我在给各位讲如何将我们学习的方法应用到编程中来。
首先呢我们要把结构看清楚,也就是下面我东拼西凑起来的图形,你们都看到了,我的ASCII艺术还差的远呢,呵呵,不过还能看明白。
山羊
┏━━━━━━━━━━━┳━━━┻━━━━━━━━━┳━━━━━━┓
脑袋 躯体 尾部 四肢
┏━━╋━━┳━━┓ ┏━┳━╋━┳━━┳━━┓ ┏━━╋━━┓ ┏━┻━┓
骨架 胡子 五官 绒毛 胸 腹 背 内脏绒毛 骨架 尾巴 绒毛 骨架 前肢 后肢
┏━━┳┻━┳━━┓ ┏━┳━┳┻┳━┳━┳━┓ ┏━━╋━━┳┛
耳朵 眼睛 鼻子 嘴巴 心 脾 肝 肺 胃 肠 肾 脚 绒毛 骨架
结构图
这样一来,我们心里面就不是一块一块零碎的羊杂烩了,而是一部分一部分串在一起的整体。我们从底部开始向上观察,你会发现这是一个重组的过程。反之是一个分解过程。
而我们又将一些最小的单元归结为{轮廓,颜色},这是外表上能感知到的信息。
通过这些信息,我们发现自底向上解决这个问题好一些,人们总是乐意从简单容易的地方入手,这也是我一开始要做划分的原因。
胡子{轮廓 , 颜色} 绒毛{轮廓 , 颜色} 骨架{轮廓 , 颜色}
胸 {轮廓 ,颜色} 腹 {轮廓 , 颜色} 背 {轮廓 , 颜色}
眼睛{轮廓 , 颜色} 鼻子{轮廓 , 颜色} 嘴巴{轮廓 , 颜色}
心 {轮廓 ,颜色} 脾 {轮廓 , 颜色} 肝 {轮廓 , 颜色}
肠 {轮廓 ,颜色} 肾 {轮廓 , 颜色} 脚 {轮廓 , 颜色}
尾巴{轮廓 , 颜色} 耳朵{轮廓 , 颜色} 肺 {轮廓 , 颜色}
胃 {轮廓 ,颜色}
可以看出这些单位的组成都是一样的,我们可以将它统称为羊的部分。
部分{轮廓,颜色}
绒毛,骨架,胸,腹,背,眼睛,鼻子,胡子,嘴巴,心,脾,肝,肠,肾,脚,尾巴,耳朵,肺,胃{{部分}}
前肢{脚,绒毛,骨架}
后肢{脚,绒毛,骨架}
前肢与后肢也有着一样的结构哦
肢体{脚,绒毛,骨架}
前肢,后肢{肢体}
四肢{前肢,后肢}
内脏{心,脾,肝,肺,胃,肠,肾}
五官{耳朵,眼睛,鼻子,嘴巴}
尾部{尾巴,绒毛,骨架}
躯体{胸,腹,背,内脏,绒毛,骨架}
脑袋{五官,胡子,绒毛,骨架}
山羊{脑袋,躯体,四肢,尾部}
其实,这些有各个部分组合起来的整体,如:前肢,后肢,躯体等等也是羊的一部分。
肢体{{部分},脚,绒毛,骨架 }
前肢,后肢{肢体}
四肢{{部分},前肢,后肢}
内脏{{部分},心,脾,肝,肺,胃,肠,肾}
五官{{部分},耳朵,眼睛,鼻子,嘴巴}
尾部{{部分},尾巴,绒毛,骨架}
躯体{{部分},胸,腹,背,内脏,绒毛,骨架}
脑袋{{部分},五官,胡子,绒毛,骨架}
山羊{{脑袋},躯体,四肢,尾部}
如果一个完整的整体是一个部分的特例的话,那么山羊也应该是一个部分。
山羊{ 部分,脑袋,躯体,四肢,尾部}
这样一来,就可以方便的表示结构图的部分--整体的关系了。
部分{轮廓,颜色}
绒毛,骨架,胸,腹,背,眼睛,鼻子,嘴巴,心,脾,肝,肠,肾,脚,尾巴,耳朵,肺,胃{{部分}}
肢体{{部分},脚,绒毛,骨架 }
前肢,后肢{肢体}
四肢{{部分},前肢,后肢}
内脏{{部分},心,脾,肝,肺,胃,肠,肾}
五官{{部分},耳朵,眼睛,鼻子,嘴巴}
尾部{{部分},尾巴,绒毛,骨架}
躯体{{部分},胸,腹,背,内脏,绒毛,骨架}
脑袋{{部分},五官,胡子,绒毛,骨架}
山羊{{部分},脑袋,躯体,四肢,尾部}
那么,现在是开始运用头脑思考的时候了。
羊的部分可以是一根颤巍巍的胡子,也可以是一个晃来晃去的尾巴,他们有共同的属性:轮廓和颜色,但是由脚,绒毛,骨架组成的肢体也是一个部分,他们有什么共同之处呢,用部分做基类是不是有点牵强呢。其实一个部分轮廓和颜色是什么样的对于我们并不重要,而且封装的原则是不应该让使用者知道内部结构,所以我们只要提供一个方法来描绘这个部分在视觉上的效果就可以了。如何让一个方法可以表现不同的效果呢,switch ? if else ? No!
我们有更好方法----多态,继承的结构已经有了,缺的就是虚函数了。
部分{显示=无显示} 如何表现一个未知的物体?无意义,一个纯虚函数正好胜任。
轮廓{显示} 并非只有羊的部分才是可以体现视觉效果的,而且视觉效果是人与羊的关系,并非羊的一部分。
绒毛{{部分,显示=显示绒毛},轮廓,颜色}
骨架{{部分,显示=显示骨架},轮廓,颜色}
胸 {{部分,显示=显示胸 },轮廓,颜色}
腹 {{部分,显示=显示腹 },轮廓,颜色}
背 {{部分,显示=显示背 },轮廓,颜色}
眼睛{{部分,显示=显示眼睛},轮廓,颜色}
鼻子{{部分,显示=显示鼻子},轮廓,颜色}
嘴巴{{部分,显示=显示嘴巴},轮廓,颜色}
胡子{{部分,显示=显示胡子},轮廓,颜色}
心 {{部分,显示=显示心 },轮廓,颜色}
脾 {{部分,显示=显示脾 },轮廓,颜色}
肝 {{部分,显示=显示肝 },轮廓,颜色}
肠 {{部分,显示=显示肠 },轮廓,颜色}
肾 {{部分,显示=显示肾 },轮廓,颜色}
脚 {{部分,显示=显示脚 },轮廓,颜色}
尾巴{{部分,显示=显示尾巴},轮廓,颜色}
耳朵{{部分,显示=显示耳朵},轮廓,颜色}
肺 {{部分,显示=显示肺 },轮廓,颜色}
胃 {{部分,显示=显示胃 },轮廓,颜色}
由于各个部分不确定,所以需要用一个轮廓来描述,既然轮廓可以描述出形状,加上颜色想必就能重现视觉效果,由此可见轮廓与颜色也是有关系的,他们结合在一起体现视觉效果。他们是间接的体现了这个部分的,比如
绒毛{{部分,显示=显示绒毛},轮廓,颜色}
事实上是
绒毛{{部分,显示=轮廓+颜色},轮廓,颜色}
这样一来这些基本单位又从新使用同一接口。
绒毛,骨架,胸,腹,背,眼睛,鼻子,胡子,嘴巴,心,脾,肝,肠,肾,脚,尾巴,耳朵,肺,胃{{部分,显示=轮廓+颜色},轮廓,颜色}
肢体{{部分, 显示=显示肢体},脚,绒毛,骨架}
前肢,后肢{{肢体}}
四肢{{部分,显示=显示四肢},前肢,后肢}
内脏{{部分,显示=显示内脏},心,脾,肝,肺,胃,肠,肾}
五官{{部分,显示=显示五官},耳朵,眼睛,鼻子,嘴巴}
尾部{{部分,显示=显示尾部},尾巴,绒毛,骨架}
躯体{{部分,显示=显示躯体},胸,腹,背,内脏,绒毛,骨架}
脑袋{{部分,显示=显示脑袋},五官,胡子,绒毛,骨架}
山羊{{部分,显示=显示山羊},脑袋,躯体,四肢,尾部}
这些组合的部分是由一些基本的部分组成的,那么它们的视觉效果是这些基本的部分的整体效果。
部分{显示=无显示}
轮廓{显示}
绒毛,骨架,胸,腹,背,眼睛,胡子,鼻子,嘴巴,心,脾,肝,肠,肾,脚,尾巴,耳朵,肺,胃{{部分,显示=轮廓+颜色},轮廓,颜色}
肢体{{部分, 显示=显示肢体},脚,绒毛,骨架}
前肢,后肢{{肢体}}
四肢{{部分,显示=显示四肢},前肢,后肢}
内脏{{部分,显示=显示内脏},心,脾,肝,肺,胃,肠,肾}
五官{{部分,显示=显示五官},耳朵,眼睛,鼻子,嘴巴}
尾部{{部分,显示=显示尾部},尾巴,绒毛,骨架}
躯体{{部分,显示=显示躯体},胸,腹,背,内脏,绒毛,骨架}
脑袋{{部分,显示=显示脑袋},五官,胡子,绒毛,骨架}
山羊{{部分,显示=显示山羊},脑袋,躯体,四肢,尾部}
再三分析以后,我们发现比起一开始的结构图来说,最后的模型竟然如此的精简,我想这样已经可以接受了。
现在整体—部分的关系已经弄清楚了,现在要做的是如何将它与现实中的事物对应起来,也就是具体如何组合。
那么让我们开始念叨一下,我们怎样用上面已知的内容来描述一只山羊呢。
山羊:一个脑袋,一个躯体,一个四肢,一个尾部
脑袋:一个五官,一把胡子,一些绒毛,一个骨架
躯体:一个胸部,一个腹部,一个背部,一些内脏,一些绒毛,一个骨架
尾部:一条尾巴,一些绒毛,一个骨架
五官:两只耳朵,两只眼睛,一只鼻子,一张嘴巴
内脏:一个心脏,一个脾脏,一个肝脏,两个肺脏,一个胃,一些肠,两只肾脏}
四肢:两只前肢,两只后肢
肢体:一只脚,一些绒毛,一个骨架
不然看出数量在组成上有着重要的地位,所以我们的模型上还要加上基数这个概念
部分{显示=无显示}
轮廓{显示}
绒毛,骨架,胸,腹,背,胡子,眼睛,鼻子,嘴巴,心,脾,肝,肠,肾,脚,尾巴,耳朵,肺,胃{{部分,显示=轮廓+颜色},轮廓[1],颜色[1]}
肢体{{部分, 显示=显示肢体},脚[1],绒毛[n],骨架[1]}
前肢,后肢{{肢体}}
四肢{{部分,显示=显示四肢},前肢[2],后肢[2]}
内脏{{部分,显示=显示内脏},心[1],脾[1],肝[1],肺[2],胃[1],肠[n],肾[2]}
五官{{部分,显示=显示五官},耳朵[2],眼睛[2],鼻子[1],嘴巴[1]}
尾部{{部分,显示=显示尾部},尾巴[1],绒毛[n],骨架[1]}
躯体{{部分,显示=显示躯体},胸[1],腹[1],背[1],内脏[1],绒毛[n],骨架[1]}
脑袋{{部分,显示=显示脑袋},五官[1],胡子[n],绒毛[n],骨架[1]}
山羊{{部分,显示=显示山羊},脑袋[1],躯体[1],四肢[1],尾部[1]}
这样的话,更接近现实的事物,而且降低了问题的复杂度。
经过一大堆的分析,这个模型也相对比较清晰了,可以接受了,不过不代表这是最好的模型,一定会有更好的模型的。但是我们并不需要最完美的模型,只要有一个合适的模型就可以了,在这里,作为我的需要,就已经很合适了。
不用说,现在开始进入编码阶段了。经过上面的分析,相信我们心里有很多很多事要去做,不要急,慢慢来,乱了阵脚可不行。
限于篇幅,我不能将图形显示方面的内容加进来,一来内容太多会混淆实现,不能突出重点;二来容易将思路引到细节的实现上去,反而忽略了全局;再来有关图形显示与编译器和操作系统的关系紧密,并不适合我讲的内容;最后,本人的艺术细胞极度匮乏,如果画出来的形象让大家失望,岂不是自讨没趣,哈哈,玩笑归玩笑,让我们继续吧。
2001/9/27
丁宁