优美代码-使用冗余字段处理问题

Jan 24, 2019 00:00 · 1162 words · 3 minute read 程序设计

最近在读 Andrew Hunt 与 David Thomas 的经典著作 <<程序员修炼之道>> 其中有一节在阐述程序设计中”知识”重复的危害(这里的知识是广义的, 对程序来说就是一些功能的代码), 文中提到的内容和我之前重构一个项目时遇到的问题有相似之处, 在此记录下来.

项目是一个为了促进用户消费的和粘稠度优惠券系统(商家/平台策划活动, 用户参与活动后获得优惠券并在以后的消费中使用优惠券…), 这个系统中有一个表是用来保存活动计划, 简化后的结构如下:

class ActivityPlan(BaseModel):
    name, # 活动计划名称

    approve,        # 运营总监是否同意发布这个活动 (0: 等待审批 1: 通过, -1: 未通过)
    isPublished,    # 活动是否已发布 (1: 是, 0: 未发布)
    endDate,        # 活动结束时间
    ....

为了方便运营直观的了解活动计划的状态, 在后台管理的界面要展示每一个活动的状态[‘待审批’, ‘审批未通过’, ‘等待发布’, ‘活动进行中’, ‘活动已结束’….]

前端拿到活动计划后需要按照与后端的约定判断逐次判断”approve”, “isPublish”, “endDate”… 这些字段的值, 最后计算出最终的状态, 如果一但业务有新的变化, 无疑前后端都要重新制定新的规则并更改对应的代码, 这其实就是后端的”知识”在前端的重复

一次优化

class ActivityPlan(BaseModel):
    name, # 活动计划名称

    approve,        # 运营总监是否同意发布这个活动 (0: 等待审批 1: 通过, -1: 未通过)
    isPublished,    # 活动是否已发布 (1: 是, 0: 未发布)
    endDate,        # 活动结束时间
    ....

    state,          # 状态

前端不再需要了解后端如何判断活动计划的状态, 只需要知道每一个state对应的状态是什么即可, 但对于后端来说又有了新的问题—我们需要在过去代码中所有更改”approve”, “isPublish”… 的地方添加state字段的更新, 而且如果活动已经进入发布状态, 还需要在每次返回前端前对当前时间与endDate进行比较以便确认活动是否已经结束.

再一次优化

class ActivityPlan(BaseModel):
    name, # 活动计划名称

    approve,        # 运营总监是否同意发布这个活动 (0: 等待审批 1: 通过, -1: 未通过)
    isPublished,    # 活动是否已发布 (1: 是, 0: 未发布)
    endDate,        # 活动结束时间
    ....

    @property
    def state(self):          # 状态
        if self.approve == 0:
            return 1001     # 待审批
        elif self.approve == -2:
            return 1002     # 审批未通过
        ....

后端只在类ActivityPlan中处理state, 而无需再其它地方处理

总结

原始代码

后端代码是简洁的, 但存在前后端”知识”(状态的判断逻辑)上的重复

第一次的改进

解决了前后端”知识”上的重复问题, 但使得后端出现状态上的重复(可以认为 state 是其它几个状态字段的冗余(由其它字段计算得来)), 同时为了保持state的正确性, 需要在代码的许多地方维护state

的二次的改进

将维护state的操作局部化到类的方法中, 而不暴露到整个后端代码中