冗长switch-ca语句优化⽅案
1、前⾔
在实际的编程中,我们经常会使⽤到a语句,这通常也是对⼀长串if..el if语句的优化。对于⼀些简单的情况(只每个ca 代码中代码长度不会很长,⽽且ca分之并不多的情况),⽤a语句即可,此时代码的可读性并不会很差,结构也算是清晰。但是⼀旦ca分⽀数⽬众多,每个ca语句块中代码长度也很长,这时对于维护这段代码的同学则是个噩梦了(本⼈就遇到过⼀段代
码,ca分⽀有近20个,每个ca语句块中,代码长度均有⼏⼗上百⾏,有的甚⾄有⼏百⾏之多。看到这段代码时,我也是醉了,果断对它进⾏重构)。
对于a语句有⼏种常⽤的⽅式,下⾯我⼀⼀为⼤家介绍。在正式介绍之前,我们优化的⽬标代码如下所⽰:
struct Param
{
...//待传⼊FuncToOptimal函数的参数郭宸
存款利率计算}
enum EDataType
{
Avg,
Min,
Max,
TI,
Tab ,
... //更多其他的枚举值
};
double FuncToOptimal(EDataType eDataTypeToCompute, const Param& inParam)
{
double dRetValue = 0;
... //局部变量定义
switch (eDataTypeToCompute)
{
ca Avg:
for (...)
{
do
{
... //(1)Avg情况下的计算内容
} while (...);
}
break;
ca Min:
for (...)
{
do
{
... //(2)Min情况下的计算内容
} while (...);
}
break;
ca Max:
for (...)
{
do
{
... //(3)Max情况下的计算内容
} while (...);
}
break;
ca TI:
for (...)
for (...)
{
do
{
... //(4)TI情况下的计算内容
} while (...);
}
break;
ca Tab:
for (...)
{
do
应收账款质押融资{
... //(5)Tab情况下的计算内容
} while (...);
}
break;
ca ...: //更多其他的ca
...
break;
default:
for (...)
{
do
{
... //(6)default情况下的计算内容
} while (...);
}
break;
}
return dRetValue;
}
君主专制制度
以上只是⽰例代码,在实际中有可能会⽐这个更为复杂,⼀个函数就有可能数千⾏。对于⼀个对于业务不熟悉的⼈,看到这样的代码⾃然是没有兴趣或者是没有勇⽓继续往下看的,因为这种代码的可读性是⾮常差的,后期维护成本会很⾼,在实际的⼯作中我们应该尽可能的避免这种代码的出现。对于以上⽰例代码,有以下三种优化⽅式:抽取法、继承与多态、跳表法,以下分别进⾏介绍:
2、抽取法
所谓抽取法,即我们将每个ca语句中的计算内容抽取为⼀个函数,然后在每个ca中只需要进⾏函数调⽤即可,如我们将“Avg”这条分之抽取为如下函数:
void ComputeAvg(const Param& inParam)
{
double dAvg = 0;
... //应⽤于Avg分之的局部变量
... //实际的计算⼯作
return dAvg;
}
然后在FuncToOptimal函数中,每个ca分⽀调⽤对应的函数即可,那么FuncToOptimal就简化为:
double FuncToOptimal(EDataType eDataTypeToCompute, const Param& inParam)
{
double dRetValue = 0;
switch (eDataTypeToCompute)
{
ca Avg:
dRetValue = ComputeAvg(inParam);
break;
ca Min:
dRetValue = ComputeMin(inParam);
break;
ca Max:
dRetValue = ComputeMax(inParam);
break;
ca TI:
dRetValue = ComputeTi(inParam);
break;
ca Tab:
dRetValue = ComputeTab(inParam);
break;
ca ...:
... //其他情况
break;
default:
dRetValue = ComputeDefault(inParam);
break;
}
return dRetValue;
}
这样最明显的好处就是,FuncToOptimal函数不再冗长,它函数的代码⾏数会⼤⼤缩减,使得代码的可读性增强。并且每个ca分之的实现都单独在⼀个接⼝中实现,这也更能满⾜⼀个函数只做⼀件事的原则。除此之外,这种⽅法还避免了在函数⼊⼝定义所有ca分⽀所需要的局部变量。
尽管如此,但是⽤这种⽅法重构之后,FuncToOptimal的函数实现仍然较长,尤其是当EDataType枚举有⼗⼏个甚⾄⼏⼗个枚举值的情况。另外,这个函数也违背了⼀个函数只做⼀件事的原则(严格来讲,有多少种ca分⽀,它就做了多少件事情)。
3、跳表法
经过抽取法优化之后,虽然函数的可读性增强了,但是函数依然违背了单⼀原则以及对修改封闭的原则,因为当增加新的枚举值时,我们仍然需要来对函数进⾏修改。
为继续修改代码,使之满⾜单⼀原则和对修改封闭原则,我们可以使⽤ 跳表法,它需要结合之前所讲的抽取法,⾸先将每个ca分⽀的实现抽取到单独的函数中,然后⽤⼀个数组来存储对应的ca分⽀实现的函数地址,这个数组即为跳表。如:
typedef double (*DataRetriver)(const Param& inParam); //定义函数指针
男子跳高世界纪录是多少DataRetirver dataRetriever[20] = {&ComputeAvg, &ComputeMin, ...}; //假设总共有20个分⽀
定义好跳表之后,对FuncToOptimal的优化如下:
double FuncToOptimal(EDataType eDataToCompute, const Param& inParam)
{
double dTabValue = 0;
DataRetriever * pRetriever = dataRetriever[eDataToCompute];
if(pRetriever != nullptr)
{
dTabValue = pRetriever(inParam);
}
return dTabValue;
}
经过优化之后,FuncToOptimal就同时满⾜了单⼀原则和对修改封闭原则。但是跳表法有⼏个明显的缺点: 1)跳表中,每⼀个位置的函数指针必须与枚举值严格对应;
2)当枚举值调整之后,跳表必须做相应的调整;
幼儿语言3)对于有些不⽤实现的ca分⽀,跳表中也必须要为其对应的枚举值留空位
4、继承与多态
所谓继承与多态,即利⽤C++中的多态性质。优化主要有以下⼏个步骤:
1)创建⼀个基类
class DataRetriever
{
public:学使
DataRetriver() = default;
~DataRetirver() = default;
double RetrieveData(const Param& inParam) = 0;
}
2)每⼀种需要计算的ca分⽀均创建⼀个类,继承⾄DataRetriever,如Avg分⽀:
class AvgRetriever : public DataRetirever
{
public:
AvgRetriever() = default;
~AvgRetriever() = default;
double RetrieveData(const Param& inParam);
}
3)实现RetrieveData接⼝,如Avg分⽀的实现:
double AvgRetirever::RetrieveData(const Param& inParam)
{
double dAvg = 0;
... //应⽤于Avg分之的局部变量
... //实际的计算⼯作
return dAvg;
}
4)创建⼀个⼯⼚⽅法(当然也可以作为DataRetriever的静态函数⽅法,此处仅为⽰例之⽤),根据不同的枚举值获取相应的对象
DataRetriever* GetRetriever(EDataType eDataTypeToCompute)
{
switch(eDataTypeToCompute)
{
ca Avg:
return new AvgRetriever();
ca ...:
return new ...; //其它情况
}
return nullptr;
}
注:返回值此处直接返回指针,仅仅为⽰例之⽤,实际⼯作中尽量使⽤智能指针
5)对FuncToOptimal进⾏优化,优化后函数实现变为:
double FuncToOptimal(EDataType eDataToCompute, const Param& inParam)
{
double dTabValue = 0;
DataRetriever * pRetriever = GetRetriever(eDataToCompute);
if(pRetriever != nullptr)
{
dTabValue = pRetriever->RetrieveData(inParam);
delete pRetriever;
pRetirever = nullptr;
}
return dTabValue;
}
经过以上5个步骤即可完成对FuncToOptimal函数的重构,重构后函数会缩短到⼏⾏。并且代码的结构清晰,可读性强。当任何⼀个分⽀的实现改变,或者新增了新的分⽀,我们都⽆需对FuncToOptimal进⾏修改,只需要修改对应分⽀的类或者为新的分⽀实现⼀个类,并在GetRetriever⼯⼚⽅法中增加
对新的分⽀的处理即可。
5、总结
在本为中,我们针对a语句的优化给出了以下3中解决⽅法:
1)抽取法
这种⽅法最为简单,它能够有效的减少代码⾏数,并增强代码可读性,降低维护成本。但这种⽅法后,函数仍然不符合单⼀原则以及对修改封闭原则,并且函数对修改不封闭。
2)跳表法
狐狸的歇后语这种⽅法是在抽取法之上对函数进⾏进⼀步的优化。它能够使得优化后的代码满⾜单⼀原则和对修改封闭原则,但它不够灵活。
3)继承与多态
这种⽅法是三种⽅法中,对函数优化最为彻底的⽅法,对于复杂的a语句,我们通常建议使⽤这种⽅法。
最后,并不是所有的a语句都需要优化,只有ca分⽀较多,且分⽀中处理⽐较复杂的情况才需要进⾏优化。在优化的时候,也不是⼀定要选择继承与多态的⽅法,这需要根据具体情况⽽定。
P.S. 以上为个⼈意见,如有纰漏,请您不吝赐教。