主頁(yè) > 知識(shí)庫(kù) > 分析Go語(yǔ)言接口的設(shè)計(jì)原則

分析Go語(yǔ)言接口的設(shè)計(jì)原則

熱門標(biāo)簽:評(píng)價(jià)高的400電話辦理 阿克蘇地圖標(biāo)注 百度地圖標(biāo)注后傳給手機(jī) excel地圖標(biāo)注分布數(shù)據(jù) 壽光微信地圖標(biāo)注 涿州代理外呼系統(tǒng) 外呼系統(tǒng)用什么卡 外呼系統(tǒng)顯本地手機(jī)號(hào) 電話機(jī)器人軟件免費(fèi)

一、前言

go的interface寫(xiě)起來(lái)更自由, 無(wú)需顯示的實(shí)現(xiàn), 只要實(shí)現(xiàn)了與interfece所包含的所有函數(shù)簽名的相同的方法即可。讓編碼更靈活, 易擴(kuò)展。

如何理解go語(yǔ)言中的interface呢?

1. interface是方法聲明的集合

2.接口的方法與實(shí)現(xiàn)接口的類型方法格式一致

3.接口中所有方法均被實(shí)現(xiàn)

4. interface可以作為一種數(shù)據(jù)類型,實(shí)現(xiàn)了該接口的任何對(duì)象都可以給對(duì)應(yīng)的接口類型變量賦值

特別說(shuō)明兩點(diǎn):

  • interface 可以被任意對(duì)象實(shí)現(xiàn),一個(gè)類型/對(duì)象也可以實(shí)現(xiàn)多個(gè) interface
  • 方法不能重載,如eat(), eat(s string)不能同時(shí)存在

那么作為interface數(shù)據(jù)類型,他存在的意義在哪呢? 實(shí)際上是為了滿足一些面向?qū)ο蟮木幊趟枷?。我們知道,軟件設(shè)計(jì)的最高目標(biāo)就是高內(nèi)聚,低耦合。那么其中有一個(gè)設(shè)計(jì)原則叫開(kāi)閉原則。什么是開(kāi)閉原則

二、開(kāi)閉原則

在面向?qū)ο缶幊填I(lǐng)域中,開(kāi)閉原則規(guī)定“軟件中的對(duì)象(類,模塊,函數(shù)等等)應(yīng)該對(duì)于擴(kuò)展是開(kāi)放的,但是對(duì)于修改是封閉的”,這意味著一個(gè)實(shí)體是允許在不改變它的源代碼的前提下變更它的行為。

看重點(diǎn): 對(duì)于擴(kuò)展是開(kāi)放的, 對(duì)于修改是封閉的.

舉個(gè)例子: 銀行每天要辦理不同的業(yè)務(wù), 存款, 轉(zhuǎn)賬, 取款等. 如果直接是實(shí)體來(lái)實(shí)現(xiàn)如下

package bank

import "fmt"

type Banker struct {

}

func (b *Banker) Save() {
    fmt.Println("存錢")
}

func (b *Banker) Transfer() {
    fmt.Println("轉(zhuǎn)賬")
}

func (b *Banker) Get() {
    fmt.Println("取錢")
}

有個(gè)人要來(lái)存錢取錢轉(zhuǎn)賬了

package main

import "aaa/bank"

func main() {
    var b = bank.Banker{}
    b.Save()
    b.Get()
    b.Transfer()
}

那么隨著業(yè)務(wù)越來(lái)越多, 越來(lái)越大. 我又要新增加一些業(yè)務(wù), 比如基金, 股票. 然后越來(lái)越多,越來(lái)越大. 導(dǎo)致Banker這個(gè)模塊越來(lái)越臃腫

這樣的設(shè)計(jì)會(huì)導(dǎo)致,當(dāng)我們?nèi)ソoBanker添加新的業(yè)務(wù)的時(shí)候,會(huì)直接修改原有的Banker代碼,那么Banker模塊的功能會(huì)越來(lái)越多,出現(xiàn)問(wèn)題的幾率也就越來(lái)越大,假如此時(shí)Banker已經(jīng)有99個(gè)業(yè)務(wù)了,現(xiàn)在我們要添加第100個(gè)業(yè)務(wù),可能由于一次的不小心,導(dǎo)致之前99個(gè)業(yè)務(wù)也一起崩潰,因?yàn)樗械臉I(yè)務(wù)都在一個(gè)Banker類里,他們的耦合度太高,Banker的職責(zé)也不夠單一,代碼的維護(hù)成本隨著業(yè)務(wù)的復(fù)雜正比成倍增大。

我們使用開(kāi)閉原則, 使用interface將banker模塊抽象出來(lái). 然后根據(jù)這個(gè)抽象的模塊, 去實(shí)現(xiàn)save, get, transfer.....

那么依然可以搞定程序的需求。 然后,當(dāng)我們想要給Banker添加額外功能的時(shí)候,之前我們是直接修改Banker的內(nèi)容,現(xiàn)在我們可以單獨(dú)定義一個(gè)股票Banker(實(shí)現(xiàn)股票方法),到這個(gè)系統(tǒng)中。 而且股票Banker的實(shí)現(xiàn)成功或者失敗都不會(huì)影響之前的穩(wěn)定系統(tǒng),他很單一,而且獨(dú)立。

所以以上,當(dāng)我們給一個(gè)系統(tǒng)添加一個(gè)功能的時(shí)候,不是通過(guò)修改代碼,而是通過(guò)增添代碼來(lái)完成,那么就是開(kāi)閉原則的核心思想了。所以要想滿足上面的要求,是一定需要interface來(lái)提供一層抽象的接口的。

golang代碼實(shí)現(xiàn)如下:

package bank

import "fmt"

// 對(duì)銀行的業(yè)務(wù)進(jìn)行抽象
type Business interface {
    doBussiness()
}


// 存錢業(yè)務(wù)
type SaveBussiness struct {
}


func (b *SaveBussiness) doBussiness() {
    fmt.Sprintf("存錢")
}


//取錢業(yè)務(wù)
type GetBussiness struct {
}

func (g *GetBussiness) doBussiness() {
    fmt.Println("取錢")
}


// 轉(zhuǎn)賬業(yè)務(wù)
type TransferBusi struct {

}

func (t *TransferBusi) doBussiness() {
    fmt.Sprintf("轉(zhuǎn)賬")
}

然后我今天去了銀行, 我們封裝一個(gè)銀行, 銀行有各種各樣的能力.

package main

import (
    "aaa/bank"
    "fmt"
)

// 這有一個(gè)銀行, 銀行可以辦理業(yè)務(wù)
func Bank(b bank.Business) {
    fmt.Println("辦理業(yè)務(wù): ", b.DoBussiness())
}

func main() {
    // 辦理具體的業(yè)務(wù)
    Bank(bank.SaveBussiness{})
    Bank(bank.GetBussiness{})
    Bank(bank.TransferBusi{})
}

這樣, 當(dāng)銀行增加業(yè)務(wù)類型, 比如股票的時(shí)候, 只需要擴(kuò)展業(yè)務(wù)接口就可以了, 不會(huì)對(duì)原來(lái)的接口進(jìn)行修改

再看開(kāi)閉原則定義:開(kāi)閉原則:一個(gè)軟件實(shí)體如類、模塊和函數(shù)應(yīng)該對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉。簡(jiǎn)單的說(shuō)就是在修改需求的時(shí)候,應(yīng)該盡量通過(guò)擴(kuò)展來(lái)實(shí)現(xiàn)變化,而不是通過(guò)修改已有代碼來(lái)實(shí)現(xiàn)變化。

接口的意義:

現(xiàn)在interface已經(jīng)基本了解,那么接口的意義最終在哪里呢,想必現(xiàn)在你已經(jīng)有了一個(gè)初步的認(rèn)知,實(shí)際上接口的最大的意義就是實(shí)現(xiàn)多態(tài)的思想,就是我們可以根據(jù)interface類型來(lái)設(shè)計(jì)API接口,那么這種API接口的適應(yīng)能力不僅能適應(yīng)當(dāng)下所實(shí)現(xiàn)的全部模塊,也適應(yīng)未來(lái)實(shí)現(xiàn)的模塊來(lái)進(jìn)行調(diào)用。 調(diào)用未來(lái)可能就是接口的最大意義所在吧,這也是為什么架構(gòu)師那么值錢,因?yàn)榱己玫募軜?gòu)師是可以針對(duì)interface設(shè)計(jì)一套框架,在未來(lái)許多年卻依然適用。

三、依賴倒置原則

3.1、什么是依賴倒置原則  

依賴倒置原則(Dependence Inversion Principle)是程序要依賴于抽象接口,不要依賴于具體實(shí)現(xiàn)。簡(jiǎn)單的說(shuō)就是要求對(duì)抽象進(jìn)行編程,不要對(duì)實(shí)現(xiàn)進(jìn)行編程,這樣就降低了客戶與實(shí)現(xiàn)模塊間的耦合。

3.2、一個(gè)耦合度極高的模塊關(guān)系設(shè)計(jì)

張三駕駛奔馳, 張三駕駛寶馬, 張三駕駛豐田.

李四駕駛寶馬, 李四駕駛奔馳, 李四駕駛豐田

package yldz

import "fmt"

// 奔馳車
type Benz struct {

}

func (b *Benz) run() string{
    return fmt.Sprintf("奔馳啟動(dòng)")
}
// 寶馬
type BM struct {

}

func (b *BM) run() string{
    return fmt.Sprintf("寶馬啟動(dòng)")
}
//豐田
type FT struct {

}

func (t *FT) run() string{
    return fmt.Sprintf("豐田啟動(dòng)")
}
//====駕車人,張三
type Zhangsan struct {

}

func (t *Zhangsan) DriverBenz(b *Benz) {
    fmt.Println("張三駕駛", b.run())
}

func (t *Zhangsan) DriverBM(b *BM) {
    fmt.Println("張三駕駛", b.run())
}

func (t *Zhangsan) DriverFT(b *FT) {
    fmt.Println("張三駕駛", b.run())
}

// 駕車人----李四.......
package main

import "aaa/yldz"

func main() {
    z := yldz.Zhangsan{}
    z.DriverBenz(yldz.Benz{})
    z.DriverBM(yldz.BM{})
    z.DriverFT(yldz.FT{})
}

我們來(lái)看上面的代碼和圖中每個(gè)模塊之間的依賴關(guān)系,實(shí)際上并沒(méi)有用到任何的interface接口層的代碼,顯然最后我們的兩個(gè)業(yè)務(wù) 張三開(kāi)奔馳, 李四開(kāi)寶馬,程序中也都實(shí)現(xiàn)了。但是這種設(shè)計(jì)的問(wèn)題就在于,小規(guī)模沒(méi)什么問(wèn)題,但是一旦程序需要擴(kuò)展,比如我現(xiàn)在要增加一個(gè)凱迪拉克汽車 或者 司機(jī)王五, 那么模塊和模塊的依賴關(guān)系將成指數(shù)級(jí)遞增,想蜘蛛網(wǎng)一樣越來(lái)越難維護(hù)和捋順。

3.3、面向抽象層依賴倒轉(zhuǎn)

如上圖所示,我們?cè)谠O(shè)計(jì)一個(gè)系統(tǒng)的時(shí)候,將模塊分為3個(gè)層次,抽象層、實(shí)現(xiàn)層、業(yè)務(wù)邏輯層。

  • 將抽象層的模塊和接口定義出來(lái),這里就需要了interface接口的設(shè)計(jì),
  • 我們依照抽象層,依次實(shí)現(xiàn)每個(gè)實(shí)現(xiàn)層的模塊,在我們寫(xiě)實(shí)現(xiàn)層代碼的時(shí)候,實(shí)際上我們只需要參考對(duì)應(yīng)的抽象層實(shí)現(xiàn)就好了,實(shí)現(xiàn)每個(gè)模塊,也和其他的實(shí)現(xiàn)的模塊沒(méi)有關(guān)系,這樣也符合了上面介紹的開(kāi)閉原則。這樣實(shí)現(xiàn)起來(lái)每個(gè)模塊只依賴對(duì)象的接口,而和其他模塊沒(méi)關(guān)系,依賴關(guān)系單一。系統(tǒng)容易擴(kuò)展和維護(hù)。
  • 業(yè)務(wù)邏輯層也是一樣,只需要參考抽象層的接口來(lái)實(shí)現(xiàn)業(yè)務(wù)就好了,抽象層暴露出來(lái)的接口就是我們業(yè)務(wù)層可以使用的方法,然后可以通過(guò)多態(tài)的方向,接口指針指向哪個(gè)實(shí)現(xiàn)模塊,調(diào)用了就是具體的實(shí)現(xiàn)方法,這樣我們業(yè)務(wù)邏輯層也是依賴抽象成編程。

看看具體的實(shí)現(xiàn)

package yldz

import "fmt"

type Car interface {
    Run() string
}

type Driver interface {
    // 接口變量肚子里有一個(gè)指針, 所以接口變量不需要使用指針.
    Driver(car Car)
}


// 奔馳車
type Benz struct {

}

func (b *Benz) Run() string{
    return fmt.Sprintf("奔馳啟動(dòng)")
}

// 寶馬車
type BM struct {

}

func (b *BM) Run() string{
    return fmt.Sprintf("寶馬啟動(dòng)")
}

// 豐田車
type FT struct {

}

func (t *FT) Run() string{
    return fmt.Sprintf("豐田啟動(dòng)")
}

// ====張三
type Zhangsan struct {

}

func (t *Zhangsan) Driver(car Car) {
    fmt.Println("駕駛",car.Run())
}
func main() {

    benz := yldz.Benz{}
    zs := yldz.Zhangsan{}
    zs.Driver(benz)

    ft := yldz.FT{}
    zs.Driver(ft)
}

以上就是分析Go語(yǔ)言接口的設(shè)計(jì)原則的詳細(xì)內(nèi)容,更多關(guān)于Go 接口的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

您可能感興趣的文章:
  • 一篇文章帶你玩轉(zhuǎn)go語(yǔ)言的接口
  • Go語(yǔ)言-為什么返回值為接口類型,卻返回結(jié)構(gòu)體
  • go語(yǔ)言實(shí)現(xiàn)接口查詢
  • GO語(yǔ)言gin框架實(shí)現(xiàn)管理員認(rèn)證登陸接口
  • Go語(yǔ)言使用swagger生成接口文檔的方法
  • Go語(yǔ)言的接口詳解

標(biāo)簽:欽州 雞西 銅川 汕頭 梅河口 蘭州 重慶 吐魯番

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《分析Go語(yǔ)言接口的設(shè)計(jì)原則》,本文關(guān)鍵詞  分析,語(yǔ)言,接口,的,設(shè)計(jì),;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《分析Go語(yǔ)言接口的設(shè)計(jì)原則》相關(guān)的同類信息!
  • 本頁(yè)收集關(guān)于分析Go語(yǔ)言接口的設(shè)計(jì)原則的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章