首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > C++ >

求教,关于李氏撤换原则的适用性和实现

2012-12-30 
求教,关于李氏代换原则的适用性和实现李氏代换原则:Subtypes must be substitutable for their base types

求教,关于李氏代换原则的适用性和实现
李氏代换原则:Subtypes must be substitutable for their base types(子类必须能够替换成它们的基类).

我记得我刚工作那会就听带我的前辈同事说这个东西,那时候我觉得我挺理解的,但是现在越来越觉得不理解。尤其是它的适用性和实现方式。

介绍这个原则的文章很多,并且大多用了"正方形不是矩形"之类的例子,看例子是容易懂的,并且觉得天经地义。但是在具体编程中,我发现这个东西并不好操作。比如下面的情况:

比如,现在我要实现矩阵这个功能,包括加减乘以及求矩阵的秩,迭代器等等,这个没问题。然后还要实现方阵相关功能,比如求逆矩阵,设为单位矩阵,LU分解等等。另外方阵的构造函数和一些操作的参数允许值也不同。

按照书上的说法,方阵不是矩阵。我可能要这么做:


// 矩阵
class matrix
{
// 公有操作
};

// 方阵
class square_matrix : public matrix
{
// 方阵操作
};

// 不方的矩阵
class non_square_matrix : public matrix
{
// 普通操作
};


或者这样:

// 矩阵
class matrix
{
// 公有操作
// 普通操作
};

// 方阵
class square_matrix
{
    matrix _data;
// 公有操作
// 普通操作
// 方阵操作
};


这两种方法我觉得都有问题。

首先第一种代码会很多而且重复,因为基类matrix里面没有几个函数能写,大部分的函数都是差不多但是参数允许值不一样或者类型不一样,这样ctrl-c+v过多肯定不好看。

然后第二种,也有问题,方阵虽然“不是”矩阵,但也不完全不是,至少测试的时候它很大程度上是,这个问题可以用接口继承解决,但是带来了第二个问题,就是这时候内联函数不内联了,成了虚函数,而且多了一次转发调用,要写很多仅仅是把操作转发到_data上的代码(事实上我甚至写了一个工具专门干这个事情),如果是一般的工程这也可以,但是如果我写的是模板代码,这种情况就比较不能忍受,毕竟写模板代码除了泛型以外,大量的内联也是很大的好处,这样就白白损失了。另外还有一个问题就是自定义操作符(operator xx),采用这种方法要吧操作符的实现单独放在一个虚方法里,再在operator里调用,太繁琐了,也不好看。

我现在的方法是,如果结构比较简单没什么大问题或者两个类确实有“物理”上的关系,我就直接使用派生类。如果我觉得确实直接派生不太合逻辑我就用工具生成那些重复的代码。

那么在这种情况下,有没有一种简洁而又不失效率的实现方法,在此向各位大侠求教。


[解决办法]
这是个复杂的问题。如果不会取舍,单纯追求完美是得不到答案的。

实际上,C++的类很多时候和现实意义上的类有很大区别。
以几何形状来说,在现实意义上,正方形是一种特殊的矩形,也是一种特殊的菱形;正方形和菱形都是一种特殊的平行四边形;而平行四边形又是一种特殊的四边形。所以应该是从四边形派生出平行四边形,从平行四边形派生出长方形、菱形,从长方形、菱形派生出正方形。
但是对于C++来说,情况恰恰相反。
其原因在于,对C++来说,描述几何形状是在人为发现的规律的基础上进行的。比如正方形,我们只要描述一个边长就可以了。而正方形却是有四条边的(尽管他们长度相等),人们总结规律,发现只要知道一条边的长度就能知道其他的边长(而且还默认内角都是90度)。而对C++来说,派生只是增加成员、调整行为而已。

所以,对于设计类库来说,你需要做的只是确定要实现哪些接口,并分别在那些类中实现,那么整个类库的各个类之间的派生关系就确定下来了。至于所谓的原则,那只是“路标”,如果搞不懂,反而给你带来困扰,那么就把他忘了吧。
我个人认为,设计类库真正的困难就是确定要实现哪些接口。
[解决办法]
该原则是说父类支持的操作子类应该也具有,但行为可能不同,就是接口一样而实现不同,但其意义应该是相同的,在替换时用子类完全可以替换父类,而且是有意义的
比如 方阵 是矩阵,只要是普通矩阵可以进行的,比如加、减、乘等,而方阵必然也可以
比如 A是普通矩阵,B是方阵,只要是A可以进行的操作B必须可以,就拿你说的构造函数来讲,说构造函数不同,其实是相同的,假设普通矩阵的构造函数可以带两个参数
matrix(int x, int y)
而方阵也可以带两个参数
matrix(int x, int y)
只是要求两个值必须相等,在实现时进行检验即可,还可以扩展另外一个
matrix(int d)
这个算是扩展的新方法,这样就保证了其可替代性
其它方法也是类似,父类有的方法,子类一定要支持并且含意相同,而子类可以扩展更多的方法

热点排行