入行时一直用c++写端游的逻辑,对这两者的区别几乎是0。
最先意识到有不同是在看了设计模式之后,但也没啥自己想法,代码照旧,只是依稀有个印象:都说组合好,少用继承。
用c++的那段时间对这句经验是没多少感受的。后来用erlang、lua、go开始自己设计搭建基础框架,这才在编码层级感受到两者的巨大不同。
一个印象非常深的例子:上个手游项目MOBA大改造,首先要做个类似dota的开房间系统,5v5。
想想房间也就是个小号地图嘛,便着手把嵌在活动中的地图代码扣了出来。做成单独的功能类,LogicMap、LogicRoom。谁要就自己去new个新的,暴露的接口也只几个,FindByID、Enter、Exit、Foreach...
LogicRoom稍微有点不同,放弃了roomID的概念,把它当成了内部的管理变量,所有对外接口都以playerID做索引,甚至放弃了房主的概念,因为它对LogicRoom的功能而言是不必要的,丢给逻辑层自己维护(这个当时纯粹是为了让代码整洁好看而已~囧,后面赚大发了,老大分了一个房主掉线自动切换的单子,给了三小时,五分钟写完<( ̄3 ̄)>)。
扣完之后意外的发现逻辑层代码颜值提高不少,只需关心什么时候进去、什么时候出来,要通告就随便找个人Foreach下……爽的一比。
随即回忆起写c++活动的情景,我们的地图系统(对,不能称之为模块了,太轻量~)有严格的继承体系(我那会可不会这么称呼它),然后你写逻辑时随便一扯就扯到它老子cMap那去了,看着糊你一脸的switch...case/if...else,还有几十百八个函数,果断不想理。
问题是我们大多需要的仅仅只是进出、找人、通知这样的简单功能而已呀。
继承带来了统一的接口,可以批量处理,可以脚本化,但同时也带来了臃肿、冗余,强迫你照顾本不需要的其它旧逻辑。
继承归一化的好处,比之于代码可读性的降低,已经不是显现的好处了。反正每次写逻辑的时候,那些基础功能的条理总是要过一遍的,还得费劲看看父类是不是这样(ˇ?ˇ)
想来最初的cMap肯定是精小的,被各种功能地图继承后,添加管理代码,慢慢从颜值九分降到两分儿。
其中最重要的动机:仅仅为了获得类的功能而去继承它。
PS:或许还有一点——设计定位缺失。缺乏一个纯粹的数据操作类,单纯的地图数据、相关业务逻辑、消息响应这些是不同层级的东西,全放在一起了。
组合呢?
组合最大的好处就是:它能把功能“比较好追踪的”封在一个集合里面。只在一个跟其它的没关。
这点继承根本做不到。总不免关心基类是不是背着干了什么事。一旦要关心基类逻辑,就得啃其它模块代码。
前段时间读陈硕的书,他有两句棒极的话:面向对象和基于对象,是两种不同的编程方式;虚函数只是编译器给你的便捷函数指针数组而已。
后一句有装逼之嫌,但很有道理,他也这么用的。尤其喜欢“基于对象”的说法。
现在用go,一个语法层面就抛弃了继承的东东,全都是基于对象的组合,清晰度高了许多(当然也有烦人的地方,到处可见的g_server、g_handle )(>﹏<)。
写完看了遍,感觉没什么干货,/(ㄒoㄒ)/~~
吃根冰棍睡觉了=_=~
PS:异形版设计模式那一套,借助继承封装、加控制层级,来硬对需求变动的搞法,非大神总是会被搞的。
PPS:代码写的简单易读改起来也耗不了多少功夫,都能写出简洁代码了,改复杂些还不容易嘛O(∩_∩)O哈!