什么是聚合?
聚合可以很好地表达对象是什么和做什么。换句话说,聚合是把执行同一任务的代码整合到一起。
关于聚合原则,在Gamedevtuts+以前的文章里,有一篇关于“隐藏行动规则”的非编程文章,里面的例子讲的很好,可以参考。
不要把很多游戏捆版在一个包里… 每个单独的游戏可能都是一个好游戏。如果放在一起,他们就会彼此不相容。
这一原则同样适用于面向对象编程。每个对象应该只有一个职责,对象的每个行为应该只做一个任务。只要超出了范围,代码的维护就会增加工作量。
为什么有用?
按照功能来组织的,只执行单一任务的代码,被称为具有“高聚合”。高聚合的代码是可重用的,单纯的,易懂的。高聚合也使对象更精悍,功能更集中。
相反的,随意组织起来,执行多个任务的代码,被称为“低聚合”。这样的代码不好理解,不易维护和重用,一般比较复杂。低聚合创建的对象臃肿,且功能散乱。
高聚合显而易见是好的,低聚合则很差。所以我们编程时一定要力求做到高聚合。
如何应用
那么我们如何把高聚合原则应用到面向对象编程里? 首先,用对象的方式编程就是对聚合性的提高。当然每个对象本身也应该有高聚合性。我们同样用上篇文章里的三个游戏例子来看聚合原则是如何应用的。
小行星游戏(Asteroids)
回想一下上一课,我们定义了飞船这个对象,它具有旋转,移动,开火等行为。
如果我们在一段代码里把三个行为都写在一起,就会显得很混乱。所以我们应该把每个行为都分开用函数来定义。函数可以分离功能,把相关的代码组合在一起,创建高聚合的程序。
在程序里,我们通过创建类来定义对象。Java语言里类的代码就像这样:
/** * 飞船类 */ public class Ship { /** * 函数– 执行旋转飞船的行为(任务) */ public void rotate() { // 旋转飞船的代码 } /** *函数–执行移动飞船的行为(任务) */ public void move() { //移动飞船的代码 } /** *函数–执行移动飞船开火射击的行为(任务) */ public void fire() { //飞船发射子弹的代码 } }
就像你看到的,每个行为都有一个函数,而程序代码在这个简略结构里被很好的组织起来了。
先不用担心具体的语法,在这个教程系列后面的部分,我们会讨论到具体的程序细节。俄罗斯方块(Tetris)
再回想一下俄罗斯方块游戏,俄罗斯方块这个对象具有下落,左右移动,旋转三个行为。基本的类结构如下:。
/** * 俄罗斯方块游戏类 */ public class Tetromino { /** * 函数 – 更新方块的位置 */ public void fall() { //更新方块位置的代码 } /** *函数 – 左右移动方块 */ public void move() { //左右移动方块的代码 } /** *函数 – 旋转方块 */ public void rotate() { //旋转方块的代码 } }
同样的,对象的行为都被分到各自的函数。你可能注意到了,“下落”这个方法执行的是“更新方块的位置”。这是因为下落的行为是一直在持续发生的,而不是一次性触发的,所以我们不能只执行“让方块下落”这样的任务。下落的方块每次向下移动一定的位移量,所以我们通过更新方块的位置来反映下落速度。
吃豆人(Pac-Man)
“鬼”这个对象有移动和改变状态两种行为,对它我们还要增加一些东西来实现高聚合。
/** * “鬼”的类 */ public class Ghost { /** * 函数 – 移动鬼 */ public void move() { // 鬼以当前方向移动的代码 } /** *函数- 改变鬼的方向 */ public void changeDirection() { //改变鬼方向的代码 } /** *函数- 改变鬼的速度 */ public void changeSpeed() { //改变鬼速度的代码 } /** *函数- 改变鬼的颜色 */ public void changeColor() { //改变鬼颜色的代码 } /** *函数- 改变鬼的状态 */ public void changeState() { //改变鬼状态的代码 //这个函数会调用其他三个函数:改变鬼的方向,改变鬼的速度,改变鬼的颜色 } }
更改鬼的状态额外增加了三个函数: 改变方向,改变颜色,改变速度。这三个函数不在我们开始的行为列表里,因为它们其实不是对象的行为。这些函数被称为“辅助函数”,它们有助于程序维持高聚合。
鬼改变状态(当吃豆人吃到超能豆的时候)这一行为需要执行三个任务:变成蓝色,调转方向,移动变慢。要维持高聚合,所以我们就不要把三个任务放在同一个函数里。我们把它们分到三个子函数里,最后在一个主函数里统一调用。
在描述函数/或者行为时常用到“and”,它的意思就是告诉我们应该创建更多的函数来执行任务。
总之,聚合原则就是要把相关的代码组织在一起,执行单一的任务。聚合帮助我们创建可维护的,可重用的代码。