//Description:说些废话
唔... 我想吐槽一下.. 昨天看简单工厂模式的时候以为那个类多态就仅仅是多态的作用,我没想过那居然就是策略模式(虽然有那么点区别..)
然后特么我今天在Context类上钻了2小时的牛角尖,后来发现....GG....尼*炸了..炸了..炸了..
//部分资料来源:
1.C++实现的简单工厂模式示例来源:http://www.jellythink.com/archives/388
2.某些归纳话语:程杰——大话设计模式
//正文:
策略模式的使用场合——(摘自部分资料来源1那网址)
1.许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法;
2.需要使用一个算法的不同变体;
3.算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构;
4.一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以替代这些条件语句。(是不是和状态模式有点一样哦?)
说一下这个UML图吧。
这个Strategy就是一个父对象,有一个虚方法AlgorithmInterface()方法,用于派生给子类。
这就是所说的策略模式定义:定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。
从概念上看:所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。
对于上面这句话的理解,就是Strategy与其三个子类,还有三个子类相互之间实现了松耦合,三个子类之间可以相互替换。
那么我们试想一下,如果不用 策略模式/简单工厂模式 来实现上面这个东西的话会怎样?
那我们应该会在main函数里面写出这么些语句:
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
int chooseNum = 0;
cout<<"putIn 1/2/3"<<endl;
cin>>chooseNum;
switch(chooseNum)
{
case 1:
cout<<"I am StrategyA"<<endl;
break;
case 2:
cout<<"I am StrategyB"<<endl;
break;
case 3:
cout<<"I am StrategyC"<<endl;
break;
}
return a.exec();
}
有很多条件分支在主函数中是不是??那么我们是不是会想到用前面说过的简单工厂模式去封装一下?
那么我们封装一下
—————————————简单工厂模式 start————————————————————————————
为了方便理解,逐个贴吧,首先是一个纯虚类作为父类:
class Strategy
{
public:
virtual void AlgorithmInterface() = 0;
};
然后就是三个继承下来的子类:
class ConcreteStrategyA : public Strategy
{
public:
void AlgorithmInterface()
{
cout<<"I am StrategyA"<<endl;
}
};
class ConcreteStrategyB : public Strategy
{
public:
void AlgorithmInterface()
{
cout<<"I am StrategyB"<<endl;
}
};
class ConcreteStrategyC : public Strategy
{
public:
void AlgorithmInterface()
{
cout<<"I am StrategyC"<<endl;
}
};
然后就是工厂了:
class NormalFactory
{
public:
Strategy* createChoice(int chosenNum)
{
Strategy* tmp = nullptr;
switch (chosenNum)
{
case 1:
tmp = new ConcreteStrategyA();
break;
case 2:
tmp = new ConcreteStrategyB();
break;
case 3:
tmp = new ConcreteStrategyC();
break;
default:
break;
}
return tmp;
}
};
再然后就是主代码:
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
int chooseNum = 0;
cout<<"putIn 1/2/3"<<endl;
cin>>chooseNum;
Strategy* MyChosenStrategy = (new NormalFactory)->createChoice(chooseNum);
MyChosenStrategy->AlgorithmInterface();
return a.exec();
}
——————————————简单工厂模式 end ————————————————————————————————
那么我们来分析一下:
用了工厂模式,我们就不用再main函数里面写条件语句了,都交给了工厂NormalFactory去做,我们在main函数里面只需要调用工厂
那么工厂就会帮我们生成一个我们需要的对应的策略子类
但是,你注意到没有,我们暴露了两个类给用户:
1.Strategy
2.NormalFactory
我们并没成功地把Srtategy与客户端分离,这样做有什么不好的地方呢?
那就是:当我们需要增加一个策略的时候,我们就需要对工厂类NormalFactory进行维护和扩展
其实我们进行了简单工厂模式,三个ConcreteStrategy子类已经是策略模式的三个具体策略了。
那么我们应该想一种方法,可以令到在main函数里面看不到Strategy这个纯虚类,只需要关注我们要用的Factory类就行了,对吧。
————————————————简单工厂+策略 模式 start —————————————————————————————————
其实超简单,就把那个NormalFactory改一下就行(其实简单工厂那已经包含了一大部分的策略模式了)
NoramlFactory 改成 ContextFactory
class ContextFactory
{
public:
ContextFactory(int chosenNum)
{
switch(chosenNum)
{
case 1:
pStrategy = new ConcreteStrategyA();
break;
case 2:
pStrategy = new ConcreteStrategyB();
break;
case 3:
pStrategy = new ConcreteStrategyC();
break;
default:
break;
}
}
void ContextOutput()
{
if(pStrategy)
pStrategy->AlgorithmInterface();
}
private:
Strategy *pStrategy;
};
然后我们就可以屏蔽了Strategy类了,在main里面的两句改一下:
这两句:
Strategy* MyChosenStrategy = (new NormalFactory)->createChoice(chooseNum);
MyChosenStrategy->AlgorithmInterface();
改成:
ContextFactory *pContext = new ContextFactory(chooseNum);
pContext->ContextOutput();
就行了..
————————————————————简单工厂+策略 模式 end ————————————————————————————————
那么说到这里就说完了,下面贴一下简单工厂+策略模式的最终代码:
#include <iostream>
using namespace std;
class Strategy
{
public:
virtual void AlgorithmInterface() = 0;
};
class ConcreteStrategyA : public Strategy
{
public:
void AlgorithmInterface()
{
cout<<"I am StrategyA"<<endl;
}
};
class ConcreteStrategyB : public Strategy
{
public:
void AlgorithmInterface()
{
cout<<"I am StrategyB"<<endl;
}
};
class ConcreteStrategyC : public Strategy
{
public:
void AlgorithmInterface()
{
cout<<"I am StrategyC"<<endl;
}
};
class ContextFactory
{
public:
ContextFactory(int chosenNum)
{
switch(chosenNum)
{
case 1:
pStrategy = new ConcreteStrategyA();
break;
case 2:
pStrategy = new ConcreteStrategyB();
break;
case 3:
pStrategy = new ConcreteStrategyC();
break;
default:
break;
}
}
void ContextOutput()
{
if(pStrategy)
pStrategy->AlgorithmInterface();
}
private:
Strategy *pStrategy;
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
int chooseNum = 0;
cout<<"putIn 1/2/3"<<endl;
cin>>chooseNum;
ContextFactory *pContext = new ContextFactory(chooseNum);
pContext->ContextOutput();
return a.exec();
}
其实我想说,我用的是:简单工厂模式再用策略模式的方法,这种方法其实简单工厂那里已经包含了一部分的策略模式了,这也是我当初想了大半天,
都不知道究竟什么是策略模式的原因,还以为那只是简单的多态。。
所以如果要理解的更加清楚的话,我还是建议去看看刚刚部分资料来源1那个网址,那个网址是先实现策略模式,再加上简单工厂模式的,可能会好理解一点。
我是参考两个资料自己码的代码,不知道有没有什么不对的地方,有不对求指出 ♂
//那么我自己的问题来了:
1.
Q:为什么说:从概念上看,所有这些算法都完成相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合?
A:首先ConcreteStragegyA、B、C 三个类相互之间是都是完成输出一句话的工作,只是输出的话不同,他们都可以用父类Stragtegy来调用,三个算法子类之间为松耦合。
2.
Q:策略模式有什么优点?
A:(1)策略模式简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。
(2)策略模式把switch的行为都封装起来,消除了这些行为的类中消除条件语句(其实我觉得是简单工厂模式封装起来的,其实是Strategy类的行为可以消除这些语句,所谓的封装了变化。所以看部分资料来源1的那个策略模式的实现,你会看到选择的实现是在main里面的,这就是我说为什么那个更加好理解的原因。。)
3.
Q:通俗地说,什么时候用策略模式?
A: 只要分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。
更加通俗地说,就是你觉得几个子类都是重写父类的那个函数的时候,你就知道你想到的就是策略模式吧。
4.(这个是我要吐槽吐槽+吐槽的)
Q:为什么我老感觉策略模式就是多态?
A:网上搜了下答案:
策略模式强调的是做同一件事情的不同方法,这些方法不能重复,也就是正交
多态只不过是一种语言机智,有的不支持多态的语言一样也要实现策略
策略处于设计层次,多态处于语言的层次
哦... 好像明白了,这并不是一个层次的东西,网上说的差别很细微,现在刚进程序猿这个坑嘛,所以我还是要理直气壮地说..我特么觉得这策略模式就是多态..GG