好久没有写心得,一来是懒了,二来是难。早就很想写一点有关业务逻辑和模式、算法的关系。能够找到不少理论,但却不多有理论实际相结合的文章。以致于许多人认为服务端代码就是CRUD,算法无用,设计模式无用。还有一种人他们认为设计模式已经融入本身平常的开发中,不须要特别去在乎。这两种状况我都经历过,在写了这么多年代码后才意识到,本身思考的仍是太浅。这篇文章就算是抛砖引玉吧。算法
抽象是从众多的事物中抽取出共同的、本质性的特征,而舍弃其非本质的特征的过程。数据库
在编码工做过程当中,咱们或多或少、有意无心的会进行一些抽象,其中最多见的,就是将事物转为变量。一个简单的变量就能够完成大量的抽象,对于一个书店,设计一个“book”变量,就能够表明全部的书。咱们换一下它的名字改成“goods”,它就能表明任何能够销售的商品,而不只仅是书。这一切看起来很是简单,太简单了,那咱们怎么会在最后要重构代码,技术迭代赶不上业务发展,业务愈来愈好,代码越写越苦?设计模式
在写代码的时候,尤为是互联网常见的服务端业务代码,一般是将现实行为作数字化的过程。例如在超市结算的时候,交钱,拿货走人,在淘宝上要复现这个行为,就得将钱和商品数字化,能够被代码表达。那么,万一有人要插队怎么办?万一网银忽然没法支付怎么办?万一商品中有赠品怎么判断,有活动商品怎么判断,临时有商品由于码扫不出(相似缺货)怎么办,所有都须要考虑,而这些现实中可能发生的问题,在线上也几乎都会发生,代码就要考虑。架构
那么理论上有多少问题须要考虑呢?这让我回想起第一节几率论的课上,老师问的一个问题:2我的约好10点见面,10点同时到达的几率是多少?由于到达的状况是无穷大,因此同时到达几率是0。这个问题也同样,现实中有无穷多种问题须要考虑,而这正是咱们重构噩梦的开始。看过《银河系漫游指南》的同窗应该知道有个“沉思”的超级电脑,它为了计算出宇宙的究极问题,设计了“地球”这个更增强大的电脑。事情就是这样,你要用程序表达这个世界,你须要的资源就是这个世界自己,除非你能从更高的纬度空间来设计。函数
既然咱们不可能表达完整的世界,咱们就须要想办法去表达部分。微服务
现代科学能够根据恐龙的骨骼化石来估计肌肉的走向和大小,从而还原它的外形。咱们不能表达整个世界,但能够想办法表达它的骨架。选择性的表达是最多见的手段,它的重点在于:骨架要在它准确的位置上,不要由于肌肉的缺失而偏离。例如钱,不管在数字钱包的时代仍是纸币的时代,它在生产生活中的价值是不变的。咱们就认为咱们找对了骨架:价值。
咱们能够用 y = ax + b表达任意一条实数域内的直线,可是现实中没有哪条线是直的,但”直“这件事做为骨架是对的。编码
程序就是这个世界的“骨架”。设计
不少问题在有限的边界内是是有限的,好比人际关系,在一代直系血亲中,只会出现父母和子女,可是扩展到整我的类社会,会出现父亲的叔叔,父亲的叔叔的爷爷,父亲的叔叔的爷爷的老婆的娘家。。。经过边界约束问题范围,正是不少DDD设计和微服务架构建设的目的。只不过微服务是一种“死缓”的作法,而DDD只是用于帮助思考,不直接解决问题。为何说微服务是死缓?不少微服务在一开始是“微”服务,随着业务复杂,就变成“肿服务”。拆分?很差意思,业务等不起。code
当咱们给问题画上了边界,怎么才知道边界是合理的?合理的边界应该是:索引
好比:边界 = 性别,值 = [男,女,男改女,女改男,男改女又改男...],每次提到性别这个属性,总有那么几个坏小子要怼我,虽然不是恶意,可是也说明这个边界有点问题。
边界改 = 当前性别,值 = [男,女]。
看,经过“当前”2个字修饰的性别,就好多了。
以前直线上的点已经说明了这种方法,而公式描述又能够经过分段函数、取样函数进一步将边界合理化,好比年龄,只有正整数。
被描述的内容在边界的约束下,元素是惟一的,例如年龄,1岁和2岁就是互斥。若是要描述一群人的年龄,每每会有同岁的,那么边界约束就要增长一个:某人。人在人类现实社会的边界内,是不会重复的,而一我的的当前年龄只有一个,因此一群人中某我的的年龄是惟一的。年龄 + 人 共同约束了一个边界范围,就像分段函数有多个约束条件。你能够看到,慢慢的咱们发如今使用数学方法来描述问题,而这些数学方法的能够经过“算法”最终实现,这就是算法的做用。
有点经验的研发很快就会发现,上面的例子反应在关系型数据库中,就是惟一索引和复合惟一索引,这没什么稀奇的。接下来我要举一个栗子来讲它到底怎么毁了咱们幸福的coding生活。有一个项目,要研究不一样的食草动物吃什么植物。研发团队根据OOP思想,抽象了动物,动物行为,植物。又把植物和动物都抽象为“生物”以描述共性,完美,开始写代码:
动物->吃(植物);//return 要死 or 健康;
好了,一切都很正常,微生物要吃什么,食肉动物要怎么吃都没有问题。还有点扩展性,不错。
同时,在隔壁的一个部门,接了另外一个需求,他们要研究植物,以及他们能够做为那些动物的食物,因而他们写了另外一种代码:
植物->被(动物)->吃();//return 能够 or 不能够;
老板以为两个部门作的有点重复,大家合并吧,反正都是吃,为啥要写两个?两边研发团队当晚打了一架,要求对方放弃本身的方案。最终动物研究部门由于体力比较好,植物研究部门放弃了本身的方案。次日老板走进办公室说:咱们今天研究一种植物叫猪笼草,它吃动物。。。动物部门一咬牙,行,我还有办法,凡是生物它都有吃,他们能够相互吃。。。
新的抽象
生物->吃(生物);//return 能够 or 不能够;
到这里,咱们才看到了真正的骨架,研发团队发现以前的边界都是有缺陷的,动物、植物都只是生物圈的一部分,这还没囊括微生物。重构问题的发生,每每是咱们没有找到骨架,没有摸清边界。在咱们研发的过程中,太多人喜欢写“动物->吃(植物);”这样的代码,很简单,也知足业务当前需求,若是抽象到“生物->吃(生物);//return 能够 or 不能够;”即可能被戴上“过分设计”的帽子。长此以往,你们就怕了。
若是你在纸上画了一条短一点的直线,若是须要一条长直线,延长即可,这很容易。若是一开始画的是弧线,要获得一条长的直线,你就得擦干净重画(重构),或者换个地方(重写)。以生物行为研究为例,若是一开始就把吃的行为赋予“生物”这个基础类,而后只实现动物的“吃”,而不关心植物的吃,好比猪笼草。这就下降了研发压力,后来出现猪笼草的需求,在植物类中覆盖掉吃方法,植物就有不一样的吃法了,微生物也同样。被吃是一个道理,食物链顶端也会被微生物吃掉,一样能够放在生物类中。这样咱们的代码就是按部就班的,可不断叠加。
终于说到设计模式,几乎全部的设计模式,都在帮助咱们解决猪笼草问题,只是方式不一样罢了。可是他们不能解决边界划分和骨架寻找问题。这就是为何不少人以为,用了设计模式,扩展性也不是很好。那么说DDD,DDD实际上是在提醒你,你要去寻找边界和骨架,它也不是告诉你如何去找。以前有个研究DDD的大牛来分享怎么划分边界,有一个简化版工序,大概有19道吧,反正我是记不得,总之不是一件简单的事情。
“业务“分析来获得抽象,分析的方法就是画边界和抽骨架的过程,方法我也就那么点都写在上面了。 而”抽象“须要数学来描述,”数学”须要算法来编码,”编码“须要模式来避免重构。