1 复杂、繁杂、庞杂
在开发工作中我们经常会听到:这个业务很复杂,这个系统很复杂,这个逻辑很复杂,只要是处理遇到困难的场景,似乎都可以使用复杂这个词进行描述。
但是我认为困难之所以困难,原因还是有所不同的,不能用复杂这个词笼而统之,有加以区分的必要。大体上我认为可以分为复杂、繁杂、庞杂三个类型。
复杂和繁杂二者均包含分支多和逻辑多的含义,但是不同之处在于,复杂场景是可以理出头绪的,如果设计得当,是可以设计出很优雅的系统的。但是繁杂场景是难以理出头绪的,为了兼容只能打各种补丁,最终积重难返只能系统重构。
还有一种类型可以称之为庞杂,当数量达到一定规模时,复杂和繁杂都可以演化为庞杂。虽然同样是庞杂,但是也有复杂庞杂和繁杂庞杂的区别。本文只要讨论清楚复杂和庞杂,只要加上数量维度就是庞杂。
我们在开发中可以写复杂的代码,要尽量避免繁杂的代码,其中代码耦合就是一种典型的繁杂场景,模块间高度耦合的代码导致最终根本无法维护,本文我们讨论七种代码耦合类型。
2 代码耦合类型
七种代码耦合类型根据耦合程度由高到低排序分别是:内容耦合、公共耦合、外部耦合、控制耦合、标记耦合、数据耦合和非直接耦合。
2.1 内容耦合
一个模块可以直接访问另一个模块的内部数据被称为内容耦合,这是耦合性最强的类型,这也是我们需要尽量避免的。
假设模块A是订单模块,模块B是支付模块,如果支付模块可以直接访问订单数据表,那么至少会带来以下问题。
第一个问题是存在重复的数据访问层代码,支付和订单模块都要写订单数据访问代码。 第二个问题是如果订单业务变动,需要变更订单数据字段,如果支付模块没有跟着及时 变更,那么可能会造成业务错误。
第三个问题是如果订单业务变动,需要分库分表拆分数据,如果支付模块没有跟着及时变更,例如没有使用shardingKey进行查询或者旧库表停写,那么可能会造成支付模块严重错误。
第四个问题是业务入口没有收敛,访问入口到处散落,如果想要业务变更则需要多处修改,非常不利于维护。
2.2 公共耦合
多个模块都访问同一个公共数据环境被称为公共耦合,公共数据环境例如全局数据结构、共享通信区和内存公共覆盖区。
例如在项目中使用Apollo动态配置,配置项A内容是一段JSON,订单模块和支付模块均读取并解析这段数据结构进行业务处理。
public class ApolloConfig {
@Value("${apollo.json.config}")
private String jsonConfig;
}
public class JsonConfig {
public int type;
public boolean switchOpen;
}
public class OrderServiceImpl {
public void createOrder() {
String jsonConfig = apolloConfig.getJsonConfig();
JsonConfig config = JSONUtils.toBean(jsonConfig, JsonConfig.class);
if(config.getType() == TypeEnum.ORDER.getCode() && config.isSwitchOpen()) {
createBizOrder();
}
}
}
public class PayServiceImpl {
public void createPayOrder() {
String jsonConfig = apolloConfig.getJsonConfig();
JsonConfig config = JSONUtils.toBean(jsonCo