美文网首页
控制反转、依赖反转、依赖注入、KISS、YAGNI原则

控制反转、依赖反转、依赖注入、KISS、YAGNI原则

作者: Yasuo_4a5f | 来源:发表于2021-01-29 16:28 被阅读0次

    ** 依赖**

    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) { ... }
    }
    

    控制反转 IOC (Inversion Of Control)

    框架提供了一个可扩展的代码骨架,用来组装对象、管理整个执行流程。程序员利用框架进行开发的时候,只需要往预留的扩展点上,添加跟自己业务相关的代码,就可以利用框架来驱动整个程序流程的执行。

    依赖注入 DI (Dependency Injection)

    把有依赖关系的类放到容器中,解析出这些类的实例,就是依赖注入。目的是实现类的解耦。

    依赖倒置 DIP (Dependence Inversion Principle)

    是指设计代码结构时,高层模块不应该依赖低层模块,二者都应该依赖其抽象。
    抽象不应该依赖细节,细节应该依赖抽象。通过依赖倒置,可以减少类与类之间的耦合性,提高系统的稳定性,提高代码的可读性和可维护性,并且能够降低修改程序所造成的风险。

    // 重生之小明 - 进击码农
    
    // MARK: **- 小明和他的手机**
    
    class Persion {
        
    }
    
    //从前有个人叫小明小明有三大爱好,逛知乎、玩王者农药和抢微信红包
    
    class XiaoMing: Persion {
    
        private let name: String = "小明"
        private let age: Int = 22
    
        func read() {
            //逛知乎
    
        }
    
        func  play(){
            //玩农药
        }
    
        func  grab() {
            //抢红包
        }
    
    }
    
    // 但是,小明作为一个人类,没有办法仅靠自己就能实现以上的功能,他必须依赖一部手机,
    
    class IPhone6 {
        
        func read(name: String) {
            print("\(name)打开了知乎然后编了一个故事")
        }
    
        func play(name: String) {
            print("\(name)打开了王者农药并送起了人头")
        }
    
        func grab(name: String){
            print("\(name)开始抢红包却只抢不发")
        }
    }
    
    // 小明非常珍惜自己的新手机,每天把它牢牢控制在手心里,所以小明变成了这个样子
    
    class XiaoMing: Persion {
    
        private let name: String = "小明"
        private let age: Int = 22
    
        func read() {
            //逛知乎
            let iphone6 = IPhone6()
            iphone6.read(name: self.name)
    
        }
    
        func  play(){
            //玩农药
            let iphone6 = IPhone6()
            iphone6.grab(name: self.name)
        }
    
        func  grab() {
            //抢红包
            let iphone6 = IPhone6()
            iphone6.grab(name: self.name)
        }
    
    }
    
    // 今天是周六,小明不用上班,于是他起床,并依次逛起了知乎,玩王者农药,并抢了个红包。
    
    let xiaoming = XiaoMing()
    xiaoming.read()
    xiaoming.play()
    xiaoming.grab()
    
    
    // 这个时候,我们可以在命令行里看到输出如下
    
    /*
     小明打开了知乎然后编了一个故事
     小明打开了王者农药并送起了人头
     小明开始抢红包却只抢不发
     */
    //这一天,小明过得很充实,他觉得自己是世界上最幸福的人。
    
    // MARK: -  第二章: 小明的快乐与忧伤
    
    /*
     
     小明和他的手机曾一起度过了一段美好的时光,一到空闲时刻,他就抱着手机,逛知乎,刷微博,玩游戏,他觉得自己根本不需要女朋友,只要有手机在身边,就满足了。可谁能想到,一次次地系统更新彻底打碎了他的梦想,他的手机变得越来越卡顿,电池的使用寿命也越来越短,一直到某一天的寒风中,他的手机终于耐不住寒冷,头也不回地关了机。小明很忧伤,他意识到,自己要换手机了。为了能获得更好的使用体验,小明一咬牙,剁手了一台iphoneX,这部手机铃声很大,电量很足,还能双卡双待,小明很喜欢,但是他遇到一个问题,就是他之前过度依赖了原来那一部iPhone6,他们之间已经深深耦合在一起了,如果要换手机,他就要拿起刀来改造自己,把自己体内所有方法中的iphone6 都换成 iphoneX。
    
     漫长的改造过程经历了漫长的改造过程,小明终于把代码中的 iphone6 全部换成了 iphoneX。虽然很辛苦,但是小明觉得他是快乐的。
    */
    class IphoneX {
    
        func read(name: String) {
            print("\(name)打开了知乎然后编了一个故事")
        }
    
        func play(name: String) {
            print("\(name)打开了王者农药并送起了人头")
        }
    
        func grab(name: String){
            print("\(name)开始抢红包却只抢不发")
        }
    
        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(){
            //玩农药
            let iphoneX = IphoneX()
            iphone6.grab(name: self.name)
        }
    
        func  grab() {
            //抢红包
            let iphoneX = IphoneX()
            iphone6.grab(name: self.name)
        }
    }
    
    /*
     于是小明开开心心地带着手机去上班了,并在回来的路上被小偷偷走了。为了应急,小明只好重新使用那部刚刚被遗弃的iphone6,但是一想到那漫长的改造过程,小明的心里就说不出的委屈,他觉得自己过于依赖手机了,为什么每次手机出什么问题他都要去改造他自己,这不仅仅是过度耦合,简直是本末倒置,他向天空大喊,我不要再控制我的手机了。
    
     天空中的造物主,也就是作为程序员的我,听到了他的呐喊,我告诉他,你不用再控制你的手机了,交给我来管理,把控制权交给我。这就叫做控制反转。
     
     */
    
    
    // MARK: - 第三章:造物主的智慧
    
    /*
     
     小明听到了我的话,他既高兴,又有一点害怕,他跪下来磕了几个头,虔诚地说到:“原来您就是传说中的造物主,巴格梅克上神。我听到您刚刚说了 控制反转 四个字,就是把手机的控制权从我的手里交给你,但这只是您的想法,是一种思想罢了,要用什么办法才能实现控制反转,又可以让我继续使用手机呢?”
    
     “呵“,身为造物主的我在表现完不屑以后,扔下了八个大字,“ 依赖注入,依赖倒置!”
    
     接下来,伟大的我开始对小明进行惨无人道的改造,如下
     
     */
    
    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)打开了知乎然后编了一个故事")
        }
    
        func play(name: String) {
            print("\(name)打开了王者农药并送起了人头")
        }
    
        func grab(name: String){
            print("\(name)开始抢红包却只抢不发")
        }
    }
    
    class IphoneX: Iphone {
    
        func read(name: String) {
            print("\(name)打开了知乎然后编了一个故事")
        }
    
        func play(name: String) {
            print("\(name)打开了王者农药并送起了人头")
        }
    
        func grab(name: String){
            print("\(name)开始抢红包却只抢不发")
        }
    
        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(){
            //玩农药
            iphone?.grab(name: self.name)
        }
    
        func  grab() {
            //抢红包
    
            iphone?.grab(name: self.name)
        }
    }
    
    
    // MARK: - 第四章:小明的感悟
    //小明的生活开始变得简单了起来,而他把省出来的时间都用来写笔记了,他在笔记本上这样写到
    
    /**
     
     我曾经有很强的控制欲,过度依赖于我的手机,导致我和手机之间耦合程度太高,只要手机出现一点点问题,我都要改造我自己,这实在是既浪费时间又容易出问题。自从我把控制权交给了造物主,他每天在唤醒我以前,就已经替我选好了手机,我只要按照平时一样玩手机就可以了,根本不用关心是什么手机。即便手机出了问题,也可以由造物主直接搞定,不需要再改造我自己了,我现在买了七部手机,都交给了造物主,每天换一部,美滋滋!我也从其中获得了这样的感悟: 如果一个类A 的功能实现需要借助于类B,那么就称类B是类A的依赖,如果在类A的内部去实例化类B,那么两者之间会出现较高的耦合,一旦类B出现了问题,类A也需要进行改造,如果这样的情况较多,每个类之间都有很多依赖,那么就会出现牵一发而动全身的情况,程序会极难维护,并且很容易出现问题。要解决这个问题,就要把A类对B类的控制权抽离出来,交给一个第三方去做,把控制权反转给第三方,就称作控制反转(IOC Inversion Of Control)。控制反转是一种思想,是能够解决问题的一种可能的结果,而依赖倒置 (Dependence Inversion Principle)把控制权抽离出来,而依赖注入(Dependency Injection)就是其最典型的实现方法 。由第三方(我们称作IOC容器)来控制依赖,把他通过构造函数、属性或者工厂模式等方法,注入到类A内,这样就极大程度的对类A和类B进行了解耦。
     
     */
    
    
    

    KISS (Keep it Simple and Stupid) 怎么做

    让代码尽可能简单,目的是保持代码可读和可维护性

    • 代码行数越少就越“简单”吗?
    // 第一种实现方式: 使用正则表达式
    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);
    }
    // 第二种实现方式: 使用现成的工具类
    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;
    }
    // 第三种实现方式: 不使用任何工具类
    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;
    }
    

    代码逻辑复杂就违背 KISS 原则吗?

    /**
    刚刚我们提到,并不是代码行数越少就越“简单”,还要考虑逻辑复杂度、实现难度、代码的可读性等。那如果一段代码的逻辑复杂、实现难度大、可读性也不太好,是不是就一定违背 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 算法以快速高效著称。当我们需要处理长文本字符串匹配问题(几百 MB 大小文本内容的匹配),或者字符串匹配是某个产品的核心功能(比如 Vim、Word 等文本编辑器),又或者字符串匹配算法是系统性能瓶颈的时候,我们就应该选择尽可能高效的 KMP 算法。而 KMP 算法本身具有逻辑复杂、实现难度大、可读性差的特点。本身就复杂的问题,用复杂的方法解决,并不违背 KISS 原则。
    不过,平时的项目开发中涉及的字符串匹配问题,大部分都是针对比较小的文本。在这种情况下,直接调用编程语言提供的现成的字符串匹配函数就足够了。如果非得用 KMP 算法、BM 算法来实现字符串匹配,那就真的违背 KISS 原则了。也就是说,同样的代码,在某个业务场景下满足 KISS 原则,换一个应用场景可能就不满足了。
    */
    
    

    如何写出满足 KISS 原则的代码?

    不要使用同事可能不懂的技术来实现代码;
    不要重复造轮子,要善于使用已经有的工具类库;
    不要过度优化。

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

    不要做过度设计

    • 无用功能的设计
    • 无用第三方库的导入

    相关文章

      网友评论

          本文标题:控制反转、依赖反转、依赖注入、KISS、YAGNI原则

          本文链接:https://www.haomeiwen.com/subject/idaqtltx.html