先说说题外话。公司的代码就像老而陈旧的破船,更新迭代过程中上面打满的补丁,并且还捕捞了一只巨大的鲸鱼。
每当在想去重新改建一下,让这艘船更加健壮,就会有新的需求过来,让原本已经破败的小船更加不堪。周而复始,
每当新的需求来的时候,都像一次重建,工程浩大,然而收结果微乎其微,还会引起其他问题。
这就是代码在创建初期就没有考虑其复用性和可拓展性。题外话说完。
之前我们谈到过复用的问题 ,但是真正意义上的复用并不是指代码的复制和粘贴片段级别的复用。
公司的代码全是这个级别的,通俗来说真正意义复用是编译单位的复用,就是指二进制级别的复用 ,
比如你添加一段代码,这段代码不会影响之前编译的后的二进制文件,作为库的形式添加到程序当中依然可以使用。
在上一次学习过程当中知道了什么是组件协作,这次学习的模式就是当中的策略模式:
动机:在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都去编码到对象当中,
将会使对象变得异常复杂,而且有时候支持不使用的算法也是一种性能负担。
下面开始用伪代码来理解这些内容:
假设当前有个需求就是计算 美国、中国、德国汇率。
结构化编程的思维伪代码1:
enum TaxBase{
CN_Tax,//中国
US_Tax,//美国
DE_Tax //德国
};
class SalseOrder{
TaxBase tax;
public:
double CalculateTax(){
//...
if(tax ==CN_Tax){
// 计算的算法
}else if(tax ==US_Tax){
}else if(tax ==DE_Tax){
}
}
};
面向对象 策略模式伪代码2:
class TaxStartegy{
public:
virtual double Calulate(const Context &context)=0;
virtual ~TaxStartegy();
};
class CNTax :public TaxStartegy{
public:
double Calulate(const Context &context){
//计算算法
}
};
class USTax : public TaxStartegy{
public:
double Calulate(const Context &context){
//计算算法
}
};
class DETax :public TaxStartegy{
public:
double Calulate(const Context &context){
//计算算法
}
};
class SalseOrder{
TaxStartegy *strategy;
public:
SalseOrder(StrategyFectory *strategyFectory){ //使用后面所学的工厂模式 这里跳过
this->strategy = strategyFectory->newStrategy();//生产一个堆空间对象
}
~SalseOrder(){
delete strategy;
}
public:
double CalculateTax(){
Context context();
double val = this->strategy->Calulate(context); // 稳定中的变化 多态
//....
}
};
简单来说2段代码的不同就是将一个一个的算法变成了一个一个的子类
设计代码和写代码的时候不单单是局部去考虑,需要一个纵向的思维。假设我们加上时间轴后,由于时间的关系
当前的功能已经不能顺应当前的需求,也就是添加新的需求,如何去应对这个变化能 。
假设我们当前的需求就是在添加一个法国的汇率的计算。
第一段结构话设计要怎么做呢?
(其实这就是我现在最纠结的,刚刚题外话说到的我的痛苦)
首先在 枚举类型里面增加一个FR_Tax
然后在if 条件 当中在添加 FR_Tax的判断。 其实目前项目的代码都是这个改过来了,看似没有动前面代码只是添加
一个业务逻辑,然后现实并没有像想那么简单,不说一定会影响前面的代码,但是一定会重新去编译现在原有的代码
而且破会当前的代码片段,违背了开闭原则。
第二段面向对象的设计怎么做呢?
我们只需要添加一个子类
class FRTax :public TaxStartegy{//扩展
public:
double Calulate(const Context &context){
//计算算法
}
};
Strategy及其子类为组件提供了一系列可重用的算法,从而可以使得类型在运行时方便的根据需要在各个算法之前进行切换。
不用去改变当前算法组织的结构,只要进行工厂的适配就好,多态就会自动找到我们想要的子类方法去调用。(通俗说)
大部分的条件判断都可以使用策略模式,也是一种消除耦合的方式。
回头我们在理解一下刚刚“动机”的最后一句话 关于性能的问题,程序运行是这些代码是划分到代码段的 ,
然后到缓存catch中加载,缓存满了 就会在内存当中,内存满了就会加载到虚拟内存当中 虚拟内存就是硬盘
一旦我们的代码过于臃肿而且有很多我们不经常使用的算法 比方第一段代码的if 我只能使用一个 其他的就是没有用的,
当我们使用策略模式后 是不是发现算法的选择是在运行时去选择的。大大减小了程序运行的开销。
GOF中 策略模式的定义;
定义一系列的算法,把他们封装起来,并且使他们可以互相替换(变化)。
该模式是的算法可以独立与使用它的客户程序(稳定)而变化(扩展,子类化)。
GOF 的类图
第二段代码套入这个类图 。
不难看出上面是稳定的(抽象接口) 下面的子类都是变化的 。