Skip to content

代码优化-使用反射减少重复代码

优化前代码:

python
    res = Table6Class()
    res.name = "合计"
    res.c = table_1.c + table_2.c
    res.si = table_1.si + table_2.si
    res.mn = table_1.mn + table_2.mn
    res.p = table_1.p + table_2.p
    res.s = table_1.s + table_2.s
    res.fe = table_1.fe + table_2.fe

简简单单一段赋值代码,只是给table_1​和table_2​的各个属性值求和

这种代码我们可以用反射来简化

我们将需要合计的属性保存起来(如果都需要合计就直接遍历属性列表),写一个常量

python
C_SI_MN_P_S_FE_LOW = ["c", "si", "mn", "p", "s", "fe"]
TABLE_HEADER_HEJI = "合计"

然后赋值操作

python
    """合计"""
    res = Table6Class()
    res.name = TABLE_HEADER_HEJI
    for ele in C_SI_MN_P_S_FE_LOW:
        setattr(res, ele, getattr(table_1, ele) + getattr(table_2, ele))

这里我用的python代码做的示例,大部分语言都有反射的对应方法

同理,下边同样是使用反射

python
    res = Table6Class()
    res.name = "表2"
    res.c = (153 * table_1.c * 0.001).quantize(Decimal("0.000"), rounding=ROUND_HALF_UP)
    res.si = (153 * table_1.si * 0.001).quantize(Decimal("0.000"), rounding=ROUND_HALF_UP)
    res.mn = (153 * table_1.mn * 0.001).quantize(Decimal("0.000"), rounding=ROUND_HALF_UP)
    res.p = (153 * table_1.p * 0.001).quantize(Decimal("0.000"), rounding=ROUND_HALF_UP)
    res.s = (153 * table_1.s * 0.001).quantize(Decimal("0.000"), rounding=ROUND_HALF_UP)
    res.fe = (153 * table_1.fe * 0.001).quantize(Decimal("0.000"), rounding=ROUND_HALF_UP)

这段代码中,table_1中的每个属性都要乘一个常量,再保留三位小数

保留三位小数的方法

python
def round_decimal(value):
    return value.quantize(Decimal("0.000"), rounding=ROUND_HALF_UP)

懒得贴常量类的代码了,就写魔数吧,领会精神!

python
res = Table6Class()
res.name = TABLE_HEADER_TIESHUI
for ele in C_SI_MN_P_S_FE_LOW:
    setattr(
        res,
        ele,
        round_decimal(
            153 * getattr(iron_table2, ele) * 0.0001
        ),
    )
return res

优化代码结构

接下来看这个方法

python
def getTable(ee, ss, tt, last_step_data, flag):
    if flag == 1:
        table = Table7Class()
    elif flag == 2:
        table = Table8Class()
    table.ee = ee
    table.ss = ss
    if ee == "C":
        if ss == "C + 0.5*O2 == CO":
            if flag == 1:
                table.deplete_1 = (
                    last_step_data.c * tt.a1 * PERCENTAGE_TO_DECIMAL
                ).quantize(Decimal("0.000"))
            elif flag == 2:
                table.deplete_2 = (
                    last_step_data.c * tt.a1 * PERCENTAGE_TO_DECIMAL
                ).quantize(Decimal("0.000"))
        elif ss == "C + O2 == CO2":
            if flag == 1:
                table.deplete_1 = (
                    last_step_data.c * tt.a2 * PERCENTAGE_TO_DECIMAL
                ).quantize(Decimal("0.000"))
            elif flag == 2:
                table.deplete_2 = (
                    last_step_data.c * tt.a2 * PERCENTAGE_TO_DECIMAL
                ).quantize(Decimal("0.000"))
    elif ee == "Si":
        if flag == 1:
            table.deplete_1 = last_step_data.si
        elif flag == 2:
            table.deplete_2 = last_step_data.si
    elif ee == "P":
        if flag == 1:
            table.deplete_1 = last_step_data.p
        elif flag == 2:
            table.deplete_2 = last_step_data.p
    elif ee == "S":
        if flag == 1:
            table.deplete_1 = last_step_data.s
        elif flag == 2:
            if ss == "S + O2 == SO2":
                table.deplete_2 = last_step_data.s * 1 / 3
            elif ss == "S + CaO == CaS + O":
                table.deplete_2 = last_step_data.s * 2 / 3
    elif ee == "Mn":
        if flag == 1:
            table.deplete_1 = last_step_data.mn
        elif flag == 2:
            table.deplete_2 = last_step_data.mn
    if flag == 1:
        table.xxx = ...
        table.yyy = ...
    elif flag == 2:
        table.zzz = ...
    return table

第一次看这段代码,给我看乐了,可是当事人觉得这样可读性高,对自己代码非常自豪。。。

我只希望如果代码有问题的话,不会让我来修改~

接下来我们使用策略模式优化他!

if-else​拆掉!统统拆掉!!!

常量:

python
C = "C"
C_REACTIVE_1 = "C + 0.5*O2 == CO"
C_REACTIVE_2 = "C + O2 == CO2"
C_REACTIVE_3 = "CO + 0.5*O2 == CO2"

SI = "Si"
SI_REACTIVE_1 = "Si + O2 == SiO2"

MN = "Mn"
MN_REACTIVE_1 = "Mn + 0.5*O2 == MnO"

P = "P"
P_REACTIVE_1 = "2P + 2.5*O2 == P2O5"

S = "S"
S_REACTIVE_1 = "S + O2 == SO2"
S_REACTIVE_2 = "S + CaO == CaS + O"

策略模式处理下,写个map做方法映射,属性要直接赋值的写成一个方法,并将属性名作为参数传递给方法,其他需要计算处理的单独写出方法

python
cal_map = {FLAG1: cal_deplete_1, FLAG2: cal_deplete_2}
# 用元素和反应作为唯一键,方法作为值
ee_ss_deplete_1_map = {
    (C, C_REACTIVE_1): cal_c_r1_bl,
    (C, C_REACTIVE_2): cal_c_r2_bl,
    (SI, SI_REACTIVE_1): (cal_straight_bl, SI_LOW),
    (MN, MN_REACTIVE_1): (cal_straight_bl, MN_LOW),
    (P, P_REACTIVE_1): (cal_straight_bl, P_LOW),
    (S, S_REACTIVE_1): (cal_straight_bl, S_LOW),
    (S, S_REACTIVE_2): (cal_straight_bl, S_LOW)
}
ee_ss_deplete_2_map = {
    (C, C_REACTIVE_1): cal_c_r1_ox,
    (C, C_REACTIVE_2): cal_c_r2_ox,
    (SI, SI_REACTIVE_1): (cal_straight_ox, SI_LOW),
    (MN, MN_REACTIVE_1): (cal_straight_ox, MN_LOW),
    (P, P_REACTIVE_1): (cal_straight_ox, P_LOW),
    (S, S_REACTIVE_1): cal_s_r1_ox,
    (S, S_REACTIVE_2): cal_s_r2_ox,
}


def get_ox_bl(table, ss=None):
    table.xxx = ...
    table.yyy = ...
    return table


def get_ox_ox(table, ss=None):
    table.zzz = ...
    return table


def cal_c_r1_bl(table, last_step_data, tt):
    table.deplete_1 = round_decimal(last_step_data.c * tt.a1 * PERCENTAGE_TO_DECIMAL)
    table = get_ox_bl(table)
    return table


def cal_c_r2_bl(table, last_step_data, tt):
    table.deplete_1 = round_decimal(last_step_data.c * tt.a2 * PERCENTAGE_TO_DECIMAL)
    table = get_ox_bl(table)
    return table


def cal_straight_bl(table, last_step_data, tt, attr_name):
    table.deplete_1 = getattr(last_step_data, attr_name)
    table = get_ox_bl(table)
    return table


def cal_c_r1_ox(table, last_step_data, tt):
    table.deplete_2 = round_decimal(
        last_step_data.c * tt.a1 * PERCENTAGE_TO_DECIMAL
    )
    table = get_ox_ox(table)
    return table


def cal_c_r2_ox(table, last_step_data, tt):
    table.deplete_2 = round_decimal(
        last_step_data.c * tt.a2 * PERCENTAGE_TO_DECIMAL
    )
    table = get_ox_ox(table)
    return table


def cal_straight_ox(table, last_step_data, tt, attr_name):
    table.deplete_2 = getattr(last_step_data, attr_name)
    table = get_ox_ox(table)
    return table


def cal_s_r1_ox(table, last_step_data, tt):
    table.deplete_2 = round_decimal(last_step_data.s * 1 / 3)
    table = get_ox_ox(table)
    return table


def cal_s_r2_ox(table, last_step_data, tt):
    table.deplete_2 = round_decimal(last_step_data.s * 2 / 3)
    table = get_ox_ox(table)
    return table


def cal_deplete_1(_tuple, last_step_data, tt):
    table = Table7Class()
    table.ee = _tuple[0]
    table.ss = _tuple[1]
    method_tuple = ee_ss_deplete_1_map[_tuple]
    if isinstance(method_tuple, tuple):
        table = method_tuple[0](table, last_step_data, tt, method_tuple[1])
    else:
        table = method_tuple(table, last_step_data, tt)
    return table


def cal_deplete_2(_tuple, last_step_data, tt):
    table = Table8Class()
    table.ee = _tuple[0]
    table.ss = _tuple[1]
    method_tuple = ee_ss_deplete_2_map[_tuple]
    if isinstance(method_tuple, tuple):
        table = method_tuple[0](table, last_step_data, tt, method_tuple[1])
    else:
        table = method_tuple(table, last_step_data, tt)
    return table


def getTable(tuple, last_step_data, tt, flag):
    return cal_map[flag](tuple, last_step_data, tt)

解决了if-else​过多的问题,另代码更加易于维护

这里每个元素和反应对应的方法都是写死的,而实际上我们应该追求在特定情况下执行某一个确定性的策略。也许业务上可以通过反应和元素判断反应量多少等条件来决定计算方式,暂时没有对业务理解透彻,只能相对写死一点,这样优化后也方便了解业务后对其进行改进。