优雅代码 - 业务层代码命名

947 阅读5分钟

优雅代码汇总篇

不是谈帕斯卡,骆驼命名等问题。而是说说业务模块和技术模块的一些命名优劣。以订单模块为例,我会给出OrderServiceOrderDao中一些基本的接口定义。

下面先列出,我认为不太友好的服务接口定义方式:

// 我先给出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类似。特别是updateXXXinsertXXX。分层或者分模块的目的就是为了解耦合,各自职责独立,我们应该严格遵守迪米特法则,把对外暴露的接口信息降到最低。

有朋友会说,queryUserInitOrdersqueryUserFinishOrders分开了,那如果订单表有10个状态,岂不是得写10个方法。我想说对,就是这样。封装10个不同状态的查询接口是订单模块细粒度提供服务的职责。

如果想要查询某用户下所有终止状态(完成和关闭)的订单,还需要封装queryUserEndOrders的服务,这算是门面模式。

绝对业务化的命名方式有几个好处。

① 模块多了,各个模块的业务方法多了,维护业务+数据库混杂命名的方法,会晕的。维护纯业务命名的方法会舒服一些。

② 其他模块的开发者并不想知道你内部模块实现细节,干嘛要在命名中带有隐晦的提现。

③ 做一个系统,我一般定先定流程骨架,业务模块只写接口定义,不写实现,业务模块的命名对我后期来完善方法具体实现有指导价值。

④ 显得专业,不,是真的专业。