那么既然OO已经可以很好的解决我们的问题了,为什么还要有GP?
我们先来看一下OO的缺点:
OO的多态是通过继承自同一接口来实现的, 修改接口会导致所有派生类的修改,耦合比较紧密。
OO的多态是运行时的, 性能比较低。
OO的多态通过抽象接口实现,使用不安全,在不支持反射的语言(比如C++)中会丧失类型检测。
用OO设计时,你只能通过抽象接口来处理不同的事物。比如你要让Cat和Dog同时跑动,你可能会抽象出一个IAnimal接口,内部有一个Run的方法,然后让Cat和Dog分别继承。但是如果你想让猫(Cat)和玩具猫(ToyCat)支持跑动,这时你抽象出IAnimal就不合适了, 你可能会抽象出ICatProperty。可以看到OO的这种继承体系非常笨重,到后面可能会导致非常深的继承层次(比如MFC),代码扩展和维护都很艰难。
上面OO的这些缺点在GP没有被发现之前都不是缺点,因为我们没有更好的解决方案,但是人们逐渐发现了GP,GP最初是在使用容器(Container)时被发现的,container<T>可以放任意类型的元素,根据元素类型,我们可以可以生成任意类型的container. 这些技术后来发展成范型编程和模板元编程。
那么什么是GP?
我的理解是 GP是基于concept的程序设计,我们通过concept来定义对象之间的关系。
什么是concept?
按我的理解,concept就是我们自己定义的概念和规则。
比如说任何对象,只要可赋值,他就符合Assignable的concept:
class B {};
void test()
{
A a1; A a2; a1 = a2;
B b1; B b2; b1 = b2;
}
又比如任何对象,只要可比较, 他就符合Comparable的concept:
{
public :
bool operator < ( const C & c);
bool operator > ( const C & c);
bool operator == ( const C & c);
bool operator != ( const C & c);
bool operator <= ( const C & c);
bool operator >= ( const C & c);
};
再看一个例子:
{
public :
void run();
};
class Cat
{
public :
void run();
};
template < typename T >
void run(T & t)
{
t.run();
}
GP的concept可以是任何我们能想到的规则,比如包含某个成员变量,或是定义某个类型, 比如下面代码:
{
public :
typedef int type;
type value;
};
template < typename T >
typename T::type GetValue(T & t)
{
return t.value;
}
甚至GP的concept可以是指明基类和派生类, 比如下面代码:
class CMyImpl: public TBase
{
public :
void work()
{
TDrived * p = static_cast < TDrived *> ( this );
p -> Hello();
}
};
class CMyBase {};
class CMyClass: public CMyImpl < CMyClass, CMyBase >
{
public :
void Hello() {}
};
这些concept在不同语言中定义不一样,在一些语言中(比如C#)我们可以通过定义来进行显式约束,表明我们某个concept要满足哪些条件;在有些语言中(比如C++)则没有这显式约束定义,我们只能通过我们编程时自己的逻辑来保证,当然如果某个方法要求他的对象满足某个concept而你的对象没有满足 ,编译器也是不会让你通过的。
另外GP的concept之间本身也有某种关系, 某个concept可能继承与另外一个concept,或者说某个concept是另外一个concept的强化,也就是满足某个concept的对象肯定也同时满足另外一个concept. 比如concept A包含Run方法, concept B包含Run和Eat方法,则concept B是concept A的强化,满足concept B的对象肯定同时满足concept A。
有些人说GP的抽象能力高于OO,这个观点我并不认同,我感觉只是他们的抽象方式不一样,OO是基于接口, 而GP是基于concept。OO的基于接口的抽象,在源代码和最终运行时都能体现,源代码中是接口,运行时是虚表,所以他们是一致的, 符合普通人的思维习惯。GP基于concept的抽象, 主要体现在源代码中 ,只是你用来告诉编译器你的思维方式, 在运行时他可能是一个完全不同的世界,所以比较难理解。
如果说OO的设计是抽象出接口, GP的设计就是抽象出concept, 满足某个concept的class是一个template class(如template<typename T> class vector), 而template class的又可以实例化成某个特定的class(如vector<int>)。 所以GP可以大大减少我们源代码的数量,但是他本身不能减小我们最终编译的可执行文件的大小,相反如果模板类过大,反而会造成代码膨胀,而OO的继承则没有这个问题(可参考 C++模板会使代码膨胀吗 )。
对于在实际开发中主要用OO还是用GP的问题, 可能应人和公司而异, 最好的方式当然是OO和GP的灵活结合 。有人觉得GP的代码不好理解,那是因为你没有理解他的concept,比如看了《范型编程与STL》,你就会发现自己也可以扩展STL了。当然,因为C++对concept没有约束定义机制, 而concept这个东西注释又不好描述,所以大部分时候我们只能通过源代码来推理,这在一定程度上也造成了GP代码难读懂这种说法的流行。
1912



被折叠的 条评论
为什么被折叠?



