OOP三大特性最重要的:多态。
很多程序员虽然在用支持OOP的语言,但却从未用过多态。
正是有了多态,软件设计才有更大弹性,更好拥抱变化。
多态,即一个接口,多种形态。
一个draw方法,以正方形调用,则画正方形;以圆形调用,则画圆形:
继承的两种方式之一的实现继承,请尽可能用组合替代。而接口继承,主要是给多态用的。
因为重点在于继承体系的使用者,主要考虑父类,而非子类。
如下代码段,不必考虑具体形状是啥,仅需调用它的draw方法
优势在于,一旦有新变化,比如将正方形换成圆,除了变量初始化,其它代码不需要动。
既然多态这么好,为什么很多人感觉无法在项目中自如地多态?
多态需构建抽象。
找出不同事物的共同点,这是最具挑战的。令人懵逼的也往往是眼中的不同之处。在很多人眼里,鸡就是鸡,鸭就是鸭。
寻找共同点,根基还是分离关注点。
当你能看出鸡、鸭都有羽毛,都养在家里,你才可能识别“家禽”。
构建出的抽象会以接口(此处接口不一定是个语法,而是一个类型的约束)体现。所以,本文讨论的多态范畴内,接口、抽象类、父类等概念等价,统一称为接口。
最影响程序的就是各种变化。有时需求来了,你的代码就得跟着改,一个可能的原因就是各种代码混在了一起。
比如,一个通信协议的调整,你要改业务逻辑,这明显不合理。
所以识别出变化与不变,是区分程序员水平的一大标准。
清晰界定系统内不同模块的职责很关键,而模块间彼此通信最重要的就是通信协议,对应到代码中的接口。
很多程序员在接口中添加方法很随意,因为他们眼里,不存在实现者和使用者的角色差异,导致没有清晰边界,后果就是模块定义随意,彼此之间互相耦合,最终玩死自己。
所以,理解多态在于理解接口,理解接口在于谨慎选择接口中的方法。
面向接口编程的价值就源于多态。
这些原则你可能都听说过,但写代码时,就会忽略细节。
比如:
这显然没有面向接口编程,推荐写法:
差别就在于变量类型,是面向一个接口,还是面向一个具体实现类。
多态对程序员的要求更高,需要你能感知未来变化!
OOP会限制使用函数指针,它是对程序控制权的间接转移施加了约束。
理解这句话,就要理解多态如何实现的。
Linux文件系统用C实现了OOP,就是用了函数指针:
即可这样赋值:
给该结构体赋不同值,就能实现不同文件系统。
但这样非常不安全。既然是个结构体字段,就可能改写它:
本该在hellofs_read运行的代码,跑进了sillyfs_read,程序崩溃。对于C这种灵活语言,你无法禁止这种操作,只能靠人为规定和代码检查。
到了OOP 语言,这种做法由一种编程结构变成一种语法。给函数指针赋值的操作下沉到了运行时去实现。运行时的实现,就是个查表过程:
一个类在编译时,会给其中的函数在虚拟函数表中找个位置,把函数指针地址写进去,不同子类对应不同虚拟表。
当用接口去调用对应函数时,实际上完成的就是在对应虚拟函数表的一个偏移,不管现在面对哪个子类,都可找到相应实现函数。
C++这种注重运行时消耗的语言:
对于Java程序员,可通过给无需改写的方法添加final帮助运行时优化。
当多态成为语法,就限制了函数指针的使用,犯错率大大降低!
封装,多态。至于继承,却不是必然选项。只要能够遵循相同接口,即可表现出多态,所以,多态并不一定要依赖继承。
动态语言中一个常见说法 - Duck Typing,若走起来像鸭子,叫起来像鸭子,那它就是鸭子。
两个类可不在同一继承体系下,但只要有相同接口,就是一种多态。
如下代码段:Duck和FakeDuck不在一棵继承树上,但make_quack调用时,它们俩都可传进去。
很多软件都有插件能力,而插件结构本身就是多态。
比如,著名的开源图形处理软件GIMP,它自身是用C开发的,为它编写插件就需要按照它规定的结构去编写代码:
struct GimpPlugInInfo
{
/* GIMP 应用初始启动时调用 */
GimpInitProc init_proc;
/* GIMP 应用退出时调用 */
GimpQuitProc quit_proc;
/* GIMP 查询插件能力时调用 */
GimpQueryProc query_proc;
/* 插件安装之后,开始运行时调用*/
GimpRunProc run_proc;
};
我们所需做的就是按照这个结构声明出PLUG_IN_INFO,这是隐藏的名字,将插件的能力注册给GIMP这个应用:
GimpPlugInInfo PLUG_IN_INFO = {
init,
quit,
query,
run
};
这里用的C语言,但依然能表现多态。
多态依赖于继承,这只是某些程序设计语言自身的特点。在面向对象本身的体系中,封装和多态才是重中之重,而继承则很尴尬。
一定要跳出单一语言的局限,这样,才能对各种编程思想有更本质的认识。
OOP三大特点的地位:
某系统需要对普通用户增删改查,后来加了超级管理员用户也需要增删改查。把用户的操作抽象成接口方法,让普通用户和管理员用户实现接口方法…… 那么问题来了,这些接口方法的出入参没法完全共用,比如查询用户信息接口,普通用户和超级管理员用户的返回体信息字段不同。所以没法抽象,请问一下老师这种应不应该抽象呢?如果应该做成抽象需要怎么分离变的部分呢
应该分,因为管理员和普通用户的关注点是不同的。管理员和普通用户可以分别提供接口,分别提供相应的内容。
如果说非要二者共用,可以考虑在服务层共用,在接口层面分开,在接口层去适配不同的接口。
多态是基于对象和面向对象的分水岭。多态就是接口一样,实现不同。
建立起恰当抽象,面向接口编程。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/qq_33589510/article/details/120630643
内容来源于网络,如有侵权,请联系作者删除!