1. 程式人生 > 實用技巧 >InterfaceDesign介面設計原則

InterfaceDesign介面設計原則

目錄

InterfaceDesign介面設計原則

介面設計真配上設計模式,真香,但是我書落學校了(所以,以後再學設計模式)

雖然我書落在學校了,但是不影響我百度啊。百度一下介面設計原則是啥樣的

介面設計6個原則(原理部分)

軟體設計的最高目標: 高內聚,低耦合

單一職責原則

Single Responsibility Principle, 簡稱SRP。

定義:There should never be more than one reason for a class to change.

應該有且僅有一個原因引起類的變更。

職責的劃分?單一的定義和級別?

應該根據實際業務情況而定。關注變化點。

實際使用時,類很難做到職責單一,但是介面的職責應該儘量單一。

里氏替換原則

Liskov Substitution Principle, 簡稱LSP。

定義:Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.

(所有引用基類的地方必須能透明地使用其子類的物件)

里氏替換原則為良好的繼承定義了一個規範:

1.子類必須完全實現父類的方法

2.子類可以有自己的個性(屬性和方法)。

3.覆蓋或實現父類的方法時輸入引數可以被放大。

4.覆寫或實現父類的方法時輸出結果可以被縮小。

注:在類中呼叫其他類時務必要使用父類或介面,如果不能使用父類或介面,則說明類的設計已經違背了LSP原則。

依賴倒置原則

Dependence Inversion Principle, 簡稱DIP

定義:High level modules should not depend upon low level modules.Both should depend upon abstractions.Abstractions should not depend upon details.Details should depend upon abstractions.

翻譯過來,包含三層含義:

1.高層模組不應該依賴低層模組,兩者都應該依賴其抽象。

2.抽象不應該依賴細節。

3.細節應該依賴抽象。

精簡的定義: 面向介面程式設計。

Test-Driven Development 測試驅動開發是依賴倒置原則的最好體現。

測試驅動開發要求先寫測試類,測試通過才寫實現類,這就要求你要先想介面定義。

依賴的三種寫法:

1.建構函式傳遞依賴物件。

2.Setter方法傳遞依賴物件。

3.介面宣告依賴物件。

最佳實踐:
1.每個類儘量都有介面或抽象類,或者抽象類和介面兩者都具備。
2.變數的表面型別儘量是介面或抽象類。
3.任何類都不應該從具體類派生。
4.儘量不要覆寫基類的方法。
5.結合里氏替換原則使用。

介面隔離原則

介面--這裡指用interface關鍵字定義的介面。
定義:
1.Clients should not be forced to depend upon interfaces that they don't use.(客戶端不應該依賴它不需要的介面)
2.The dependency of one class to anther one should depend on the smallest possible interface.(類間的依賴關係應該建立在最小的介面上)

概括:建立單一介面,不要建立臃腫龐大的介面。

通俗來講:介面儘量細化,同時介面中的方法儘量少。

如何細化?細化到什麼程式?

沒有統一的標準,應根據業務合理細分,適合業務才是重點。

保證介面的純結性:
1.介面要儘量小。
2.介面要高內聚。
3.定製服務。
4.介面的設計是有限度的。

最佳實踐:
1.一個介面只服務於一個子模組或業務邏輯。
2.通過業務邏輯壓縮介面中的public方法,介面時常去回顧,儘量讓介面達到“滿身筋骨肉”,而不是“肥嘟嘟”的一大堆方法。
3.已經被汙染了的介面,儘量去修改,若變更的風險較大,則採用介面卡模式進行轉化處理。
4.瞭解環境,拒絕盲從。每個專案或產品都有特定的環境因素,不要盲從大師的設計,要根據業務邏輯進行最好的介面設計。

迪米特法則

Law of Demeter, LOD。又稱最少知識原則(Least Knowledge Principle, LKP)。
通俗來講:一個類應該對自己需要耦合或呼叫的類知道得最少,你(被耦合或呼叫的類)的內部是如何複雜都和我沒有關係,那是你的事情,我就呼叫你提供的public方法,其他一概不關心。

低耦合要求:
1.只和朋友交流
朋友類:出現在成員變數、方法的輸入輸出引數中的類。方法體內部的類不屬於朋友類。
2.朋友間也是有距離的
迪米特法則要求類“羞澀”一點,儘量不要對外公佈太多的public方法和非靜態的public變數,儘量內斂,多使用private、package-private、protected等訪問許可權。
3.是自己的就是自己的
如果一個方法放在本類中,既不增加類間關係,也對本類不產生負面影響,就放置在本類中。
4.謹慎使用Serializable

開閉原則

Software entities like classes, modules and functions should be open for extension but closed for modifications.(一個軟體實體如類、模組和函式應該對擴充套件開放,對修改關閉)

軟體實體包括以下幾個部分:
1.專案和軟體產品中按照一定的邏輯規則劃分的模組。
2.抽象和類。
3.方法。

變化的三種類型:
1.邏輯變化
2.子模組變化
3.可見檢視變化

Golang實現

多型(這個不是原則,是基礎)

  • 蘋果手機能打電話
  • 華為手機能打電話
  • 他倆都是手機,所以出現了手機介面,下面是多型
package main

import "fmt"

type Phone interface {
	Call()
}

// 蘋果手機
type PingGuo struct {
	Phone
	Name string
	Color string
}

func (pg *PingGuo) Call() {
	fmt.Println(fmt.Sprintf("%s型號%s顏色的蘋果正在打電話", pg.Name, pg.Color))
}

func NewPingGuo(n string, c string) *PingGuo {
	return &PingGuo{
		Name:  n,
		Color: c,
	}
}

// 華為機
type HuaWei struct {
	Phone
	Name string
	Color string
}

func (hw *HuaWei) Call() {
	fmt.Println(fmt.Sprintf("%s型號%s顏色的蘋果正在打電話", hw.Name, hw.Color))
}

func NewHuaWei(n string, c string) *HuaWei {
	return &HuaWei{
		Name:  n,
		Color: c,
	}
}

func main() {
	var (
		p Phone
	)
	pg := NewPingGuo("6plus+", "black")
	p = pg
	p.Call()

	hw := NewHuaWei("榮耀100", "白色")
	p = hw
	p.Call()
}

#輸出
6plus+型號black顏色的蘋果正在打電話
榮耀100型號白色顏色的蘋果正在打電話
優化
package main

import "fmt"

type Phone interface {
	Call()
}

// 蘋果手機
type PingGuo struct {
	Phone
	Name string
	Color string
}

func (pg *PingGuo) Call() {
	fmt.Println(fmt.Sprintf("%s型號%s顏色的蘋果正在打電話", pg.Name, pg.Color))
}

func NewPingGuo(n string, c string) *PingGuo {
	return &PingGuo{
		Name:  n,
		Color: c,
	}
}

// 華為機
type HuaWei struct {
	Phone
	Name string
	Color string
}

func (hw *HuaWei) Call() {
	fmt.Println(fmt.Sprintf("%s型號%s顏色的蘋果正在打電話", hw.Name, hw.Color))
}

func NewHuaWei(n string, c string) *HuaWei {
	return &HuaWei{
		Name:  n,
		Color: c,
	}
}

// 這是優化額的地方,很好的封裝了
func Work(p Phone) {
	p.Call()
}

func main() {
	pg := NewPingGuo("6plus+", "black")
	Work(pg)

	hw := NewHuaWei("榮耀100", "白色")
	Work(hw)
}

開閉原則

為了業務的拓展

  • 首先毛哥開了一個商店,這個商店有很多員工(這個例子和上邊差不多)
  • 修車員工,做修車工作
  • 客服,負責騷擾使用者
  • 看起來上邊是廢話,但是不是,為了店鋪方便管理,職責劃分
  • 為了以後可以加業務啥的,好整
package main

import "fmt"

type Shop interface {
	Busy()
}

// 修車員
type RepairCarer struct {
	Shop
	Name string
}

func (rc *RepairCarer) Busy() {
	fmt.Println(fmt.Sprintf("我是修車員%s, 忙於業務修車", rc.Name))
}

func NewRepairCarer(n string) *RepairCarer {
	return &RepairCarer{
		Name: n,
	}
}

// 客服人員
type Servicer struct {
	Shop
	Name string
}

func (s *Servicer) Busy() {
	fmt.Println(fmt.Sprintf("我是客服%s, 忙於業務騷擾使用者", s.Name))
}

func NewServicer(n string) *Servicer {
	return &Servicer{
		Name: n,
	}
}

// 下面可以無限推展

// 封裝統一工作
func ShopWork(s Shop) {
	s.Busy()
}

func main() {
	fmt.Println("歡迎來到毛哥開的店鋪,目前店鋪比較小,以後大了,可以拓展業務")
	repairCarer := NewRepairCarer("郭德綱")
	servicer := NewServicer("林志玲")

	ShopWork(repairCarer)
	ShopWork(servicer)
}

# 輸出
歡迎來到毛哥開的店鋪,目前店鋪比較小,以後大了,可以拓展業務
我是修車員郭德綱, 忙於業務修車
我是客服林志玲, 忙於業務騷擾使用者

依賴倒置原則

  • 假設有倆司機,one,two
  • 假設有倆車,bmw, aodi
  • 這司機隨便開車,假如one想開bmw,就需要寫個方法,如果是想開奧迪呢?還需要寫
  • 如果車多了,人也多了,就會亂套
  • 所以,需要抽象司機和車
package main

import "fmt"

// 抽象司機介面
type Person interface {
	Drive(car Car)
}

// 抽象車的介面
type Car interface {
	Run()
}

// 司機們
type MaoOne struct {
     Person
}

func (mo *MaoOne) Drive(car Car) {
	fmt.Println("maoone開始開車")
	car.Run()
}

func NewMaoOne() *MaoOne {
	return &MaoOne{}
}

type MaoTwo struct {
	Person
}

func (mo *MaoTwo) Drive(car Car) {
	fmt.Println("maotwo開始開車")
	car.Run()
}

func NewMaoTwo() *MaoTwo {
	return &MaoTwo{}
}

// 車
type Bmw struct {
	Car
}

func (b *Bmw) Run() {
	fmt.Println("寶馬開始啟動")
}

func NewBmw() *Bmw {
	return &Bmw{}
}

type AoDi struct {
	Car
}

func (ad *AoDi) Run() {
	fmt.Println("奧迪開始啟動")
}

func NewAoDi() *AoDi {
	return &AoDi{}
}

func Work(p Person, car Car) {
	p.Drive(car)
}

func main() {
	// 初始化司機
	one := NewMaoOne()
	two := NewMaoTwo()

	// 初始化汽車
	bmw := NewBmw()
	ad := NewAoDi()

	// 隨便開
	Work(one, bmw)
	Work(one, ad)
	Work(two, bmw)
	Work(two, ad)
	
	fmt.Println("如果司機和車多,可以隨意組合")
}

#輸出
maoone開始開車
寶馬開始啟動
maoone開始開車
奧迪開始啟動
maotwo開始開車
寶馬開始啟動
maotwo開始開車
奧迪開始啟動
如果司機和車多,可以隨意組合