0%

如何写出更好的程序二:尽可能减少代码的修改

职场新人兼新手程序员斗胆开了新坑「如何写出更好的程序」,所见所得都是来自实际写代码时自己的思考,且已脱敏。这一系列不包含任何复杂的技术,也不包含任何难懂的代码。只是将核心问题暴露出来,针对这些场景,如何写出可维护性更高、更简洁优雅的代码。

目前仅包括 python 装饰器的使用,等某天遇到其他技术也可以减少代码的修改时,会追加到本文。

使用 Python 装饰器

背景

一开始写代码的时候,都在想着要尽可能的支持全部功能,要获取各种信息并反馈给用户。于是我写了一大堆代码,创建了各种类、各种数据结构,以及实现了各种方法。

1
2
3
4
5
6
7
8
9
10
class A:
def func1(): ...
def func2(): ...
def func3(): ...


class B:
def func4(): ...
def func5(): ...
def func6(): ...

为了高效的获取信息,一些数据可以复用,一些逻辑可以跳过,这样写出来的代码也会错综复杂:

1
2
3
4
5
6
7
8
9
10
11
def main():
a = A()
a.func1()
b = B()

val = some_func()

if val < 100:
a.func3()
else:
b.func4()

某天忽然遇到一个新需求:需要增加一个轻量版的代码,只得到 3 个核心信息就好了,其他信息直接忽略掉。这时我回首我的代码发现:为了得到各种信息,之前的代码十分庞大,有很多类,也有很多方法,复杂的逻辑修改起来并不是件很容易的事。

  • 为了实现轻量版的代码,重写代码肯定是不值得的,毕竟一些代码逻辑和数据结构可以复用。重写代码势必会导致代码文件增加,冗余代码增多。
  • 如果复用代码,会发现这个类可以不用创建,这个逻辑可以跳过,一些类的成员方法可以不用执行。

坏代码

如果在代码中手动添加 lite 这一轻量化参数,遇到不需要执行的代码就根据 liteif else 分支给代码加岔路口,代码结构会十分繁杂。比如有 lite 选项时,我们需要创建 A 这个类,根据临时结果判断是否需要执行 b.func4(),那么上述代码修改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
def main():
lite = True
a = A()
if not lite:
a.func1()
b = B()

val = some_func()

if val < 100 and lite:
a.func3()
else:
b.func4()

对于 1000 多行更加复杂的代码,手动添加 lite 分支并修改逻辑,这是很累的工作,写出来的代码也不好看,通用性也随之变差。

装饰器优化

此时我们可以使用装饰器来完成这一工作,如果不知道装饰器是什么东西可以参考我之前的文章。在装饰器中首先传入 self 参数,如果检测到类的 lite 属性为 true,直接跳过这一函数不执行。此时我们只需要打开需要改动的类,增加 lite 属性。

如果确定这个方法可以不执行,给方法增加装饰器即可。而对于 main 函数中的代码,是不需要任何修改的,也不需要增加大量的 if else 分支,减少代码结构的修改和破坏。逻辑处理部分的代码如下所示,相比坏代码部分精简了很多,且 a.func1a.func3 都是不会执行的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def use_lite(func):
def wrapper(self, *args, **kwargs):
if self.is_lite:
pass
else:
return func(self, *args, **kwargs)
return wrapper

class A:
def __init__(self, lite=False):
self.lite = True
@use_lite
def func1(): ...


def main():
a = A(True)
a.func1()
b = B()

val = some_func()

if val < 100:
a.func3()
else:
b.func4()

补充:@use_lite(self.lite) 是会报错的,因为装饰器是外部方法,并不是类的成员,也就无法捕捉类对象。

感谢上学期间打赏我的朋友们。赛博乞讨:我,秦始皇,打钱。

欢迎订阅我的文章