文章目录
零、简介
以 Java 为例,作为一个 OOP 语言,当我们学习的时候,肯定会接触到:封装、继承、多态(和抽象)。
这几种特征,也被称面向对象编程的主要原则。这些主要原则,是用于提高代码的可读性和可复用性,使得程序能够被高效的构建。
除了这些主要原则外,还有六大设计原则:
- 单一职能原责(Single Responsibility Principle)
- 开闭原则(Open Closed Design Principle)
- 里氏替换原则(Liskov Substitution Principle )
- 接口隔离原则(Interface Segregation Principle)
- 依赖倒置原则(Dependency Inversion principle)
- 迪米特原则(Low Of Demeter)
前面五个原则,取第一个字母,就凑成了著名的:S.O.L.I.D原则,这五个原则是由 Robert C. Martin 定义的,而最后一个迪米特原则,最早在1987年提出,在 2003/2004年重新审议的。
这六个设计原则,主旨是为了帮助我们清晰的设计代码,不仅方便了我们开发,在后期的维护过程中也是相得益彰。而这六大设计原则,已然渐渐成为一个程序员必不可少的理解内容了,如果你还没有了解,或者不够熟悉的话,赶紧来学习一下吧!
一、单一职能原则(Single Responsibility Principle)
定义:当你修改或者创建一个类,该类应该只处理一个单一的功能。
这个原则是最好理解的,比如说你可以创建一个 Switch 类,用于放松打游戏的,而其肯定有个组合成员:手柄类,那么手柄类就是专门用于操控的功能。
该原则的主要优点就是,减少了各个功能组件和代码之间的耦合。
二、开闭原则(Open Closed Design Principle)
定义:类或者方法可以(Open)扩展,但是不要(Closed)修改。
对于一个类来说,如果类的功能已经写的比较完整(No Bugs),或者是在后期的维护阶段,当你觉得哪个地方需要修改,但是请尽量不要这么做,你可以通过扩展的方式来达到你的目的,例如继承、组合、动态代理等方式。
对于一个方法来说,如果方法的功能是可以通过的(No Bugs),你想添加个什么玩意儿,不要这么做!你可以通过重载的手段来达到你的目的,对于已有的方法,千万不要去动它!
该原则的主要优点就是,之前已经测试通过的代码可以被完整的保留,且不会被破坏。
三、里氏替换原则(Liskov Substitution Principle )
定义:你可以将子类替换成超类,并且使用替换的类调用其方法,不会出错。
abstract class Father{
abstract fun dosm()
}
class Son: Father() {
override fun dosm() {
print("我是儿子")
}
}
//Main函数入口
fun main() {
//Java代码:Father son = new Son();
val son:Father = Son()
son.dosm()
}
如上述代码示例,创建一个son对象,但是将其类型替换成Father,再调用dosm()
方法时,也能够正常打印那句:我是儿子。(谁在心里默默地应了一句,我保证不打死你)
该原则主要是为了限制父类比子类功能更多的尴尬局面,但是一般在我们开发的过程中,都不会出现这样的事情(语言特性会限制你)。
四、接口隔离原则(Interface Segregation Principle)
定义:当一个类实现了一个接口,来达到想要实现的功能,但是该接口有冗余的方法,使得该类被迫实现不需要实现的方法,那么这个时候,我们就需要细分接口。
简单的来说,就是考验你划分接口的功底了,你首先需要对应用程序整体结构的划分,然后再定义各个接口。一般来说,接口定义好之后,是不能够修改的(参考单一职能原则),所以一定要谨慎!
该原则是为了使代码更加清晰,配合单一职能原则更好的体现代码的可阅读性。当你使用接口时,必须要实现其所有的方法(kotlin或者Java 1.8关键词 default来达到可以不重写的目的),而接口隔离的话,你就可以实现最少的方法。
五、依赖倒置原则(Dependency Inversion principle)
定义:提供复杂逻辑的高级模块应该易于重用,并且不受提供实用功能的低级模块的更改的影响。该原则由两个部分组成:
- 高级模块不应依赖于低级模块。两者都应该依赖于抽象。
- 抽象不应依赖于细节。细节应该取决于抽象。
interface IWorker {
fun work()
}
class Worker : IWorker {
override fun work() {
print("我是Worker")
}
}
class SuperWorker : IWorker {
override fun work() {
print("我是SuperWorker")
}
}
class Manager {
private var worker: IWorker? = null
fun setWorker(worker :IWorker){
this.worker = worker
}
fun manage() {
worker!!.work()
}
}
fun main() {
val manager = Manager()
//此处是我们具体的实现,我们实现的就是 SuperWoker 来进行工作
manager.setWorker(SuperWorker())
}
我们可以分析上面的例子,最后我们通过 manager.setWorker(IWorker)
方法,来使得一个IWoker来进行工作,而具体是哪个IWorker呢?我们可以自己去定义。而定义高层的过程中,底层肯定是依赖于高层,其具体实现完全取决于高层的逻辑处理。
该原则的优点及其明显且实用,我们有时候仅仅需要定义一些接口,再在高层抽象类中写一些逻辑,就能完成某个功能组件的基本架构。
六、迪米特原则(Low Of Demeter)
定义:该原则也称Principle of Least Knowledge,知道最少原则,只与你的直接朋友打交道。例如,从不调用从另一个调用或全局对象获得的对象上的方法。
class Person(val name:String){
var friend:Person?=null
fun getFriendName(): String {
return friend?.name?:"我没有朋友"
}
}
fun main() {
val a = Person("小A")
val b = Person("小B")
val c = Person("小C")
a.friend = b
b.friend = c
val cName_1 = a.friend?.getFriendName()
val cName_2 = b.getFriendName()
}
分析上例,每个人只能有一个朋友,A的朋友是B,B的朋友是C,现在我们需要知道C的名字。在上例中我们有两种方式拿到C的名字:
val cName_1 = a.friend?.getFriendName()
val cName_2 = b.getFriendName()
第一种方式,我们借助了A的朋友B,而第二种方式,我们直接通过B来获取名字。
该原则是达到高内聚低耦合目的的一种手段,平时我们写代码一定要注意这点,否则你写出来的代码很可能就存在类似的问题。
七、小结
我们在六大设计原则的基础上,才能去更好的理解各种设计模式。而在各种设计模式中,我们也能体会这六大设计原则的奥妙。