在面向对象程序设计领域,有5条非常重要的设计原则,它们的首字母拼起来,正好是S.O.L.I.D这个单词,因此也称SOLID原则。SOLID原则能够指导程序员有效避免代码中的坏味道,从而开发出易于维护和可扩展的软件。
然而SOLID原则比较难以理解,包括我自己也常常会记不全,我特别希望能把技术去应用到生活里,通过与生活的结合,去从新的维度思考技术发展的本质。因此我尝试把设计原则结合生活实际,把它们重新阐述一遍。
S——单一职责原则,Single-responsiblity principle
从代码角度来讲,一个类只能承担一个职责。通俗点儿说就是一个类只能承担一件事,并且只能有一个潜在的原因去更改这个类,否则就违反了单一职责原则。
例如,筷子是用来吃饭的,勺子是用来喝汤的,刀叉是用来吃西餐的,从来没有一样厨具可以把所有的功能兼容包并,即便有那样的厨具存在,对于使用成本来说也太大了。
无论是在编程,还是在其他领域,都主张一件事物只做一件事情,只有把职责划分的足够细,功能才会明晰。
从工作角度来说,因为分工产生了协作,因协作而提升了效率,每个人都要明确自己的职责,以防在协作的过程中含糊不清,增加讨论交流的成本。
也许你会说,手机既可以拍照,也可以打电话,也可以玩各种app,它能做很多件事情,是否违反了单一职责原则?其实不然,手机是一个上层的系统,而构成手机的无数个组件都是按照单一职责来划分的。单一职责看的是局部,而不是整体。
O ——开闭原则, Open-closed principle
软件实体应该对扩展开放,对修改关闭。允许扩展行为而无需修改源代码。
说回手机的例子,我们之所以能够在手机上下载各式各样的应用,其实就跟开闭原则有关。手机提供了一个平台,为各类app提供了很多的基础能力,而各个app按照自己的应用场景去定制开发,然后使用者使用安装app的形式,去获得这部分的功能。因此对手机来说,它本身的修改是关闭的,但是对功能的扩展是开放的。
试想,如果手机违反了开闭原则,则每次想要获得新功能,都要首先升级手机自身的程序,安装app就会变得特别繁琐。
再比如,吹风机可以搭配不同风格的出风口,充电器可以搭配不同型号的充电线,这些都是很好的开闭原则的例子。
我觉得开闭原则对我们人生也有很多启示,如果我们自己能够形成一以贯之的人生方法论,不用每次获取新知识时都把自己推翻和重建,是不是在获取新知的过程中效率更高了呢?
L——里氏替换原则,Liskov substitution principle
程序中的对象应该可以被其子类实例替换掉,而不会影响程序的正确性。
例如做菜的时候,你可能要放食用油,但是油分为好多种,比如大豆油,橄榄油,花生油,菜籽油等等,这些品种都是食用油的子类,每一种都可以进行替代。
谈到替换两个字,我不禁想到了作为程序员,很容易被更年年轻的程序员替换,如果一个程序员只是满足于当下的工作,完全按照既定的父类的框框去做事情,不注重技术的创新和知识的迭代,那往往就会被更多的新的子类程序员替换掉。
所以代码要遵从里式替换,但是职业发展拒绝被里式替换。
在编程开发中,往往只要代码符合了里式替换原则,也很难不符合开闭原则,对于不符合里式替换原则的场景,又可以借助下面的接口隔离原则来实现。
I ——接口隔离原则,Interface segregation principle
使用多个特定细分的接口比单一的总接口要好,不能强迫用户去依赖他们用不到的接口。
举个简单的例子,麻雀,燕子,鸽子,鸵鸟都是鸟,但是鸵鸟不会飞,对于会飞这个属性来说,就不能独属于鸟,而应作为鸟类的一个接口。
接口隔离原则也体现了继承和组合的区别,如果是继承,继承使得父类暴露了过多细节给子类,也使得父类难以改变,而组合则更加灵活,是一种更方便的扩展手段。
扯远一点说,接口隔离原则是承认了世界的复杂性。没有一项事物是我们能够用单一标准所评价的。
D ——依赖倒置原则,Dependency Inversion Principle
程序要依赖于抽象接口,而不是具体实现。
高层模块不应该依赖于低层模块,二者都应该依赖于抽象
抽象不应该依赖具体实现,具体实现应该依赖抽象
简单来讲,实现依赖抽象,存在一个总的原则在指导我们,而具体的实现细节可以自由把控。
比如做一道炒菜,你可能需要买菜、备菜、炒菜、出锅等必要工序,具体去那里买,具体怎么切菜,用什么锅炒菜都可以自由发挥,但是整体的做菜过程是千篇一律的。
再比如,我们每个人从出生,到上学,到工作,到成家立业,也是在一个大的框架体系下进行的,这种框架类似于某种社会共识,而每个人都是这个社会共识的具体实现,如果没有了这个框架,个体自由度过大,则会加剧社会的维护成本。
能否灵活运用依赖倒置原则,也是判断一个程序员是否资深的重要标准。高水平的程序员应该用框架来指导开发,而不是拘泥于细节,编程领域的很多框架,例如spring,tomcat都运用了依赖倒置思想。
以上5点设计原则对于编程设计来说,尤为重要。对于已经有一定经验的程序员,在日常工作开发中,一定要对自己的要求更高一层,而设计原则能够指导代码往增强可测试性和更易于维护的方向转变,是非常不错的优化方向。