Django Web开发中控制层复杂的重构

Django是一个Python界的Web开发框架,符合MVC模式,只不过对应到Django是MTV(T为Template,即视图层,V为View,即控制层)。当使用类似vue框架来实现前端开发后,其实Web框架的视图层已经用不到了,只需要处理数据层和控制层。但往往随着业务的不断变化,业务功能上线时间紧迫,控制层容易变的越来越乱。下面介绍一些改进和避免复杂性的方法。

乱是怎么产生的?

我总结3点常见的原因:

抽象度不够

抽象是复用的基础,复用越多,代码量越少,代码也就越清晰。如果代码里充斥着 copy-paste 方式的复用,情况会在需求改动时变得糟糕,因为要改好多个地方,累且容易出错,但这也是很明显的重构信号。如果这时候还没有抽象,那么累的工作下次还需要做一遍。

代码冗长

假设控制层是基于CBV(Class-Base-View)来实现的,即每个Class的get、post方法处理对应的路由请求,把所有逻辑都写在get或post方法里,行数过百,代码很长。

代码冗长首先带来可读性差的问题。例如下面变量的含义得往上翻才能看出来,所以一般一个方法的行数不超过一个屏幕。另外代码冗长不容易看出此方法的整体逻辑的结构,容易被细节所迷惑。

代码实现不够简洁

这里指可能只需要5行代码就可实现的逻辑,写了10多行,虽然运行结果一样,但实现不优雅,难维护。通常会出现在有很多条件判断+循环的代码中。所以觉得代码复杂了,不清晰了,需要自己停下来思考。

一些优化方法

分解抽象

还以 CBV 模式举例,大概的代码结构如下:

Class RequestCheck(object):
    "请求参数校验"
    def is_valid(self, *args, **kwargs):
        pass
    
Class ResponseFormat(object):
    "响应码+数据格式化"
    def response(self, code, data={}, message):
        pass
    
Class Service(RequestCheck, ResponseFormat):
    "处理路由请求"
        
    def post(self, request, *args, **kwargs):
        # 参数验证,若有需要可以重写父类的 is_valid
        valid, argname = self.is_valid(args, kwargs)
        if not valid:
            self.response(1, message=f"参数{argname}错误")
            
        # 步骤1
        self.__action1()
        
        # 步骤2
        self.__action2()
        
        # 步骤3
        self.__action3()
        
        # 等等
        
        # 返回结果
        self.response(0, "xxxx")
        
    def __action1():
        pass
        
    def __action2():
        pass
        
    def __action3():
        pass

上面Service就是一个典型的接口路由处理类,参数校验和结果格式化继承通用的功能,若不满足可以重写方法。__action1、__action2、__action3 都是具体的业务逻辑代码,需要提前将业务逻辑的实现分为几个步骤,优化可读性的同时也帮助代码抽象。

上面的例子类似 Template design patten,针对复杂逻辑先定义好步骤然后对应实现。如果只有一个action,并且逻辑也简单,也可以直接写在 post 。但当发现逻辑不再简单或者逻辑内部有可复用的地方,需要及时抽象出独立方法。

action方法不一定是 Service 类的方法,也可以是调用其他Class的方法。

分层抽象

上面分解抽象做好后,Service 类相对就比较清晰了,但可能存在另一个问题,就是Service1 和 Service2 里的action功能类似,但却分布在不同的Service里,这时候就需要分层抽象,考虑从Service里抽象出相对稳定的逻辑实现,作为Service的下层,提供独立的服务。这样 MVC 里的控制层变成了2层。

这种方法可以继续使用,抽象出更稳定的独立服务作为第3层,形成倒金字塔型的依赖关系。上层依赖下层,减少同层之间的依赖,同层之间依赖太多也是乱的原因之一。

什么时候用继承来实现代码复用

一般有如下几种情况使用继承,其他情况选择调用、组合的方式来复用。
1、子类与父类有“是”的关系,例如毛笔是笔,钢笔是笔。
2、需要父类的全部功能;
3、除了使用父类的某些功能外还可能重写父类的方法;
4、实现多态的特性,即调用者只拿父类调用,不关心他实际是不是某个子类。

要不要对 ORM 层封装

如果只是对一个Model进行读写,没有其他业务逻辑,我觉得没必要封装。因为对于调用者来说没有更优。只是把 ModelName.objects.filter 换成了 query_obj 的名字而已,调用者还是需要对结果判断是否为空来继续后续逻辑。

如果对Model的读写包含了业务逻辑,或者是同时对多个Model进行读写,这就构成了一个功能了,有复用可能,这时候才有封装的必要。

代码扩展性

代码扩展性指应对需求变化的能力,当有新的需求出现时,系统不需要或者仅需要少量修改就可以支持。

提升可扩展性的一般方法:分层 + 设计模式。

分层通常有两种:一种是将变化封装在一个"变化层",将不变的部分封装在一个独立的"稳定层"。另一种是提炼一个"抽象层"和一个"实现层"。后一种在设计模式中经常见到。

Python中设计模式用的没Java重,因为Python的语言特性已经支持一些设计模式的效果(比如单例模式),而且Python语言讲究简单直接,不像Java弄很多抽象层。但设计模式相当于套路,掌握一些通用的套路以及对应解决的问题,能够提高写代码的敏感力,能及时发现需要重构的地方。

但太强调扩展性也不好,如果预测变化不准确可能导致过度设计,那种变化可能永远不会到来。过度设计的代码层次特别多,可读性差。所以扩展性适度就好,何为适度,就是在不影响整体架构和开发速度的情况下,方便就做下扩展,不方便就使用最简单的方式实现,后面如果有变化再根据变化持续迭代重构。

你可能感兴趣的:(Django Web开发中控制层复杂的重构)