查看原文
其他

写代码精进之路:快反而是慢?巧用DDD设计,慢中变快!

技术琐话 2022-07-13

The following article is from 程序架道 Author 程序架道



周一来到,小明接手了一个需求。

 

要搞一次促销抽奖活动,奖池里配置了很多奖项,我们需要按运营预先配置的概率抽中一个奖项。

 

解决思路貌似很简单,生成一个随机数,匹配符合该随机数生成概率的奖项即可。

 

实现貌似也很简单。

 

很快,弄了两张表。

 

 

很快,弄了三个类。

 

 

 

很快,搞定,上线,花费时间不到半天,小明很得意。

 

可是,需求变化也很快。

 

产品经理找到小明,规则变化了,要增加过去两个月内购买过10个订单以上的用户才可以参与抽奖。

 

小明应该把代码添加到哪个位置呢?

 

需求再次变化了,未来三天购买订单数满足跨店才能参加抽奖。

 

小明应该把代码添加到哪个位置呢?

 

即使后面来的两次需求,都找到了自己的添加位置,第四次、五次。。。呢。

 

每次添加新代码的时候,如何快速的定位?

 

开发完成后,测试人员要不要全回归测试呢?

 

毕竟你都改动到了好几处地方了,如果不全回归,以前的代码会不会有问题呢?

 

如果这样都做了,后面没增加一个需求变化,到底是快,还是慢了呢?

 

业务逻辑复杂了,业务的逻辑、状态会散落到大量方法中,你没有抽象,就没有办法模块化,就不能区分核心和周边,需求越来越多,你就只能硬写,你的这种硬写,往往都是写到了核心模块里面了,之所以成为核心,不就是希望你不要总是改变它吗,要尽可能将其变为只读的,否则,你当初的快就是后来的慢;

 

上面的编程方式是哪种方式呢,什么编程风格?

 

基于“Service + 贫血模型”的实现。

 

大家,为什么总是习惯用上面那种方式编写代码呢?

 

可能是业务简单到就是基于SQL的CRUD。

 

可能是在service层中可以定义任何操作。

 

可能是思维已固化。

 

可能是转型成本太大。

 

可能是。。。

 

如何应对变化,如何不让当初的快,变成后面的慢呢。

 

就是要千方百计地将核心模块和周边模块,变成正交性的设计,让核心模块变成只读,每次来一个需求只需要修改或增加周边模块就好了。

 

 

那如何才能一步一步实现正交设计的代码呢,最原始的基础就是要用丰满的面向对象技术,用丰满的面向对象技术的基础方法又是充血模型。

 

基于贫血模型的传统的开发模式,比较适合业务比较简单的系统开发;相对应的,基于充血模型的 DDD 开发模式,更适合业务复杂的系统开发;比如,包含各种利息计算模型、还款模型等复杂业务的金融系统;

 

应用基于充血模型的 DDD 的开发模式,需要事先理清楚所有的业务,定义领域模型所包含的属性和方法;领域模型相当于可复用的业务中间层;新功能需求的开发,都基于之前定义好的这些领域模型来完成;

 

越复杂的系统,对代码的复用性、易维护性要求就越高,就越应该花更多的时间和精力在前期设计上;而基于充血模型的 DDD 开发模式,正好需要前期做大量的业务调研、领域模型设计,所以它更加适合这种复杂系统的开发;

 

好了,小明也觉得使用丰满的对象编程技术,会比较好。

 

那问题来了,小明需要第一次,就按照这样的编程风格编程吗?

 

第一次就需要考虑那么的周全吗?

 

第一次就需要面向未来设计吗?

 

我个人的建议,你可以被子弹打中一次,但是不要被打中第二次。

 

为什么这样说呢。

 

 

 

对这个图还有印象吗,整洁架构,向内依赖,最”内“里面是什么呢。

 

是个领域模型。

 

如果你第二次,第三次依然没有抽象出领域模型,你的每一次以为的快,都是为后面每一次的慢,埋下了“因缘”。

 

有没有好的策略,来指导如何判断要不要搞成所谓的领域形式呢。

 

个人建议:

 

1、判断是否你的程序只为一个业务方服务。比如财务人员要用到、营销人员要用到、运营人员要用到。如果是,就要提前考虑沉淀出业务领域模型。

 

2、判断是否你的程序只为一个业务模式服务。比如拼团业务要用到、国际业务要用到、健康业务要用到。如果是,就要提前考虑好业务身份的判断且抽象共享服务。

 

有没有好的原则,我按照这样的原则进行设计,进行开发就是能符合”高大上“的技术范的领域模型呢。

 

个人建议:

 

1、SOLID设计原则和23设计模式,优选SOLID设计原则。你问我为啥这样选,打个比喻的话,SOLD就像独孤九剑,23设计模式就像九阳真经。练招式起步容易,而且那些SOLID原则里面所包含的内容的内核,你只要”细思极恐“下,暂且用这个词语,一样能有一翻“洞天”。

 

练习独孤九剑也需要悟得。

 

当你可以将这其中的5个设计原则能够融汇贯通的时候,也是你的任督二脉打通之时。

 

 

当然,还有其它简单易行的原则,比如DRY、KISS、YAGNI、LOD等等。

 

2、增加辅助措施,没有规范,则显凌乱,若要有序,执行编程规范,同时把代码重构养成日常行为。

 

 

 

小明想,思维建议策略有了,设计指导原则也有了,有没有代码让我学习一番呢。

 

如今,学习,最不缺的就是代码,看开源软件。

 

Tomcat程序就是非常优秀的开源软件,你不会不这样认为吧,你的程序打包都发布到它的容器里面了,这里面有非常优雅的设计。

 

试想,Tomcat这样的web容器是怎么样接收我们的http请求的呢。

 

我们的queryOrder请求为什么就被送到相应的动作上执行的呢。

 

Tomcat要为每一个请求都加一个if else来判断,才能实现不同动作请求到不同类文件上面吗。

 

如果都加上这样的if else判断,是要在业务类里面增加吗,那不就跟业务类耦合了吗。

 

因此,Tomcat的程序的设计者们,想了很优美的设计思路。

 

他们发明了 Servlet 容器,Servlet 容器用来加载和管理业务类。HTTP 服务器不直接跟业务类打交道,而是把请求交给 Servlet 容器去处理,Servlet 容器会将请求转发到具体的 Servlet,如果这个 Servlet 还没创建,就加载并实例化这个 Servlet,然后调用这个 Servlet 的接口方法。因此 Servlet 接口其实是 Servlet 容器跟具体业务类之间的接口。

 

图自 李号双

 

这个设计里面,充满了浓浓的我们先前说的SOLID味道和正交设计的味道,你可以找出来吗。

 

只有当我们具备了,良好的开抽象维意识,开闭认知意识,原则学习意识,分合执行意识,实践到了,也就摸到了,我们写程序起初的慢,才是后续增加需求的快。

 

 

----END----


这里记录,我每周碰到的,或想到的,引起触动,或感动的,事物的思考及笔记。不见得都对,但开始思考记录总是好的。

 

与爱学习、爱思考、爱记录的你共勉。


 

参考资料:

https://tech.meituan.com/2017/12/22/ddd-in-practice.html 《领域驱动设计在互联网业务开发中的实践》 美团技术团队

https://www.cnblogs.com/wod-Y/p/12337721.html 《如何利用基于充血模型的DDD开发一个虚拟钱包系统》杨海星

https://time.geekbang.org/column/article/95480?cid=100027701 《你应该知道的Servlet规范和Servlet容器》李号双



往期推荐:


技术琐话 



以分布式设计、架构、体系思想为基础,兼论研发相关的点点滴滴,不限于代码、质量体系和研发管理。



您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存