设计模式篇(零)——面向对象的六大设计原则

零、简介

以 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)

定义:提供复杂逻辑的高级模块应该易于重用,并且不受提供实用功能的低级模块的更改的影响。该原则由两个部分组成:

  1. 高级模块不应依赖于低级模块。两者都应该依赖于抽象。
  2. 抽象不应依赖于细节。细节应该取决于抽象。
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来获取名字。

该原则是达到高内聚低耦合目的的一种手段,平时我们写代码一定要注意这点,否则你写出来的代码很可能就存在类似的问题。

七、小结

我们在六大设计原则的基础上,才能去更好的理解各种设计模式。而在各种设计模式中,我们也能体会这六大设计原则的奥妙。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值