不是谈帕斯卡,骆驼命名等问题。而是说说业务模块和技术模块的一些命名优劣。以订单模块为例,我会给出OrderService和OrderDao中一些基本的接口定义。
下面先列出,我认为不太友好的服务接口定义方式:
// 我先给出DAO的定义,这个DAO的定义没有问题的,主要看下面的OrderService的定义
public interface OrderDao {
// 根据用户Id和状态查询订单
List<Order> selectByUserIdAndState(String userId, List<Integer> orderStates);
// 根据订单Id查询订单
Order selectByPrimaryKey(String id);
// 插入订单数据
int insert(Order order);
// 更新订单状态
int updateStateById(String id, Integer state);
}
Bad Case
// 这个接口定义是我自己编出来的,虽然不完全真实。但我想大家项目中一定见到过在业务逻辑中这么与定义方法名字。
// 出现了insert,update,select等等技术行为动作
// 看着像是DAO的定义,但有不完全像,下面注释具体说问题。
public interface OrderService {
// 根据用户Id和状态查询订单
// 问题① select不应该出现在业务层,可以使用query代替,偏业务化
// 问题② state状态不应该暴露给外面,订单状态应该有订单模块内部控制
List<Order> selectOrdersByUserIdAndState(String userId, int state);
// 根据订单Id查询订单
// 问题① select不应该出现在业务层,可以使用query代替,偏业务化
Order selectOrderById(String orderId);
// 插入订单信息
// 问题① insert不应该出现在业务层,从业务角度我们可以说初始化订单,生成订单,创建订单等等,但是不要用插入这种数据库专用名词
boolean insertOrder(String userId, String price, String productId);
// 更新订单状态
// 问题① update不应该出现在业务层,我们可以换个方式去暴露接口
// 问题② state状态不应该暴露给外面,订单状态应该有订单模块内部控制
void updateOrderState(String orderId, int state);
}
Good Case
// 优化过后
public interface OrderService {
// 查询用户初始化订单
// ① 把select修改成了偏业务化的query,也不再有by,userId,state这种数据库关键字或者字段化的概念了
// ② 把订单状态对其他模块屏蔽了,其他模块只需要向订单模块发起查询初始化订单的请求,不再需要知道初始化订单状态是啥数字
List<Order> queryUserInitOrders(String userId);
// 查询用户完成订单
// ① 把select修改成了偏业务化的query,也不再有by,userId,state这种数据库关键字或者字段化的概念了
// ② 把订单状态对其他模块屏蔽了,其他模块只需要向订单模块发起查询完成订单的请求,不再需要知道完成订单状态是啥数字
List<Order> queryUserFinishOrders(String userId);
// 根据订单Id查询订单
// ① 把select修改成了偏业务化的query,也不再有by,orderId这种数据库关键字或者字段化的概念了
Order queryOrder(String orderId);
// 初始化订单
// ① 插入订单的概念被修改成初始化订单,更偏业务化,而不是机械动作
boolean initOrder(String userId, String price, String productId);
// 支付成功通知
// ① 更新订单状态的行为不再交由外部模块控制,而是由订单模块内部自己控制。
// 订单模块只管给外部提供支付成功的通知接口,其他模块不关心通知过后订单模块干了啥,接到通知,是否要修改订单状态,订单状态要修改成什么,是订单模块自己的事情。
void paySuccessNotice(String orderId);
// 支付失败通知
// ① 更新订单状态的行为不再交由外部模块控制,而是由订单模块内部自己控制。
// 订单模块只管给外部提供支付失败的通知接口,其他模块不关心通知过后订单模块干了啥,接到通知,是否要修改订单状态,订单状态要修改成什么,是订单模块自己的事情。
void payErrorNotice(String orderId);
}
// 根据上述订单服务,简单列了几个基本的订单状态,不是很严谨。
public enum OrderStateEnum {
INIT(0),// 初始化
PAY_ING(1),// 支付中
ORDER_FINISH(2),// 订单完成
ORDER_CLOSE(3),// 订单关闭
;
private int state;
OrderStateEnum(int state) {
this.state = state;
}
public int getState() {
return state;
}
}
在三层架构(展示层,业务逻辑层,数据访问层)下,我们编码时应该要充分考虑每一层各自的职责。业务逻辑层的重要性谁都清楚。但很多时候,我经常看到业务逻辑层的方法命名长得跟DAO类似。特别是updateXXX,insertXXX。分层或者分模块的目的就是为了解耦合,各自职责独立,我们应该严格遵守迪米特法则,把对外暴露的接口信息降到最低。
有朋友会说,queryUserInitOrders和queryUserFinishOrders分开了,那如果订单表有10个状态,岂不是得写10个方法。我想说对,就是这样。封装10个不同状态的查询接口是订单模块细粒度提供服务的职责。
如果想要查询某用户下所有终止状态(完成和关闭)的订单,还需要封装queryUserEndOrders的服务,这算是门面模式。
绝对业务化的命名方式有几个好处。
① 模块多了,各个模块的业务方法多了,维护业务+数据库混杂命名的方法,会晕的。维护纯业务命名的方法会舒服一些。
② 其他模块的开发者并不想知道你内部模块实现细节,干嘛要在命名中带有隐晦的提现。
③ 做一个系统,我一般定先定流程骨架,业务模块只写接口定义,不写实现,业务模块的命名对我后期来完善方法具体实现有指导价值。
④ 显得专业,不,是真的专业。