模块递归拆分法: 设计模式 设计原则,复杂层次设计举例。系统重构 装饰模式,门面模式,代理模式

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/fei33423/article/details/79878754

程序员最牛逼的能力是模块拆分能力,

    然后才能利用模块依赖的工具,java 9 或者 runtime期的osgi ,其他maven插件,maven build期. 其他idea插件,类似阿里云的代码规范检查.

   Modularity—the result of Project Jigsaw https://www.oracle.com/corporate/features/understanding-java-9-modules.html

   https://www.hello2morrow.com/doc/sg7/ReportGeneration-Maven.html

   osgi jigsaw区别 https://stackoverflow.com/questions/7498540/osgi-java-modularity-and-jigsaw/7500268#7500268

  从包到模块化系统, http://www.warski.org/blog/2012/11/lets-turn-packages-into-a-module-system/

  模块化后,在jdk里用同步的方式实现跨模块通知. 方式就是底层调用接口,上层实现类.接口和类分属于两个模块. 这点是技术实现和理念上很重要的突破.

设计模式,设计原则,架构设计的核心出发点,根本原因:

技术同学的升级路线图.

模块递归拆分法: 

1. 实体关系是简单的. 普通开发同学

2. 命名是难的. 比如是乘客和司机的关系. 是多对多关系. 人类语境目前给他定义了订单的概念.然后才有含义了.

3. 流程的拆分和命名,实体的拆分和命名, 功能的拆分是复杂的. 这些都是模块拆分的基础. 新系统这个很难. 几种拆分的思路.

 1. 第一步按角色的行为维度拆.  (注册)用户系统,(下单,抢单)订单和支付系统 [虽然有依赖,但总的来说还是面向用户的平行模块]2. 按照不同的人的维度拆, 营销系统. 企业单模块 3. 按照各个模块都有类似的功能拆. 日志监控系统,通用日志推送系统.4. 模块内又可以按照这个角度继续递归拆分模块. 获取费用流程. 1.费用模块 2.网关模块 3.账户模块. 4.1 模块内多了一个,基础功能和后期叠加功能的拆分.[内部依赖层级就出来了.]例如下普通单,下各种场景的单. 开普通会议,开预约会议. 

     模块是实体,附带了各个流程的方法. 模块内部的拆分比较难,脱离于人了. 

实体生命周期(水平切分,职能图的衍生)\用户(垂直拆分)乘客司机y运营q企业主   
订单模块发单抢单,结束行程     
支付模块支付u收钱     


支付系统\用户乘客    
tradeu生成交易流水.保存交易额外信息(流程图箭头又回到这个系统.)    
费用模块h获取费用    
账户模块b记录支付流水    
      
      

如何对应到代码上?

   大的pojo 需要拆分到小的pojo上. 虽然get同时get,但是 传递的时候向下游只传递需要的pojo
AppointmentConfLazyDO
BaseVideoConferenceLazyDO
HostVideoConfLazyDO 拥有会议主(主叫人)的会议DO. 未来会议主可能会被,也可能会没有. 类的化要重新命名,比较恐怖. 但是面向过程的就比较简单修改的.

VideoConfLazyDO 


1. 解决未来变化新增引发的效率问题。(费用类型新增,导致各个业务变更。分润规则匹配新增不同的匹配字段,商户,城市等。字段接口化。责任链)2. 解决量级引发的效率问题。(接入商户变多,运维人员工作越发困难。)3. 基础服务的沉淀。职责单一,也是封装在系统化的体现。先将属性封装为类。 持久化信息谁来维护(企业的余额值)+提供对应的接口。3.1 老的字段拆分 3.2.新的实体放在新的系统。

平台化,插件化,配置化 解决效率三大途径。


状态机模式是任何业务系统必备的。


三种模式的相同和区别:

相同点: 三者都采用了组合的方案,来实现功能。

区别点: 1. 装饰模式要和被组合的对象保持一致的接口,是继承的另外一种可维护的实现。但是代码会更繁琐。解决功能扩展的变化

             2. 门面模式,是封装繁琐细节,可能会组合多个对象,类似前台。解决量级引发的效率问题。

             3. 代理模式是对资源访问场景下的模式,本来是远程调用,现在变成了本地调用。或者是代理商帮你搞定各种操作。

遥控器下的设计模式。

1. 把按钮和各种指令进行抽象,命令模式

2. 可以匹配不同的厂家冰箱- 适配器模式

3. 可以控制不同的电器- 门面模式。


复杂系统的对标对比(1.先具体代码再重构抽象的实践,方法论 2.直接设计抽象的思考 3.两者结合推演到其他系统主导):

    jdk里有个很好的设计。 1. 先定义了对象类 2.再把行为剥离出来,读一个类,写一个类 outPutStram FileOutPutSram socketOutPutStream 3. 基于行为再进行功能叠加。例如日志打印类。

     如果一个模块需要上游系统的某个数据. 数据分散在一个流程的前,后.

     行为上有继承关系,新的功能的一个好思路是仿造底层依赖层级设计层级框架。例如日志appender。

     流:文件流,网络流。

    流appender:文件流,网络流appender。 先思考基于流的appender的生命周期,初始化(例如流的生成),运行中流程逻辑(流的写入)。 如果直接写File的的appender也可以。但是都要面向接口,抽象编程,然后把具象化的代码部分抽成方法,把这些方法抽出去,封装成一个类【包含新的具象的属性,field】。就变成了面向组合的代码。

   另外一个例子:对账系统。1.对账实体是有层次的。 具体实现可能是ftp,也可能是sftp,也可能是http文件。3.然后ftp 还会有 招商,支付宝等。 2.对账逻辑也是需要有层次的。有一层是直接操作抽象对象实体的。好的代码要分层,也是这个原因。层层依赖。同时注意生命周期的初始化的设计。 对标类比数据库的create和update 之上的业务逻辑。

如何从具象的逻辑(现有代码) 抽出 抽象的部分?

         1. 原有流程中,通过字段值 if else ,产品层面场景扩展。 简单 2. 本来就是两套代码,两套逻辑。例如不同的广告流程。不同的日志appender处理。3. 设计的时候所有属性放到一个类里。还是一点点重构,抽取。再次回味《重构》和《可维护的代码》 新的类取名是什么? 

要抽出的模块的数据分散在一个流程的前,后怎么办?

    更好的办法是将参数封装到模块类中,然后再前面new出来,把需要的数据先抓住, 后面再执行action. 另外一种low的办法是通过形参传递. 很好的例子就是 log类. 含有startTime,中期可能还有exception. 最终才是result和endTIme. 另外一个例子就是 日志数据结构化,打印出来,传递给其他监控系统. 这可以抽出独立的模块. 前面可能也需要收集startTime,endTime.


   

怎么定义模块?

    1.模块是功能集合 2.先明确核心流程 ,再将细枝末节或者精细化运行的东西拆分出去. 3. 不要被用例干扰,不同模块的角色都是相同的,不要从角色的角度先拆分系统.

举例:  1.打车软件.

            1. 核心流程. 乘客发单,司机抢单产生关联.

            2. 核心流程状态机细化. 拆分成 订单和支付系统.

            3. 边界分支, 营销和企业主系统,司机管培系统. 乘客基础系统.

         2. 会议系统

               1. 核心流程是, 用户A发起会议,用户B接受会议. 状态机无法再拆分.[基础会议系统]

               2. 其他边界,增加管理员的功能. [会议管理系统]增加预约的功能.[预约模块] 可能是依赖的,也可能是扩展的.

               3. 具体思考到会议管理系统,需要各种通知和控制,增加通知模块.

   1. 不同流程(状态机下)对应于乘客来说是 平行的关系. 但是内部其实是有依赖的,如果乘客直接感知,那么就平行放置. (类似,订单,用户,支付). 数据库这种,用户无感知的.放在业务层之下. 监控的这种,横跨各级得的.放在左边上.

    已视频会议系统.

一次重构演示. 核心点: 1. 细节业务流程 2.对象

  调度任务算法代码实现和设计

代码实现和数据存储之间的矛盾?

    数据存储是扁平的. 但是组合层次是多层级的. 举个例子. 用户信息和账户信息组合关系. 即使是一对一.

    1. 一份数据从数据库取出后应该根据业务需要拆分到有层级关系的领域实体中. 例如会议中有 uid和deviceid ,一般会认为只和uid相关,但是会有一部分业务是和deviceId相关的. 这个实体就需要拆分了. 如果类似面向过程写的化,可能会比较尴尬,代码.

     面向对象的写法写,里面的层级数据类似hibernate实体模式.  最好是懒加载,1.1 然后会出现线程安全的问题. 1.2 对于1对无线多的那种. 可能本来只操作n中的一个n的状态. 例如 修改会议里某个人的状态.

     应用服务的面向对象和中间件的面向对象的区别是 一个是lazyDO,更新按字段更新, 一个是主体. 整体store. [可以设计改进]

ConferenceController{

  int controllerType;

  int controllerModel;

}   

Conference{

     List<ConferenceMember>

       

  }

   ConferenceMember{

    UserInfo  ;

   }


   2. 不同的领域拆分方式可能不同, 同一个数据,订单域的处理和支付域有不同的处理,例如说费用项. 是否优惠. 

   

 为什么服务端代码可维护性会比较差?

    数据对象层级后. service也需要层级,但是由于service 的数据是通过形参传的,所以service方法可以到处乱放. 依赖关系也非常变态. 



在计算机科学中只有两件事情最难:缓存失效【非功能层面】和取名字 【功能层面】。 有时候关于某个属性的代码,放到这个类,还是放到那个类都是基于类名字决定的。而这些都是顾名思义的沟通,就像文字的含义。所以要见多识广,一般有哪些名字好取,取名的一些关键字是啥。名词,动词名词化。


如何取名?

    案例整理,取名和文字其实是人类沟通的基础,这个还是要见多识广,多看具体案例,只抽象是不行的。

   1.  通用类

   2. 领域类

          要理解各个领域的流程。

          日志: appener

          

   动词名词化。

       listener,filter,

   名词

    


   

附录: 设计模式归类图


  

展开阅读全文

没有更多推荐了,返回首页