【求助】关于Design Pattern概念及应用的理解
———————————————————————————————————————————————
最近在看有关Design Pattern (C++),在看到Strategy pattern时,我的理解是如果进行某种运算,而这种运算有多种不同的算法实现时,就可以用这种模式,比如解线性方程组Ax=b, 在给定A和b的情况下,可以有多种不同的解法:高斯消去,高斯列主元消去,全主元消去,。。。等。这时我们就可以设计一个基类,其中定义一个虚函数,比如solve(),然后从这个基类继承并实现solve()函数。在使用这些类时,可以通过使用指向相应算法的对象的指针。
不过,我看到,在很多讲Design pattern的资料里在提到Strategy pattern时,都要提到Factory method pattern,说是用Factory method来实现对strategy的初始化。这点我感觉比较难理解,我的问题是:
1。我上面对于Strategy pattern的理解对吗?
2。怎样理解:“Factory method lets a class defer instantiation to subclasses." ... "Factory method extends the initialization of strategy transparently." "Adding new solutions does not affect existing code"
3。大家在实际应用中的经验是怎样的?
请大家不吝赐教。谢谢!
———————————————————————————————————————————————
1.你对strategy的理解差不多,另外可以去看一下template pattern, strategy和tmplate的目的差不多,但是表现形式有差异。
2.你可以用factory来产生具体的strategy类。
———————————————————————————————————————————————
先谢谢回复。
不过我觉得较难理解用Factory来产生具体的strategy类。通常我觉得产生具体的stragegy类一般都是根据一些条件,用if...elseif...elseif...就可以完成。比如像我上面提到的那个例子。我可以设计:
class B{
public:
...
virtual solve()=0;
};
class D1 : public B{
public:
...
solve() { //高斯消去法}
};
class D2 : public B{
public:
...
solve() { //列主元消去法 }
};
然后在具体应用时
int main(int argc, char** argv)
{
B* pMySolver = NULL;
if (用户选择高斯消去法)
pMySolver = new D1;
elseif (用户选择列主元)
pMySolver = new D2;
elseif ...
pMySolver->solve();
delete pMySolver;
return 0;
}
请问这种做法和Factory方法相比,有什么缺陷吗?或者Factory方法有什么优越性吗?谢谢!
利用Factory来产生Concrete的strategy的class, 实际上是有一个的"封装". 把通过if..else, 或switch...case的东西, 都隐藏在某一个接口的后面. 其实OO的,目的也就是要把具体的实现和最终用户的使用隔离开, 这样才能实现代码的重用, 当然前提是遵守统一的接口. 这样只要提供interface就行了, 具体的实现是可以变化的, 不用影响到别人(类)的使用.
就你的例子而言, 一肿可能的Factory就是.
代码:
class Factory
{
public:
static b* Create(int i);
};
Factory::Create(int i)
{
switch (i)
{
case 0: //高斯消去法
return new D1;
case 1: //列主元消去法
return new D2
default:
return nil;
}
}
int main(int argc, char** argv)
{
B* pMySolver = NULL;
int i; //用户选择高斯消去法
...
pMySolver = Factory::Create(i);
if (pMySolver != nil)
{
pMySolver->solve();
delete pMySolver;
return 0;
}
else
return -1;
}
再次的封装, 也使得程序结构显的整洁一些. 开销是: 额外的class; 可能使应用变得不直接一些. 不过应该都是可以接受的.
———————————————————————————————————————————————
A related question: should we try as much as possible to avoid the type code and those "if-then-else" or "switch" statements. Does it make the different strategies couple too tightly with each other?
引用 XX 发表的贴子:
A related question: should we try as much as possible to avoid the type code and those "if-then-else" or "switch" statements. Does it make the different strategies couple too tightly with each other?
Right, sort of.
I guess the point is still not on avoiding the type code and those "if-then-else" or "switch" statements. From the users side, it doesn't matter which methods you use, they don't care how but what to do. So by using Factory pattern, you hide all the detail implementation info behind a neat interface, if applicable. You may change your mind later by using different mechanism to capture the type infor, or the user's choice. It won't affect other codes if the interface is maintained.
———————————————————————————————————————————————
引用 XX发表的贴子:
利用Factory来产生Concrete的strategy的class, 实际上是有一个的"封装". 把通过if..else, 或switch...case的东西, 都隐藏在某一个接口的后面. 其实OO的,目的也就是要把具体的实现和最终用户的使用隔离开, 这样才能实现代码的重用, 当然前提是遵守统一的接口. 这样只要提供interface就行了, 具体的实现是可以变化的, 不用影响到别人(类)的使用.
就你的例子而言, 一肿可能的Factory就是.
代码:
class Factory
{
public:
static b* Create(int i);
};
Factory::Create(int i)
{
switch (i)
{
case 0: //高斯消去法
return new D1;
case 1: //列主元消去法
return new D2
default:
return nil;
}
}
int main(int argc, char** argv)
{
B* pMySolver = NULL;
int i; //用户选择高斯消去法
...
pMySolver = Factory::Create(i);
if (pMySolver != nil)
{
pMySolver->solve();
delete pMySolver;
return 0;
}
else
return -1;
}
再次的封装, 也使得程序结构显的整洁一些. 开销是: 额外的class; 可能使应用变得不直接一些. 不过应该都是可以接受的.
———————————————————————————————————————————————
这种做法不是很好的.一般而言,使用Factory模式就是为了加强程序的灵活性..如果用上面的方法,那Factory的灵活性完全没有得到体现...
比较好的做法应该是这样的:
class a{}
class b:a{}
class c:b{};
class Factory
{
public:
virtual a* Create() ;
static a* GetInstanse(Factory* fac){
return fac->Create();
}
};
class BFactory:Factory{
public:
a* Create(){
return new b();
}
class CFactory:Factory{
public:
a* Create(){
return new c();
}
int main(int argc, char** argv)
{
B* pMySolver = NULL;
...
//根据用户的选择,动态生成相应的Factory了类..
pMySolver = Factory::GetInstanse(new XFactory());
if (pMySolver != nil)
{
pMySolver->solve();
delete pMySolver;
return 0;
}
else
return -1;
}
———————————————————————————————————————————————
我的理解是,结合Factory method pattern 和Strategy pattern, 就可以使得Concrete strategy的instantiation和使用相对于client(在这个例子里就是main())完全独立和透明,以后要使扩展增加新的concrete strategy,也就是设计一个新的strategy类,同时在Factory pattern地实现里增加对新的strategy的实例化。而这时main()函数作为client,可以不用改动。
可是你给的这个例子,对于每个strategy有一个factory,以后再增加新的strategy事后需要改动三个地方:新的strategy, 相应的factory, 以及main()。这样是不是失去了使用Factory method pattern的原意?我觉得倒是asap给的例子能够体现使用Factory的好处,不知我的理解对不对。
———————————————————————————————————————————————
是这样的,如果你用楼上的那种方法,那么当你的算法种类变了的时候,你的Factory类中也得相应的变化..对不对?得去修改那个if/else语句...
这样的一个不好的地方就是,如果你把这个函数给别人使用,而且是封装成Dll的形式,那么当你修改的时候,那个Dll也就要相应的修改,别人使用的时候也得进行修改,这不一定很方便(不是所有的人都想去更新的)..
或是当那个人想自己扩展算法的时候,楼上的方案那就完全改不了.为什么?因为判断的部分都在那个Dll中,修改不了.又没有源代码....
如果按我的方法,当要自己去扩展的时候,只要从Factory类派生自己的Factory,从A派生自己的D(算法)类,函数的别的部分都可以保持不变,一样可以完成工作~
其实像这个东东完全可以不用Factory的,使用Strategy本身就可以完成了.
如:
//算法的基类,所有的算法类都从这个类派生.
class A{
public :
virtual void Calcu(...) = 0;//用来计算的方法...
}
class MyStrategy{
public :
static void Run(A* a,...){
a->Calcu(...);
}
class B:A{
void Calcu(...);
}
class C:A{
void Calcu(...);
}
void main(){
A* a = new B();
MyStrategy::Run(a,...);
}
使用这种方法,把最后具体实现的细节交给用户来处理,可以获得更多的灵活性...
用户自己的算法,只要把Main()中的new B() 换成自己的类就OK了~(前提,从A类派生)
———————————————————————————————————————————————
引用 XX 发表的贴子:
是这样的,如果你用楼上的那种方法,那么当你的算法种类变了的时候,你的Factory类中也得相应的变化..对不对?得去修改那个if/else语句...
这样的一个不好的地方就是,如果你把这个函数给别人使用,而且是封装成Dll的形式,那么当你修改的时候,那个Dll也就要相应的修改,别人使用的时候也得进行修改,这不一定很方便(不是所有的人都想去更新的)..
或是当那个人想自己扩展算法的时候,楼上的方案那就完全改不了.为什么?因为判断的部分都在那个Dll中,修改不了.又没有源代码....
这个我倒是没有考虑到。
引用 XX发表的贴子:
...
其实像这个东东完全可以不用Factory的,使用Strategy本身就可以完成了.
...
这正是我发贴向各位请教的问题,为什么人们在讲到用strategy pattern的时候,都说要和Factory method pattern结合起来用,主要是由Factory来产生不同算法的实例。就拿线性方程组这个例子来说,对于我的问题二里那三句英文的理解,不是从字句表面上,而是从OO方法论的角度。
引用 XX 发表的贴子:
...
使用这种方法,把最后具体实现的细节交给用户来处理,可以获得更多的灵活性...
用户自己的算法,只要把Main()中的new B() 换成自己的类就OK了~(前提,从A类派生)
如果对具体的算法选择,需要在runtime才来决定,比如在用户界面上有个地方软件用户可以选择使用那种解法来解一个线性方程组,还能用你说的这种方法吗?我想,这时我们只能用那些if...then, 或switch...case,或者Factory方法。我说的对吗?
———————————————————————————————————————————————
主要是这样子的,你的这个题目是比较的Easy的,但是如果是一个比较大的系统,里面有很多个Strategy,而且这些Strategy得同时进行初始化,那些如果还是像我上面说的那样就完全不行,太不安全了,万一用户使用的时候露了一个呢了?
所以这个时候就得使用Factory模式了...
不管怎么样,在选择的时候都会用到if/else的语句,如果用不上,那这些语句早就不要了,对不对.它的存在是必要的... 只是在设计的时候,要考虑究竟把它放到那个地方?像我上面说的,把这些东西放到最后的Runtime时,用户的手中进行选择,选择完成后再生成不同的算法的类,最后再去执行..通过这种方式可以增加这个系统的灵活度和可扩展性...
btw,Design Partern中的东西是人们总结出来的模式,在具体使用的时候不一定拘限于某种特定的模式,而是一些模式的组合..而这些组合也不是一成不变的,也是灵活变通的..
具体问题,具体分析...
当你看书看得多了,了解的多了,程序写的多了,想的多了的时候你就会觉得,,模式也没什么了不起的.就那样,该咋用咋用:P
———————————————————————————————————————————————
1. 诚然Strategy不必一定要和Factory挂钩的, 尤其对比较简单的应用.
2. Abstract Factory的出现, 就是在Factory的基础上, 提供了更多的灵活性. 正如XXX提到的, 连Factory本身都可以被定制(通过继承), 所以才是abstract的.
3. 所谓模式, 就如同围棋中的定式一样. 不用定式, 你会很快输棋的; 但不用模式, 你的程序仍然okay, 但如果大一点的系统, 恐怕就有点麻烦. .
4. 学习模式固然重要, 但更难的是要知道何时该用,何时不用. 有一本书居然就叫: AntiPattern. 但有趣的是: An AntiPattern is a pattern that tells how to go from a problem to a bad solution.
5. 模式无处不在.
———————————————————————————————————————————————
谢谢!这些正是我提问时特别想得到的答案和确认。因为就我自己的经验和理解来说,书上说的有些模式应用未免有点死板和教条。而在具体的应用中根据问题本身,使用正确的模式设计则是至关重要的,有时也不必居于教条。
就拿我们上面说的这个例子来说,我们都同意使用strategy pattern,但是对于具体实现,至少有三种:
1) 直接在main()根据用户选择实例化及使用strategy pattern。
2) 结合Factory method pattern来做具体strategy的实例化,
3) 使用Abstract factory, 使其更灵活。
我们能说那种方法总是最好吗? 对于这个应用, 我觉得方法1反而更容易让人接受(业余选手的感觉)。
如果有说错的,请加以纠正。谢谢!
———————————————————————————————————————————————
呵呵,,模式无处不在....
其实做为一个新手,通常会把自己处于一个较低的位置,,厄,把自己做为一个业余的选手..
但是老是这样对自己的发展很不好的..
所以我建议新手首先把自己的位置定要的高一点...尽量去模仿高手,,,多去学习,,多严格要求自己..要像高手一样的学习...
所以我觉得你最好还是选择第三种方法..对自己要求高一点...对自己有利一些....成长就会更快一些...
我的看法:
其实模式学习是比较重要的,特别是对于一些大型软件的开发...真的是有很大的帮助....推荐每个人都读读..这个里面有一些观点还是比较正确的.等你把模式学习得比较/很熟练的时候,你就会发现模式已深深嵌入了你的思维,模式无处不在.....