2022-01-03 設(shè)計原則-依賴倒置與KISS峡碉、YAGNI原則總結(jié)

依賴倒置(DIP)

  1. 控制反轉(zhuǎn)實際上,控制反轉(zhuǎn)是一個比較籠統(tǒng)的設(shè)計思想驮审,并不是一種具體的實現(xiàn)方法鲫寄,一般用來指導(dǎo)框架層面的設(shè)計。這里所說的“控制”指的是對程序執(zhí)行流程的控制疯淫,而“反轉(zhuǎn)”指的是在沒有使用框架之前地来,程序員自己控制整個程序的執(zhí)行。在使用框架之后峡竣,整個程序的執(zhí)行流程通過框架來控制靠抑。流程的控制權(quán)從程序員“反轉(zhuǎn)”給了框架量九。

    public class UserServiceTest {
        public static boolean doTest(){
            return false;
        }
    
        //代碼流程都由程序員來控制
        public static void main(String[] args) {//注意:這部分邏輯可以放到框架中
            if(doTest()){
                System.err.println("Test successed!");
            }else {
                System.err.println("Test failed!");
            }
        }
    }
    
    /**
     * JunitApplication類
     *
     * 框架提供了一個可擴(kuò)展的代碼骨架适掰,用來組裝對象、管理整個執(zhí)行流程荠列。程序員利用框架進(jìn)行開發(fā)的時候类浪,
     * 只需要往預(yù)留的擴(kuò)展點上,添加跟自己業(yè)務(wù)相關(guān)的代碼肌似,就可以利用框架來驅(qū)動整個程序流程的執(zhí)行费就。
     *
     * 這里的“控制”指的是對程序執(zhí)行流程的控制,而“反轉(zhuǎn)”指的是在沒有使用框架之前川队,程序員自己控制整個
     * 程序的執(zhí)行力细。在使用框架之后,整個程序的執(zhí)行流程可以通過框架來控制固额。流程的控制權(quán)從程序員“反轉(zhuǎn)”到了框架眠蚂。
     *
     */
    public class JunitApplication {
    
        private static final List<TestCase> testCases = new ArrayList<>();
    
        public static void register(TestCase testCase){
            testCases.add(testCase);
        }
    
        //程序啟動入口
        public static final void main(String[] args) {
            for (TestCase testCase : testCases) {
                testCase.run();
            }
        }
    }
    
    public abstract class TestCase {
    
        public void run() {
            if (doTest()) {
                System.out.println("Test succeed.");
            }else{
                System.out.println("Test failed.");
            }
        }
        //框架預(yù)留的擴(kuò)展點
        public abstract boolean doTest();
    }
    
    public class IocUserServiceTest extends TestCase{
    
        @Override
        public boolean doTest() {
            return false;
        }
    
        public static void main(String[] args) {
            // 注冊操作還可以通過配置的方式來實現(xiàn),不需要程序員顯示調(diào)用register()
            JunitApplication.register(new IocUserServiceTest());
        }
    }
    
  2. 依賴注入依賴注入和控制反轉(zhuǎn)恰恰相反斗躏,它是一種具體的編碼技巧逝慧。我們不通過 new 的方式在類內(nèi)部創(chuàng)建依賴類的對象,而是將依賴的類對象在外部創(chuàng)建好之后,通過構(gòu)造函數(shù)笛臣、函數(shù)參數(shù)等方式傳遞(或注入)給類來使用云稚。

    public class Notification {
    
        private MessageSender messageSender;
    
        // 非依賴注入實現(xiàn)方式
        public Notification() {
            this.messageSender = new MessageSender();
        }
    
        public void sendMessage(String cellphone, String message){
            this.messageSender.send(cellphone,message);
        }
    }
    
    public class Notification {
    
        private MessageSender messageSender;
    
        //依賴注入實現(xiàn)方式,注入對象可以是抽象類也可以是接口
        //通過依賴注入的方式來將依賴的類對象傳遞進(jìn)來,這樣就提高了代碼的擴(kuò)展性沈堡,我們可以靈活地替換依賴的類,此處使用
        // 接口+組合方式替代繼承可以顯著提高代碼的擴(kuò)展性
        public Notification(MessageSender messageSender) {
            this.messageSender = messageSender;
        }
    
        public void sendMessage(String cellphone, String message) {
            this.messageSender.send(cellphone, message);
        }
    }
    
    public class Demo {
        public static void main(String[] args) {
            //Notification notification = new Notification();
            //notification.sendMessage("cellphone","非依賴注入實現(xiàn)方式");
    
            MessageSender messageSender = new MessageSender();
            Notification notification = new Notification(messageSender);
    
            notification.sendMessage("cellphone","依賴注入實現(xiàn)方式");
        }
    }
    
  3. 依賴注入框架我們通過依賴注入框架提供的擴(kuò)展點静陈,簡單配置一下所有需要的類及其類與類之間依賴關(guān)系,就可以實現(xiàn)由框架來自動創(chuàng)建對象踱蛀、管理對象的生命周期窿给、依賴注入等原本需要程序員來做的事情

  4. 依賴反轉(zhuǎn)原則也叫作依賴倒置原則率拒,英文翻譯是 Dependency Inversion Principle崩泡,縮寫為 DIP。它的英文描述:High-level modules shouldn’t depend on low-level modules. Both modules should depend on abstractions. In addition, abstractions shouldn’t depend on details. Details depend on abstractions.翻譯成中文猬膨,大概意思就是:高層模塊(high-level modules)不要依賴低層模塊(low-level)角撞。高層模塊和低層模塊應(yīng)該通過抽象(abstractions)來互相依賴。除此之外勃痴,抽象(abstractions)不要依賴具體實現(xiàn)細(xì)節(jié)(details)谒所,具體實現(xiàn)細(xì)節(jié)(details)依賴抽象(abstractions)。

    所謂高層模塊和低層模塊的劃分沛申,簡單來說就是劣领,在調(diào)用鏈上,調(diào)用者屬于高層铁材,被調(diào)用者屬于低層尖淘。這條原則跟控制反轉(zhuǎn)有點類似,主要用來指導(dǎo)框架層面的設(shè)計著觉。高層模塊不依賴低層模塊村生,它們共同依賴同一個抽象。抽象不要依賴具體實現(xiàn)細(xì)節(jié)饼丘,具體實現(xiàn)細(xì)節(jié)依賴抽象趁桃。

    我們拿 Tomcat 這個 Servlet 容器作為例子來解釋一下。Tomcat 是運行 Java Web 應(yīng)用程序的容器肄鸽。我們編寫的 Web 應(yīng)用程序代碼只需要部署在 Tomcat 容器下卫病,便可以被 Tomcat 容器調(diào)用執(zhí)行。按照之前的劃分原則典徘,Tomcat 就是高層模塊蟀苛,我們編寫的 Web 應(yīng)用程序代碼就是低層模塊。Tomcat 和應(yīng)用程序代碼之間并沒有直接的依賴關(guān)系烂斋,兩者都依賴同一個“抽象”屹逛,也就是 Servlet 規(guī)范础废。Servlet 規(guī)范不依賴具體的 Tomcat 容器和應(yīng)用程序的實現(xiàn)細(xì)節(jié),而 Tomcat 容器和應(yīng)用程序依賴 Servlet 規(guī)范罕模。

KISS原則

KISS 原則是保持代碼可讀和可維護(hù)的重要手段评腺。KISS 原則中的“簡單”并不是以代碼行數(shù)來考量的。代碼行數(shù)越少并不代表代碼越簡單淑掌,我們還要考慮邏輯復(fù)雜度蒿讥、實現(xiàn)難度、代碼的可讀性等抛腕。而且芋绸,本身就復(fù)雜的問題,用復(fù)雜的方法解決担敌,并不違背 KISS 原則摔敛。除此之外,同樣的代碼全封,在某個業(yè)務(wù)場景下滿足 KISS 原則马昙,換一個應(yīng)用場景可能就不滿足了。

對于如何寫出滿足 KISS 原則的代碼刹悴,我還總結(jié)了下面幾條指導(dǎo)原則:

不要使用同事可能不懂的技術(shù)來實現(xiàn)代碼行楞;

不要重復(fù)造輪子,

要善于使用已經(jīng)有的工具類庫土匀;不要過度優(yōu)化子房。

/**
 * IpValidater類
 *
 */
public class IpValidater {

    // 第一種實現(xiàn)方式: 使用正則表達(dá)式;不滿足KISS原則
    //1. 正則表達(dá)式本身是比較復(fù)雜的就轧,寫出完全沒有 bug 的正則表達(dá)本身就比較有挑戰(zhàn)证杭;
    // 2.并不是每個程序員都精通正則表達(dá)式。對于不怎么懂正則表達(dá)式的同事來說钓丰,看懂并且維護(hù)這段正則表達(dá)式是比較困難的躯砰。
    // 這種實現(xiàn)方式會導(dǎo)致代碼的可讀性和可維護(hù)性變差
    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);
    }

    // 第二種實現(xiàn)方式: 使用現(xiàn)成的工具類每币;滿足KISS原則
    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;
    }

    // 第三種實現(xiàn)方式: 不使用任何工具類携丁;不滿足KISS原則
    // 第三種要比第二種更加有難度,更容易寫出 bug兰怠。從可讀性上來說梦鉴,第二種實現(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;
    }
}

/**
 * ContentValidater類
 * 
 * KMP 算法以快速高效著稱肥橙。當(dāng)我們需要處理長文本字符串匹配問題(幾百 MB 大小文本內(nèi)容的匹配),
 * 或者字符串匹配是某個產(chǎn)品的核心功能(比如 Vim秸侣、Word 等文本編輯器)存筏,又或者字符串匹配算法是
 * 系統(tǒng)性能瓶頸的時候宠互,我們就應(yīng)該選擇盡可能高效的 KMP 算法。而 KMP 算法本身具有邏輯復(fù)雜椭坚、實現(xiàn)
 * 難度大予跌、可讀性差的特點。本身就復(fù)雜的問題善茎,用復(fù)雜的方法解決券册,并不違背 KISS 原則。
 *
 * 不過垂涯,平時的項目開發(fā)中涉及的字符串匹配問題烁焙,大部分都是針對比較小的文本。在這種情況下耕赘,直接調(diào)
 * 用編程語言提供的現(xiàn)成的字符串匹配函數(shù)就足夠了骄蝇。如果非得用 KMP 算法、BM 算法來實現(xiàn)字符串匹配操骡,
 * 那就真的違背 KISS 原則了乞榨。
 *
 * 也就是說,同樣的代碼当娱,在某個業(yè)務(wù)場景下滿足 KISS 原則吃既,換一個應(yīng)用場景可能就不滿足了。
 */
public class ContentValidater {


    // 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;
    }
}

YAGNI原則

YAGNI 原則的英文全稱是:You Ain’t Gonna Need It冀惭。直譯就是:你不會需要它震叙。當(dāng)用在軟件開發(fā)中的時候,它的意思是:不要去設(shè)計當(dāng)前用不到的功能散休;不要去編寫當(dāng)前用不到的代碼媒楼。實際上,這條原則的核心思想就是:不要做過度設(shè)計戚丸。

當(dāng)然划址,這并不是說我們就不需要考慮代碼的擴(kuò)展性。我們還是要預(yù)留好擴(kuò)展點限府,等到需要的時候夺颤,再去實現(xiàn);我們不要在項目中提前引入不需要依賴的開發(fā)包胁勺。

總結(jié):YAGNI 原則跟 KISS 原則并非一回事兒世澜。KISS 原則講的是“如何做”的問題(盡量保持簡單),而 YAGNI 原則說的是“要不要做”的問題(當(dāng)前不需要的就不要做)署穗。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載寥裂,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者嵌洼。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市封恰,隨后出現(xiàn)的幾起案子咱台,更是在濱河造成了極大的恐慌,老刑警劉巖俭驮,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件回溺,死亡現(xiàn)場離奇詭異,居然都是意外死亡混萝,警方通過查閱死者的電腦和手機(jī)遗遵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來逸嘀,“玉大人车要,你說我怎么就攤上這事≌柑龋” “怎么了翼岁?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長司光。 經(jīng)常有香客問我琅坡,道長,這世上最難降的妖魔是什么残家? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任榆俺,我火速辦了婚禮,結(jié)果婚禮上坞淮,老公的妹妹穿的比我還像新娘茴晋。我一直安慰自己,他們只是感情好回窘,可當(dāng)我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布诺擅。 她就那樣靜靜地躺著,像睡著了一般啡直。 火紅的嫁衣襯著肌膚如雪烁涌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天付枫,我揣著相機(jī)與錄音烹玉,去河邊找鬼驰怎。 笑死阐滩,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的县忌。 我是一名探鬼主播掂榔,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼继效,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了装获?” 一聲冷哼從身側(cè)響起瑞信,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎穴豫,沒想到半個月后凡简,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡精肃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年秤涩,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片司抱。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡筐眷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出习柠,到底是詐尸還是另有隱情匀谣,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布资溃,位于F島的核電站武翎,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏溶锭。R本人自食惡果不足惜后频,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望暖途。 院中可真熱鬧卑惜,春花似錦、人聲如沸驻售。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽欺栗。三九已至毫痕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間迟几,已是汗流浹背消请。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留类腮,地道東北人臊泰。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像蚜枢,于是被迫代替她去往敵國和親缸逃。 傳聞我的和親對象是個殘疾皇子针饥,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容