共计 1816 个字符,预计需要花费 5 分钟才能阅读完成。
本文系转载,著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
作者:宋宝华
来源:微信公众号 linux 阅码场 (id: linuxdev)
前言
《设计模式》这本经典的书里面定义了 20 多种设计模式,虽然都是面向对象的,似乎需要 C ++、Java 这样的语言才能实现,但是根据笔者前面反复强调的,Linux 内核虽然是用 C 语言和汇编语言写成,但是其实也到处充满了面向对象的设计。面向对象更多的是一种思想,而不是一个语言。我们可以用 C 语言实现极大的 OO,Linux 内核到处都有 OO。
模版方法
比如,在 Linux 的设备驱动框架中,就用了一种非常经典简单的设计模式——模板方法 (Template Method),当然还有一些其他的设计模式。而设计模式牛逼的地方在于,高手往往不经意之间已经用到了设计模式,甚至自己都不知道。如果高手没有系统地学习过设计模式,这其实不见得是一个问题。这并不意味着它不懂设计模式,只是他自己都不知道自己用到了哪个模式。而设计模式学习的终极目的,当然也是忘记设计模式,这个跟练独孤九剑没什么区别,到最后其实是无招胜有招。
模板方法这个模式,强调定义一个基类,这个基类实现了通用的流程和算法。比如做一件事情需要经过 step1()、step2()、step3()。那么我们定义一个基类:
而其中的 step1()、step2()、step3()、step4() 具体如何实现则是因人而异,所以我们从 baseClass 类里面,继承出来的类里面,实现 step1()、step2()、step3() 这样的代码,override 掉 baseClass 里面的函数。
这样的设计让外部不关心 derivedClass,因为流程和接口都是在基类的。而基类实现的 doSomething() 成员函数,是对外的接口。这个 UML 关系是非常简单的:
驱动案例
在 Linux 设备驱动里面,大量存在类似的设计,我们以 NAND 为例子。在 drivers/mtd/nand/nand_base.c 这层里面,定义了 NAND 的一些操作流程。
比如写 OOB 的代码:
它这个里面要走 cmdfunc()、write_buf()、cmdfunc()、waitfunc() 这些步骤,这些步骤,不管是全世界哪个 NAND 的硬件,都是一样的通用的,但是具体的不同的 NAND 硬件控制器,实现这些步骤中涉及到的 cmdfunc() 等函数的实现方法却因人而异。
譬如 freescale 的版本 fsl_elbc_nand.c 就是:
nand_base.c 这个 C 文件是 NAND 的中间层,它非常类似我们前面说的实现 baseClass 这一层的代码,nand_write_oob_std 函数类似 baseClass :: doSomething。而 Linux 驱动中定义的 nand_chip 的各个不同的 NAND 控制器,对 nand_chip 这个结构体中成员函数 cmdfunc()、write_buf() 等的实现则是各异的,类似 derivedClass 里面 override 掉 step1()、step2()。nand_chip 定义在 include/linux/mtd/nand.h:
这样的设计,好处是非常明显的。特定的硬件只用管与自身操作相关的事情,而通用的流程,都由 nand_base 搞定,最大程度上减小了具体实例的代码量,也最大程度上复用了中间层的代码。
这样的例子无处不在,比如我们在 LCD 的中间层:
后语
本文后语不搭前言,请见谅。最近有很多童鞋询问笔者, 做 Linux 驱动有没有前途?笔者明确地告诉大家:根本没有前途! 但是前途是自己赚的,这依赖你从驱动进去,但是从更大的视角出来:
- 通过做驱动理解很多 OO 的架构设计思想,升华自己高内聚和低耦合的理解,把自己变成一个更高 level 的 software engineer;
- 通过做驱动,进一步理解 Linux 本身的进程、内存、IO 等知识,升华对软件系统和性能分析的理解,把自己变成一个更高 level 的技术 expert。
如果做了 5 年驱动,进入的时候是调试寄存器搞示波器,出来的时候还是调寄存器搞示波器,那自然是完全没有什么前途的!
有没有前途,这个事情,完全是因人而异的。前途是无所谓有,无所谓无的。你如果有抽象、衍生的能力和不断学习总结的精神,无论是做驱动还是不做驱动,都会是很有前途的事情。反之,做什么基本都没前途。
更多精彩更新中……欢迎关注微信公众号:linux 阅码场 (id: linuxdev)