软件开发必修课:你应该知道的GRASP责任分配模型性能、可用??性、可维护性、可扩展性、性能、一致性、容错性、稳定性、可重用性、幂等性、兼容性等简单的”。如何组织一个复杂的系统?将复杂的事物分解成不同的层次。级别代表不同的抽象级别。一层建立在另一层之上,每一层都屏蔽了上层的内部复杂性。1、为什么要用RDD?在RDD中,我们相信“软件对象有责任”。这个定义符合人们在社会群体中划分和协作的方式。软件也是人写的,所以基于职责设计的软件系统更符合人的行为习惯,更易于理解和管理。在微服务架构中,不同的组织和人负责不同的系统。把系统看成一个对象(人),系统提供的接口就是对象(人)的责任。职责驱动设计的核心是考虑如何为对象分配职责,适用于从系统到对象的任何规模的软件。责任分配的本质是分工,分工是劳动生产率提高的主要原因。提高熟练程度,专注于某个领域(降低复杂性)。节省时间,同一个人在不同工作之间来回切换需要花费大量时间。人工发明的机器和应用程序(特定领域的工具)。2、如何给对象(元素)分配职责?责任的分配应该从明确的责任描述开始。对于软件领域对象,领域模型描述了领域对象的属性和关联,以及相应类的属性和引用。用例模型包含一系列Behavioral活动对应类的方法。领域模型的创建方法请参考《UML和模式应用》、UDD、DDD。使用GRASP(一般职责分配软件模式)模式来分配职责。GRASP是一种通用的责任分配模式,它命名和描述了一些基本的责任分配原则。总共有9种模式(一些GRASP原理是基于其他原理和设计模式的)总结起来,设计模式有上百种,但要记住GoF的23种设计模式就已经很困难了,更不用说记住每一种的细节了模式,所以需要对设计模式进行有效的分类。GRASP中的Principles描述了模式的本质。除了有助于加速设计模式的学习,也更有效地发现现有设计的问题,即归纳法的价值)。当谈论低耦合和高内聚时,我们到底在谈论什么?问题不是高耦合和低内聚,而是它产生的负面影响。负面影响通常在变化发生时反映出来。这些负面影响会影响到我们的开发效率、稳定性、可维护性、可扩展性、可重用性等,整个GRASP的核心就是如何防止突变(change)。在学习过程中,我发现GRASP缺乏结构化的展示和归纳结果。通过自己的理解,将开发中常用的GoF设计模式、面向对象设计原则、架构设计原则和GRASP联系起来:注:此图可能不够概括准确,正在逐步学习修改。3、GRASP责任分配模式1防止变异这种模式基本上等同于信息隐藏和开闭原则。如何在不修改原有功能的情况下扩展变化的部分?识别不稳定因素特别困难,也决定了我们能否做出符合开闭原则的设计。问题:如何设计对象、子系统和系统,以便它们内部的变化或不稳定性不会对其他元素产生不利影响。解决方案:确定预期的变化或不稳定性,并分配创建超出这些变化的稳定接口的责任。相关原则和模式:GRASP:间接、多态GoF:大量模式Others:接口、数据封装2低耦合、高内聚耦合是衡量一个元素与其他元素之间的连接、感知和依赖关系。聚集是衡量元素职责的相关性和集中度(这里的元素是指类、系统、子系统等),耦合和内聚从不同的角度看问题,它们相互依赖,相互影响(下两点也可以反过来说):内聚性太低,相关功能分散在不同的模块中,需要增加额外的耦合将这些功能集中在一起,发生变化时影响多个模块。如果内聚度太高,不相关的功能集中在一个模块中,耦合度高,发生变化时会产生意想不到的影响。低耦合耦合是衡量一个元素与其他元素的连接、感知和依赖程度的指标。这里的元素是指类、系统、子系统等。问题:如何减少依赖,减少变更的影响,提高复用性?解决方案:分配职责并使耦合尽可能低。使用此原则来评估备选方案。相关模式或原则:GRASP:PreventingVariation注意:耦合不能独立于其他原则(如专业知识和高内聚)来考虑。紧耦合系统在开发阶段有以下缺点:一个模块的修改会产生连锁反应,其他模块也需要相应修改(通常是低内聚造成的)。由于模块之间的依赖关系,模块的组合会需要更多的精力和时间,复用性低(通常是高耦合造成的)。解释:耦合是指元素之间存在依赖关系。当我们谈论“高耦合”时,我们在谈论什么?就是依赖带来的负面影响,所以低耦合的核心就是解决坏的依赖。高或低是衡量耦合结果质量的标准,而不是判断耦合结果质量的标准。用“badcoupling”和“loosecoupling”来形容更准确。不良耦合的负面影响主要有两个:依赖本身错综复杂,难以维护和理解,容易造成遗漏和问题(这是对人来说,处理复杂事物的能力有限)。在创建具有不稳定元素的依赖项时容易发生变化(没有依赖项通常是不可避免的)。那怎么办呢?首先评估依赖关系的好坏:依赖方式、依赖方向、依赖链接。方向:双向依赖(差)两个相互依赖的元素不能独立行动。在微服务系统架构的体系中,类层面不会造成特别复杂的问题,但是在模块或者系统层面,就特别容易发生变化。.例子:A<->B,A调用了B的b接口,B的b接口又依赖了A的a接口,如果需要改变ab接口,两个系统怎么发布?A依赖B先释放,B也依赖A先释放,两个相互依赖的元素不能独立行动。Circulardependencies(worse)循环依赖比双向依赖的链接更长,影响范围更广。单向依赖(好)链接:深度B调用A.getC().getD().getE().getF()得到F。广度在加宽链接的过程中没有约束和管理,很容易生成大杂烩元素,也容易产生双向和循环依赖。Mode:ContentCoupling(High)当一个模块直接使用另一个模块的内部数据,或者通过异常入口传递给另一个模块。SharedCoupling/CommonCoupling(High)指那些通过公共数据环境交互的模块之间的耦合。公共耦合的复杂性随着耦合模块的数量而增加。控制耦合(中)是指一个模块调用另一个模块时,传递的是一个控制变量(如开关、标志等),被调用模块通过控制变量;特征耦合/标记耦合(中)是指几个模块共享一个复杂的数据结构,如数组名、记录名、文件名等。模块通过传值共享数据,每个数据都是最基本的数据,只有这些数据是共享的(例如将整数传递给计算平方根的函数)。间接耦合(低)两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的。耦合度最弱,模块独立性最强。非耦合(无)模块根本不与其他模块交换信息。解决不良依赖:Managecomplexdependencies依赖方向:使用单向依赖,去除或弱化双向依赖,不使用循环依赖。依赖链接:遵循最少感知原则。依赖模式:尽量使用数据耦合,少用控制和特性耦合,控制公共耦合的范围,不使用内容耦合。如果依赖对象不稳定,使用间接耦合来削弱耦合紧密度。分配正确的职责以减少不必要的依赖:专家、创作者。通过其他原则和模式减少不稳定元素的影响:高内聚、纯虚构、控制器、多态、间接、最小意识。高内聚内聚是衡量元素职责相关性和集中度的指标。问题:如何让对象保持专注、可理解、可维护,并且能够支持低耦合?解决方案:根据依赖关系分配职责,保持高内聚。优点:分解后的元素更简单,更容易理解和维护。按依赖关系拆分提高了可重用性。相关原则和模式:单一职责原则、关注点分离、模块化。低内聚的缺点:低内聚的类做了很多不相关的工作,或者需要完成很多工作。这样的类会造成以下问题:难以理解难以重用难以维护经常受变更影响示例:某变更影响从3个模块改为1个总结通过结构化管理保持低耦合和高内聚。3CreatorCreator指导我们分配与创建对象相关的职责。选择这个是为了保持低耦合。问题:谁应该负责创建类的新实例?解决方案:当以下条件之一为真时,将创建类A的责任分配给类B(当多于1个为真时,通常首选遏制或聚合)。B“包含”或聚合了A。B记录了A。B经常使用A。B具有A的初始化数据,这些数据将在创建时传递给A。优点:支持低耦合,因为创建者和被创建者已经关联,所以这种方式不会增加耦合。相关模式或原理:GRASP:低耦合GoF:具体工厂,抽象工厂Others:Whole-Part注:Include(笔者这里标注“”,因为uml中的inclusion表示用例关系,也可以用来说明对象关系),Aggregation,Whole——部分见UML定义;包括强依赖(A是B的子集,A属于B,A缺失时B不是一个整体),聚合是弱依赖(B由A组成,A不属于B)。示例:OrdercontainsGoods(OrdercontainsGoods(没有Goods,Order就失去了完整性,也就没有了存在的意义)。订单记录相关商品。商品初始化数据:情况一:只需要订单上的商品数据,此时订单中有商品的初始化数据。案例二:订单上的Goods数据不完整。在这种情况下,Order只有一小部分Goods的初始化数据,Order不可能是creator。4信息专家(或专家)“信息”不仅仅指数据。问题:给对象分配职责的基本原则是什么?解决方案:将职责分配给信息专家,该专家拥有履行此职责所需的信息。支持低耦合(至少不增加耦合)。行为分布在那些具有所需信息的类之间,使功能更加集中,从而支持高内聚。相关模式或原则:GRASP:低耦合,高内聚注:与“关注点分离”一起使用,使对象进一步内聚,从而达到高内聚,降低耦合。示例:获取所有购买商品的总金额,Order和Goods是一对多的关系。分析:Order本身是和Goods相关联的,了解Goods的结构。图中Client通过Order获取Goods,进行逻辑运算得到商品的总量。这种方法会产生不必要的依赖性并增加耦合的数量。计算商品总金额的责任最适合Order。扩展:在某些情况下,这种解决方案并不适用,通常是由于耦合和内聚的问题,例如:谁应该将对象A存储在数据库中?按照原则,每个类都应该有自己坚持的能力。5PureFiction为了保持良好的耦合和内聚,虚构业务中不存在的对象来承担责任。问题:当你不想违反高内聚低耦合或其他目标,但专家模式提供的解决方案不合适时,哪些对象应该承担这个责任?解决方案:分配一组高内聚的职责,这个类不代表问题域的概念——虚构事物来支持高内聚、低耦合和重用。优点:支持高内聚,因为职责被分解为细粒度的类,这些类只关注一组非常具体的相关任务。增加潜在的可重用性。相关原则和模式:GRASP:低耦合,高内聚。接受根据专家模式分配给领域类的职责是很常见的。GoF的所有设计模式都是虚构的,事实上所有其他设计模式也都是虚构的。示例:计算货物总量。按照专家模式,计算商品总量的责任也应该交给Order。如果这样分配,就会有相关的商品:总重量,总体积,总XX。这时Order的职责会越来越多,可能会产生额外的耦合,最好通过纯虚构的对象来分配这些职责来设计。虚构对象GoodsItems承担与商品聚合计算相关的责任。延伸:经常发现代码中使用了Util、Handler、Service等虚构的类。缺点是这些类通常是单例和共享的。这些虚构的类会承担越来越多的职责(一个Util类就是2000行代码)。更接近业务的虚拟对象可以促进对耦合关系的理解和管理。6控制器解决方案:将职责分配给一个可以代表以下选择之一的类:代表整个“系统”、“根对象”、运行软件的设备或主要子系统,它们都是外观控制器的变体.表示发生系统事件的用例场景。相关模式:GRASP:纯虚构GoF:命令,外观其他:控制器层核心是提供统一入口,避免客户内部耦合元素,维护好边界:api层根对象接口7多态性问题:如何处理类型的选择?可插入软件组件是如何创建的?解决方案:当相关选择或行为因类型而异时,使用多态操作为不同的行为类型分配责任。优点:高度可扩展,不会影响客户。相关原则和模型:GRASP:PreventionofvariationGoF:大量订单退款时,需要计算用户退款金额和商户抵扣金额。在新零售业务进来之前,直接使用计算服务返回的数据结构。新零售进来后数据结构不统一需要适配,多态后的代码可扩展性强。在微服务架构中,更复杂的多态问题通常通过增加一层来解决,比如支付网关、交付网关。8间接计算机科学中的大多数问题可以通过添加一层来解决,如果不添加另一层的话。相反,大多数性能问题可以通过删除一层来解决。问题:为了避免两个或多个事物之间的直接耦合,应该如何分配职责?解决方案:将职责分配给中介对象,充当其他构造或服务之间的中介,避免它们之间的直接耦合。优点:实现了组件之间的低耦合。相关原则和模式:GRASP:突变预防、低耦合、大量间接调解器纯属虚构GoF:大量模式注意:间接性通常用于支持突变预防。四种架构模型除了职责分配原则之外,还需要一些架构模型来帮助我们更好的实现。1分层架构独立存在于分布式系统中,可以独立改变而不影响其他系统。但是,随着业务整体复杂度的增加,也带来了一些负面影响:因为整体被分解成大量独立的系统,随着复杂度的增加,系统之间的依赖关系会变得错综复杂,一个系统的变化会影响其他系统,也会出现意想不到的问题,效率也会下降。这时候就需要重新设计分布式系统的逻辑架构,解决系统间耦合和内聚性差的问题,从而提高效率。分层架构是一种非常实用和普遍的方式。TCP/IP、HTTP、操作系统等都使用了分层。分层的本质很简单:通过关注点分离实现高内聚;通过向下依赖和拒绝循环依赖来实现高内聚,使用接口,来实现低耦合。分层架构也有缺点:按照分层架构,消息消费应该在基础设施层,但是消息消费是为了执行一定的业务逻辑,所以需要依赖应用层或者领域层。如果真的这样写,就会出现Circulardependencyproblem。依赖性问题可以通过依赖性倒置来解决。2HexagonalArchitecture(洋葱圈架构)六角架构(HexagonalArchitecture),又称端口和适配器架构风格,其中具体的数字“六”没有特殊含义,只是表示一个“量级”的意思,六边形的定义只是为了便于更生动的理解。六边形架构提倡以全新的视角看待整个系统,其中有两个区域:“外部区域”和“内部区域”。外部区域的不同客户端可以提交输入(网络请求、定时脚本、消息消费等),而内部区域是处理特定逻辑的地方。来源:https://www.jianshu.com/p/d3e8b9ac097b五种情况情况一:Jpa被替换为Mybatis@ComponentpublicclassCloseOrderService{@Autowired(required=false)@Qualifier("rstOrderTransactionManager")JpaTransactionManagerrtm;publicvoidinvalid_order(LongorderId,LonguserId,ShortprocessGroup)throwsUserException,SystemException,UnknownException{//其他逻辑。..Omit//开启交易DefaultTransactionDefinitiondef=newDefaultTransactionDefinition();TransactionStatusts=tm.getTransaction(def);try{order=orderDAO.get(orderId);order.setStatusCode(toStatus);order.setUpdatedAt(newTimestamp(System.currentTimeMillis()));orderDAO.save(order);//提交事务tm.commit(ts??);}catch(Exceptione){if(!ts.isCompleted()){//回滚tm.rollback(ts);}if(einstanceofSatisfiedStateException){return;}throwe;}}@Transactional(transactionManager="rstOrderTransactionManager",rollbackFor=Exception.class)publicvoidinvalidOrder(){}}@ComponentpublicinterfaceOrderDAOextendsJpaRepository
