控制反轉(zhuǎn)、依賴反轉(zhuǎn)、依賴注入、KISS、YAGNI原則

** 依賴**

public class A {
  private B b;
  public A(B b) {
    this.b = b;
  }
}

public class A {
  private B b;
  public A() {
    this.b = new B();
  }
}

public class A {
  public void func(B b) { ... }
}

控制反轉(zhuǎn) IOC (Inversion Of Control)

框架提供了一個(gè)可擴(kuò)展的代碼骨架,用來組裝對(duì)象、管理整個(gè)執(zhí)行流程。程序員利用框架進(jìn)行開發(fā)的時(shí)候,只需要往預(yù)留的擴(kuò)展點(diǎn)上,添加跟自己業(yè)務(wù)相關(guān)的代碼,就可以利用框架來驅(qū)動(dòng)整個(gè)程序流程的執(zhí)行。

依賴注入 DI (Dependency Injection)

把有依賴關(guān)系的類放到容器中,解析出這些類的實(shí)例,就是依賴注入。目的是實(shí)現(xiàn)類的解耦。

依賴倒置 DIP (Dependence Inversion Principle)

是指設(shè)計(jì)代碼結(jié)構(gòu)時(shí),高層模塊不應(yīng)該依賴低層模塊,二者都應(yīng)該依賴其抽象。
抽象不應(yīng)該依賴細(xì)節(jié),細(xì)節(jié)應(yīng)該依賴抽象。通過依賴倒置,可以減少類與類之間的耦合性,提高系統(tǒng)的穩(wěn)定性,提高代碼的可讀性和可維護(hù)性,并且能夠降低修改程序所造成的風(fēng)險(xiǎn)。

// 重生之小明 - 進(jìn)擊碼農(nóng)

// MARK: **- 小明和他的手機(jī)**

class Persion {
    
}

//從前有個(gè)人叫小明小明有三大愛好,逛知乎、玩王者農(nóng)藥和搶微信紅包

class XiaoMing: Persion {

    private let name: String = "小明"
    private let age: Int = 22

    func read() {
        //逛知乎

    }

    func  play(){
        //玩農(nóng)藥
    }

    func  grab() {
        //搶紅包
    }

}

// 但是,小明作為一個(gè)人類,沒有辦法僅靠自己就能實(shí)現(xiàn)以上的功能,他必須依賴一部手機(jī),

class IPhone6 {
    
    func read(name: String) {
        print("\(name)打開了知乎然后編了一個(gè)故事")
    }

    func play(name: String) {
        print("\(name)打開了王者農(nóng)藥并送起了人頭")
    }

    func grab(name: String){
        print("\(name)開始搶紅包卻只搶不發(fā)")
    }
}

// 小明非常珍惜自己的新手機(jī),每天把它牢牢控制在手心里,所以小明變成了這個(gè)樣子

class XiaoMing: Persion {

    private let name: String = "小明"
    private let age: Int = 22

    func read() {
        //逛知乎
        let iphone6 = IPhone6()
        iphone6.read(name: self.name)

    }

    func  play(){
        //玩農(nóng)藥
        let iphone6 = IPhone6()
        iphone6.grab(name: self.name)
    }

    func  grab() {
        //搶紅包
        let iphone6 = IPhone6()
        iphone6.grab(name: self.name)
    }

}

// 今天是周六,小明不用上班,于是他起床,并依次逛起了知乎,玩王者農(nóng)藥,并搶了個(gè)紅包。

let xiaoming = XiaoMing()
xiaoming.read()
xiaoming.play()
xiaoming.grab()


// 這個(gè)時(shí)候,我們可以在命令行里看到輸出如下

/*
 小明打開了知乎然后編了一個(gè)故事
 小明打開了王者農(nóng)藥并送起了人頭
 小明開始搶紅包卻只搶不發(fā)
 */
//這一天,小明過得很充實(shí),他覺得自己是世界上最幸福的人。

// MARK: -  第二章: 小明的快樂與憂傷

/*
 
 小明和他的手機(jī)曾一起度過了一段美好的時(shí)光,一到空閑時(shí)刻,他就抱著手機(jī),逛知乎,刷微博,玩游戲,他覺得自己根本不需要女朋友,只要有手機(jī)在身邊,就滿足了。可誰能想到,一次次地系統(tǒng)更新徹底打碎了他的夢(mèng)想,他的手機(jī)變得越來越卡頓,電池的使用壽命也越來越短,一直到某一天的寒風(fēng)中,他的手機(jī)終于耐不住寒冷,頭也不回地關(guān)了機(jī)。小明很憂傷,他意識(shí)到,自己要換手機(jī)了。為了能獲得更好的使用體驗(yàn),小明一咬牙,剁手了一臺(tái)iphoneX,這部手機(jī)鈴聲很大,電量很足,還能雙卡雙待,小明很喜歡,但是他遇到一個(gè)問題,就是他之前過度依賴了原來那一部iPhone6,他們之間已經(jīng)深深耦合在一起了,如果要換手機(jī),他就要拿起刀來改造自己,把自己體內(nèi)所有方法中的iphone6 都換成 iphoneX。

 漫長的改造過程經(jīng)歷了漫長的改造過程,小明終于把代碼中的 iphone6 全部換成了 iphoneX。雖然很辛苦,但是小明覺得他是快樂的。
*/
class IphoneX {

    func read(name: String) {
        print("\(name)打開了知乎然后編了一個(gè)故事")
    }

    func play(name: String) {
        print("\(name)打開了王者農(nóng)藥并送起了人頭")
    }

    func grab(name: String){
        print("\(name)開始搶紅包卻只搶不發(fā)")
    }

    func isBroken() -> Bool {
        return true
    }
}

class XiaoMing: Persion {

    private let name: String = "小明"
    private let age: Int = 22

    func read() {
        //逛知乎
        let iphoneX = IphoneX()
        iphone6.read(name: self.name)

    }

    func  play(){
        //玩農(nóng)藥
        let iphoneX = IphoneX()
        iphone6.grab(name: self.name)
    }

    func  grab() {
        //搶紅包
        let iphoneX = IphoneX()
        iphone6.grab(name: self.name)
    }
}

/*
 于是小明開開心心地帶著手機(jī)去上班了,并在回來的路上被小偷偷走了。為了應(yīng)急,小明只好重新使用那部剛剛被遺棄的iphone6,但是一想到那漫長的改造過程,小明的心里就說不出的委屈,他覺得自己過于依賴手機(jī)了,為什么每次手機(jī)出什么問題他都要去改造他自己,這不僅僅是過度耦合,簡直是本末倒置,他向天空大喊,我不要再控制我的手機(jī)了。

 天空中的造物主,也就是作為程序員的我,聽到了他的吶喊,我告訴他,你不用再控制你的手機(jī)了,交給我來管理,把控制權(quán)交給我。這就叫做控制反轉(zhuǎn)。
 
 */


// MARK: - 第三章:造物主的智慧

/*
 
 小明聽到了我的話,他既高興,又有一點(diǎn)害怕,他跪下來磕了幾個(gè)頭,虔誠地說到:“原來您就是傳說中的造物主,巴格梅克上神。我聽到您剛剛說了 控制反轉(zhuǎn) 四個(gè)字,就是把手機(jī)的控制權(quán)從我的手里交給你,但這只是您的想法,是一種思想罷了,要用什么辦法才能實(shí)現(xiàn)控制反轉(zhuǎn),又可以讓我繼續(xù)使用手機(jī)呢?”

 “呵“,身為造物主的我在表現(xiàn)完不屑以后,扔下了八個(gè)大字,“ 依賴注入,依賴倒置!”

 接下來,偉大的我開始對(duì)小明進(jìn)行慘無人道的改造,如下
 
 */

protocol Iphone {
    func read(name: String)
    func play(name: String)
    func grab(name: String)
    func isBroken() -> Bool
}

class IPhone6: Iphone {

    func isBroken() -> Bool {
        return false
    }

    func read(name: String) {
        print("\(name)打開了知乎然后編了一個(gè)故事")
    }

    func play(name: String) {
        print("\(name)打開了王者農(nóng)藥并送起了人頭")
    }

    func grab(name: String){
        print("\(name)開始搶紅包卻只搶不發(fā)")
    }
}

class IphoneX: Iphone {

    func read(name: String) {
        print("\(name)打開了知乎然后編了一個(gè)故事")
    }

    func play(name: String) {
        print("\(name)打開了王者農(nóng)藥并送起了人頭")
    }

    func grab(name: String){
        print("\(name)開始搶紅包卻只搶不發(fā)")
    }

    func isBroken() -> Bool {
        return true
    }
}

class XiaoMing: Persion {

    private let name: String = "小明"
    private let age: Int = 22
    private var iphone: Iphone? = nil

    init(phone: Iphone) {
        self.iphone = phone
    }

    func read() {
        //逛知乎
        iphone?.read(name: self.name)

    }

    func  play(){
        //玩農(nóng)藥
        iphone?.grab(name: self.name)
    }

    func  grab() {
        //搶紅包

        iphone?.grab(name: self.name)
    }
}


// MARK: - 第四章:小明的感悟
//小明的生活開始變得簡單了起來,而他把省出來的時(shí)間都用來寫筆記了,他在筆記本上這樣寫到

/**
 
 我曾經(jīng)有很強(qiáng)的控制欲,過度依賴于我的手機(jī),導(dǎo)致我和手機(jī)之間耦合程度太高,只要手機(jī)出現(xiàn)一點(diǎn)點(diǎn)問題,我都要改造我自己,這實(shí)在是既浪費(fèi)時(shí)間又容易出問題。自從我把控制權(quán)交給了造物主,他每天在喚醒我以前,就已經(jīng)替我選好了手機(jī),我只要按照平時(shí)一樣玩手機(jī)就可以了,根本不用關(guān)心是什么手機(jī)。即便手機(jī)出了問題,也可以由造物主直接搞定,不需要再改造我自己了,我現(xiàn)在買了七部手機(jī),都交給了造物主,每天換一部,美滋滋!我也從其中獲得了這樣的感悟: 如果一個(gè)類A 的功能實(shí)現(xiàn)需要借助于類B,那么就稱類B是類A的依賴,如果在類A的內(nèi)部去實(shí)例化類B,那么兩者之間會(huì)出現(xiàn)較高的耦合,一旦類B出現(xiàn)了問題,類A也需要進(jìn)行改造,如果這樣的情況較多,每個(gè)類之間都有很多依賴,那么就會(huì)出現(xiàn)牽一發(fā)而動(dòng)全身的情況,程序會(huì)極難維護(hù),并且很容易出現(xiàn)問題。要解決這個(gè)問題,就要把A類對(duì)B類的控制權(quán)抽離出來,交給一個(gè)第三方去做,把控制權(quán)反轉(zhuǎn)給第三方,就稱作控制反轉(zhuǎn)(IOC Inversion Of Control)??刂品崔D(zhuǎn)是一種思想,是能夠解決問題的一種可能的結(jié)果,而依賴倒置 (Dependence Inversion Principle)把控制權(quán)抽離出來,而依賴注入(Dependency Injection)就是其最典型的實(shí)現(xiàn)方法 。由第三方(我們稱作IOC容器)來控制依賴,把他通過構(gòu)造函數(shù)、屬性或者工廠模式等方法,注入到類A內(nèi),這樣就極大程度的對(duì)類A和類B進(jìn)行了解耦。
 
 */


KISS (Keep it Simple and Stupid) 怎么做

讓代碼盡可能簡單,目的是保持代碼可讀和可維護(hù)性

  • 代碼行數(shù)越少就越“簡單”嗎?
// 第一種實(shí)現(xiàn)方式: 使用正則表達(dá)式
public boolean isValidIpAddressV1(String ipAddress) {
  if (StringUtils.isBlank(ipAddress)) return false;
  String regex = "^(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[1-9])\\."
          + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."
          + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."
          + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)$";
  return ipAddress.matches(regex);
}
// 第二種實(shí)現(xiàn)方式: 使用現(xiàn)成的工具類
public boolean isValidIpAddressV2(String ipAddress) {
  if (StringUtils.isBlank(ipAddress)) return false;
  String[] ipUnits = StringUtils.split(ipAddress, '.');
  if (ipUnits.length != 4) {
    return false;
  }
  for (int i = 0; i < 4; ++i) {
    int ipUnitIntValue;
    try {
      ipUnitIntValue = Integer.parseInt(ipUnits[i]);
    } catch (NumberFormatException e) {
      return false;
    }
    if (ipUnitIntValue < 0 || ipUnitIntValue > 255) {
      return false;
    }
    if (i == 0 && ipUnitIntValue == 0) {
      return false;
    }
  }
  return true;
}
// 第三種實(shí)現(xiàn)方式: 不使用任何工具類
public boolean isValidIpAddressV3(String ipAddress) {
  char[] ipChars = ipAddress.toCharArray();
  int length = ipChars.length;
  int ipUnitIntValue = -1;
  boolean isFirstUnit = true;
  int unitsCount = 0;
  for (int i = 0; i < length; ++i) {
    char c = ipChars[i];
    if (c == '.') {
      if (ipUnitIntValue < 0 || ipUnitIntValue > 255) return false;
      if (isFirstUnit && ipUnitIntValue == 0) return false;
      if (isFirstUnit) isFirstUnit = false;
      ipUnitIntValue = -1;
      unitsCount++;
      continue;
    }
    if (c < '0' || c > '9') {
      return false;
    }
    if (ipUnitIntValue == -1) ipUnitIntValue = 0;
    ipUnitIntValue = ipUnitIntValue * 10 + (c - '0');
  }
  if (ipUnitIntValue < 0 || ipUnitIntValue > 255) return false;
  if (unitsCount != 3) return false;
  return true;
}

代碼邏輯復(fù)雜就違背 KISS 原則嗎?

/**
剛剛我們提到,并不是代碼行數(shù)越少就越“簡單”,還要考慮邏輯復(fù)雜度、實(shí)現(xiàn)難度、代碼的可讀性等。那如果一段代碼的邏輯復(fù)雜、實(shí)現(xiàn)難度大、可讀性也不太好,是不是就一定違背 KISS 原則呢?
*/

// KMP algorithm: a, b 分別是主串和模式串;n, m 分別是主串和模式串的長度。
public static int kmp(char[] a, int n, char[] b, int m) {
  int[] next = getNexts(b, m);
  int j = 0;
  for (int i = 0; i < n; ++i) {
    while (j > 0 && a[i] != b[j]) { // 一直找到 a[i] 和 b[j]
      j = next[j - 1] + 1;
    }
    if (a[i] == b[j]) {
      ++j;
    }
    if (j == m) { // 找到匹配模式串的了
      return i - m + 1;
    }
  }
  return -1;
}
// b 表示模式串,m 表示模式串的長度
private static int[] getNexts(char[] b, int m) {
  int[] next = new int[m];
  next[0] = -1;
  int k = -1;
  for (int i = 1; i < m; ++i) {
    while (k != -1 && b[k + 1] != b[i]) {
      k = next[k];
    }
    if (b[k + 1] == b[i]) {
      ++k;
    }
    next[i] = k;
  }
  return next;
}
/**
KMP 算法以快速高效著稱。當(dāng)我們需要處理長文本字符串匹配問題(幾百 MB 大小文本內(nèi)容的匹配),或者字符串匹配是某個(gè)產(chǎn)品的核心功能(比如 Vim、Word 等文本編輯器),又或者字符串匹配算法是系統(tǒng)性能瓶頸的時(shí)候,我們就應(yīng)該選擇盡可能高效的 KMP 算法。而 KMP 算法本身具有邏輯復(fù)雜、實(shí)現(xiàn)難度大、可讀性差的特點(diǎn)。本身就復(fù)雜的問題,用復(fù)雜的方法解決,并不違背 KISS 原則。
不過,平時(shí)的項(xiàng)目開發(fā)中涉及的字符串匹配問題,大部分都是針對(duì)比較小的文本。在這種情況下,直接調(diào)用編程語言提供的現(xiàn)成的字符串匹配函數(shù)就足夠了。如果非得用 KMP 算法、BM 算法來實(shí)現(xiàn)字符串匹配,那就真的違背 KISS 原則了。也就是說,同樣的代碼,在某個(gè)業(yè)務(wù)場景下滿足 KISS 原則,換一個(gè)應(yīng)用場景可能就不滿足了。
*/

如何寫出滿足 KISS 原則的代碼?

不要使用同事可能不懂的技術(shù)來實(shí)現(xiàn)代碼;
不要重復(fù)造輪子,要善于使用已經(jīng)有的工具類庫;
不要過度優(yōu)化。

YAGNI (You Ain’t Gonna Need It) 要不要做

不要做過度設(shè)計(jì)

  • 無用功能的設(shè)計(jì)
  • 無用第三方庫的導(dǎo)入
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容