OOD的扩展性(继承)

继承的作用,很大程度上,体现在扩展性上。
如果是SOD设计,如果要实现多种不同的场景或者条件下的计算,那么需要在函数中,实现不同程度的分支判断,随着场景的越来越复杂,实现分支判断将变得极其复杂,甚至是不可实现。
这就涉及到一个问题,可扩展性。
可扩展性如果不可得,那么就引发了可维护性问题。

OOD的扩展性,通过继承来实现,也就是去除了代码中需要手动实现的复杂的分支判断,而将这些分支判断工作,交给编译器来自动完成,从而将人从分支判断逻辑中解放出来。

通过继承,可以无需修改父类代码,只需要修改子类代码即可。
在编译过程中,参与链接的代码段,由编译器来选择,是忽略父类的代码段,采用子类的代码段。
也就是说,虽然父类的代码段没有被采用,但是父类的代码段,仍然是存在的,也并没有受到修改。
这就留下了余地,一旦在其他地方需要链接父类的代码段,就可以直接采用了。

这里,已经比较清晰了,
继承能够带来可扩展性和可维护性的本质,是在于代码段的显隐控制。
显隐控制,取决于代码段的作用域标识。
这又涉及到OOD的最基本特性,封装。
通过将代码段封装到类中,划定了代码段的归属,打上了类的作用域标识。

被封装在父类中的同名函数,和被封装在子类中的同名函数,
编译时,都会被编译成代码段,但是,根据调用对象的不同,会被编译器识别出不同的作用域,在链接时,自然会采用不同的代码段。

继承,除了上述的控制代码段的显隐,对数据成员的影响,也是正面的。
数据成员被按照圈层,划分为几个组。对应于SOD中的嵌套数据结构。
父类的数据成员,被组织成一个结构体,而子类的数据成员中,首成员,就是父类的那个结构体对象,然后,才是子类的自有数据成员。
虽然编译器在索引时,增加了层次结构,但是并未实质性的增加数据存储开销。

在OOD的代码中,虽然形式上,仍然是对象名索引成员名的形式,但是编译器会自动识别出对象所属的类的继承关系,并自动展开为层次索引,无需手工介入。

这样做的好处,显而易见。
如果在代码中,为父类对象分配内存时,不会出现冗余的数据成员,为子类对象分配内存时,会按照圈层关系,首先分配父类数据成员的内存,然后才分配子类的扩展数据成员的内存。
这样,在为不同的子类分配各自的对象的内存时,各自的对象中,均不会出现冗余的数据成员。

通过类的继承,将数据成员封装到合适的类中,明确了数据成员的生存周期。
子类对象的自有数据成员,只会在子类对象被new创建后,才会存在于内存中,并位于子类的自有数据区域。
这给可扩展性带来了极大的好处,因为不用修改父类的数据结构体,保持了父类的数据结构体的安全性。

总而言之,继承,通过增加层次关系链的复杂性,换取了可扩展性。这是值得的,因为,层次关系链的复杂性,可以交给编译器和链接器来维护,将人解放出来。

如果所有的代码的增删改,都放在父类中去实现,那么将出现大量冗余数据成员去适应不同的场景和条件,大量复杂的分支判断,去落实到当前特定的场景和条件。
但是通过继承,只需要延长层次关系链,就可以保持父类不修改,将需要的增删改放在子类中实现。
数据成员的集合,由编译器根据创建对象的类来识别,
代码段的显隐,也由编译器根据调用对象的类来识别。

总之,对象所归属的类,决定了作用域标识,而作用域标识,则是编译器决策的判据。

这就是那个著名的论断:
OOD使得一个类只做一件事,系统越复杂,工程中存在的类就越来越多。
OOP使得一个类越来越全能,系统越复杂,类虽然不会增多,但是类的冗余成员越来越多,方法也越来越复杂。

猜你喜欢

转载自blog.csdn.net/weixin_42418557/article/details/120674228