阿里妹导读:软件开发为什么需要职责驱动设计(RDD)?职责应该如何分配?如何结合架构模式在实际开发中实践落地?本文介绍一种通用的职责分配模式——GRASP,通过举例详解GRASP的几大原则,并分享两个实际运用的案例。
文末福利:免费领取开发者学习资料。
熟练度的提高,专注于某个领域(降低复杂度)。
时间的节约,同一个人在不同工作来回切换需要耗费大量时间。
人工发明的机器和应用(特定领域的工具)。
GRASP:间接性、多态
GoF:大量模式
其他:接口、数据封装
内聚过低,相关功能分散在不同模块中,需要增加额外的耦合使这些功能聚合在一起,发生变更时影响多个模块。
内聚过高,不相关的功能聚集在一个模块中,耦合度高,发生变更时会产生意想不到的影响。
GRASP:防止变异
一个模块的修改会产生涟漪效应,其他模块也需随之修改(通常是内聚低引起的)。
由于模块之间的相依性,模块的组合会需要更多的精力及时间,可复用性低(通常是耦合高引起的)。
依赖关系本身错综复杂难以维护和理解,很容易产生遗漏和问题(这点针对人,人处理复杂性事物时能力是局限的)。
与不稳定元素产生依赖时很容易受到变化的影响(通常无法避免不依赖)。
双向依赖(差)
相互依赖的两个元素不能独立行动,在微服务系统架构的系统中类级别不会产生特别复杂的问题,但是在模块 or 系统级别就特别容易受到变化带来的影响。
举例:A <-> B,A调用B的b接口,B的b接口依赖A的a接口,如果a b接口都要变更,两个系统如何发布?A依赖B先发布,B也依赖A先发布,相互依赖的两个元素不能独立行动。
循环依赖(更差)
循环依赖比双向依赖的的链路更长,影响的范围更大。
单向依赖(好)
深度
B调用A.getC().getD().getE().getF() 获取到F。
广度
在链路变宽的过程中不加以约束和管理很容易产生大杂烩的元素,也很容易产生双向和循环依赖。
内容耦合(高)
当一个模块直接使用另一个模块的内部数据,或通过非正常入口而转入另一个模块内部。
共享耦合/公共耦合(高)
指通过一个公共数据环境相互作用的那些模块间的耦合。
公共耦合的复杂程度随耦合模块的个数增加而增加。
控制耦合(中)
指一个模块调用另一个模块时,传递的是控制变量(如开关、标志等),被调模块通过该控制变量的值有选择地执行块内某一功能;
特征耦合/标记耦合(中)
指几个模块共享一个复杂的数据结构,如高级语言中的数组名、记录名、文件名等这些名字即标记,其实传递的是这个数据结构的地址;
数据耦合(低)
指模块借由传入值共享数据,每一个数据都是最基本的数据,而且只分享这些数据(例如传递一个整数给计算平方根的函数)。
非直接耦合(低)
两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的。耦合度最弱,模块独立性最强。
无耦合(无)
模块完全不和其他模块交换信息。
管理复杂的依赖关系
依赖方向:使用单向依赖,去除或弱化双向依赖,不使用循环依赖。
依赖链路:遵守最少认知原则。
依赖方式:尽量使用数据耦合,少用控制和特征耦合,控制公共耦合的范围,不使用内容耦合,如果依赖的对象不稳定使用非直接耦合来弱化耦合紧密程度。
分配正确的职责减少不必要的依赖:专家、创建者。
通过其他原则和模式减少不稳定元素带来的影响:高内聚、纯虚构、控制器、多态、间接性、最少认知。
分解后的元素更加简单易于理解和维护。
按照相关性拆分可以提高重用性。
难以理解
难以复用
难以维护
经常会受到变化影响
B“包含”或聚合A。
B记录A。
B频繁使用A。
B具有A的初始化数据,该数据将在创建时传递给A。
GRASP:低耦合
GoF:具体工厂、抽象工厂
其他:整体-部分
Order包含Goods(Order脱离Goods就失去了完整性,没有存在的意义)。
Order记录相关的Goods。
Goods初始化数据:
情况一:只需要订单上的Goods数据,这种情况Order具有Goods的初始化数据。
情况二:订单上的Goods数据不完整,这种情况Order只有Goods初始化数据的一小部分,Order不能做为创建者。
对象使用自身信息来完成任务,所以信息的封装性得以维持,因此支持了低耦合(至少不会增加耦合性)。
行为分布在那些具有所需信息的类之间,这样功能更集中,因此支持了高内聚。
GRASP:低耦合、高内聚
支持高内聚,因为职责被解析为细粒度的类,这种类只着重于极为特定的一组相关任务。
增加了潜在的复用性。
GRASP:低耦合、高内聚。
通常接纳本来是基于专家模式所分配给领域类的职责。
所有GoF设计模式都是纯虚构,事实上所有其他设计模式也都是纯虚构。
代表整个“系统”、“根对象”、运行软件的设备或主要子系统,这些是外观控制器的所有变体。
代表用例场景,在该场景中发生系统事件。
GRASP:纯虚构
GoF:命令、外观
其他:层
api层
根对象
接口
GRASP:防止变异
GoF:大量模式
GRASP:防止变异、低耦合、大量间接性中介都是纯虚构
GoF:大量模式
public class CloseOrderService {
JpaTransactionManager tm;
public void invalid_order(Long orderId, Long userId, Short processGroup)
throws UserException, SystemException, UnknownException {
//其他逻辑。。。省略
// 开启事务
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
TransactionStatus ts = tm.getTransaction(def);
try {
order = orderDAO.get(orderId);
order.setStatusCode(toStatus);
order.setUpdatedAt(new Timestamp(System.currentTimeMillis()));
orderDAO.save(order);
//提交事务
tm.commit(ts);
} catch (Exception e) {
if (!ts.isCompleted()) {
//回滚
tm.rollback(ts);
}
if (e instanceof SatisfiedStateException) {
return;
}
throw e;
}
}
public void invalidOrder(){
}
}
public interface OrderDAO extends JpaRepository<OrderPO, Long> {
Long generateGlobalOrderId( Long userId,
Long restaurantId,
String seqName);
}
调用OrderDAO的类(70多个类)都需要替换为新的dao。
使用JpaTransactionManager.getTransaction()的位置需要替换为MyBatis的TransactionManager。
使用@Transactional(transactionManager = "rstOrderTransactionManager")的位置需要改为编写事务提交和回滚的代码块儿,便于做灰度。
以上改动的位置需要增加开关做灰度。
创建订单和支付单是在一次操作中完成。
用户回到订单列表页点击“去支付”时创建支付单。
订单号
支付金额
支付类型
一堆支付系统分配的用于识别业务的参数
bos比order多出识别订单结构的成本。
bos比order多出认知交易域业务知识的成本。需要深入了解交易状态,这样才知道什么状态才能去支付(一般是去问order服务的开发),打破了边界。
如何构建技能树,补全技术债?阿里妹精选上百本阿里系技术电子书,覆盖 Java、物联网、云原生、前端、大数据、开源、AI 等多个领域,深度分享阿里工程师实践精华,另外还有各种技术大会资料、面试宝典等,助力开发者学习进步!
识别下方二维码加「阿里妹」微信好友,回复 “资料大全” 立即领取吧~