為什麼你總是覺得設計模式很難?

語言: CN / TW / HK

荒腔走板

上週沒更新,抱歉。週末去重慶浪了一圈,都被催更了。。。

重慶於我而言,是一座熟悉又陌生的城市。這幾年些許變化,但依舊很美,熙熙攘攘,人來人往,只是多了一些商業化的氣息,從外地來的遊客絡繹不絕。

為什麼旅行?有人為美食美景,有人為風土人情。有人想認識新朋友,有人想見見老朋友。有人想找過去的自己,有人想找全新的自己。所以啊,旅行的意義在於尋找自己想要的東西,圖謀不同,感受不同。

這次回重慶,與朋友見見面,喝喝茶,聊起以前時候的事,感覺又回到了那個年少輕狂的時候。雖然只有短短几個小時,但也聊了很多很多,總有説不完的話題,也總有説再見的時候。

也按照慣例説一下為什麼會寫這篇文章。

第一個是大家對設計模式比較感興趣,也是每個程序員(尤其是使用面嚮對象語言的程序員)都應該去學習掌握的思維和工具。

第二個是平時工作中確實會用到,用了設計模式,不但能讓你的程序更加健壯、可維護性更好,最主要的是,看起來逼格更高了!

第三個是設計模式確實不好學,我自己剛學設計模式的時候,老是感覺有點繞,很多,記不住。或者是看似當時理解了,轉眼就忘了,真到該用的時候,又一臉懵逼,不知道該用哪種設計模式。不開玩笑,我相信你應該也有過這種感覺。

關於設計模式的書不少,網上博客也有很多。那我會想,如果我要寫一篇關於設計模式的文章,它給讀者帶來的價值在哪裏呢?如果只是機械地介紹一遍所有的設計模式,那其實沒有多大意義。授人以魚不如授人以漁,今天這篇文章我想探討更多的,是如何去認知和學習設計模式,以及如何才能夠真正讓它為你所用,成為你編程的神兵利器,披荊斬棘。

關於設計模式的幾個問題

首先想和大家探討幾個關於設計模式的問題,這是我在打算寫這篇文章時腦海中浮現出來的一些問題,自己也嘗試給出一些解答,歡迎大家交流探討。

1 設計模式解決的是什麼問題?

軟件是一門工程,要實現功能很簡單,但是在實現功能的基礎上,還能把它做得更加容易擴展,容易應對變化,那就是軟件的藝術。

你會發現,大部分時間,你並不是在從頭寫代碼,而是在“改代碼”。設計得好的代碼會讓你改起來非常的舒服,只需要改動很小的地方,甚至不需要任何改動,就能實現新的功能。而設計得不好的代碼會讓你改起來像是在排地雷,一改紅一大片,這就是程序太過耦合的原因。

縱觀所有的軟件方法論,你會發現絕大多數都是在解決耦合的問題,都想盡力把程序做得高內聚,低耦合。

設計模式解決的是使用面向對象帶來的軟件複雜性,尤其是面對變化的複雜性。我們希望在最開始寫代碼階段,就考慮到後來有可能發生的變化,然後把自己的程序設計得更加好一點,以後改動的代價儘量更小一點。

2 什麼是設計模式的六大原則?

你去看所有關於設計模式的書和系列文章,基本上都會先介紹設計模式的六大原則。

上面提到設計模式解決的其實是使用面向對象帶來的軟件複雜性,那具體如何解決呢?其實就是靠的這幾條前人總結的原則。

理解六大原則比理解具體的設計模式更加重要,因為具體的設計模式是“術”,而六大原則是“道”。你會發現,各種各樣的設計模式,都是遵循了這些六大原則中的一條或者多條。

其中,開閉原則有些特殊。它是最“虛”的,其它原則都可以看成是開閉原則的實現。

哪怕你寫的程序沒有用到任何已知的設計模式,也應該儘量去遵循這六大原則,這樣你寫出來的程序也不會太差,基本上都具有“高內聚,低耦合”的特點。

當然了,原則這個東西有時候是可以違背的,只是你要衡量違背它的代價和帶來的收益,是否值得。

3 為什麼通常説23種設計模式?

因為“設計模式”這個詞是由四人幫(GoF)寫的一本叫《Design Patterns》的書中提出來的,這本書裏面總結了23種設計模式,也是最常用的設計模式

所以設計模式其實並不止23種,只是因為這23種是比較常用的設計模式,它們分別適用於不同的場景,也覆蓋了絕大多數軟件設計的場景。這23種設計模式只是前人的“經驗總結”或者“套路”,有時候經驗還是蠻重要的,使用這些經驗能夠少走很多彎路。

有時候多種設計模式可以互相配合起來使用,或者在現有的23種設計模式之上有一些變化,甚至是總結出一些新的“套路”,用來解決現有23種設計模式不能解決的問題,都是正常的。設計模式並不是死的,一成不變的,它應該是靈活的。

4 怎樣才能掌握設計模式?

設計模式這麼多,而且那麼靈活,那如何才能很好地馴服設計模式這匹野馬呢?

首先要理解六大設計原則,吃透他們,理解為什麼有這幾個原則,如果不遵循這幾種原則會怎樣?理解了六大設計原則,才能更好地理解具體的設計模式。

在學習一個設計模式的時候,要理解它解決了什麼問題?如果不使用這種設計模式,會有什麼壞處?這樣在下次面對類似的問題時,就能夠想起有這麼一種設計模式,可以解決這個問題。

然後可以自己寫一寫代碼,感受一下這個設計模式是怎麼用的,最好是舉個例子,這樣比較生動形象。再看看開源代碼,看看大神們是怎麼用這個設計模式的。

如何學習設計模式?

設計模式是一種“套路”,那學習設計模式也是可以有套路的。我們隨便選擇一個設計模式,來應用這種套路。比如“模板方法模式”。

解決了什麼問題?

假如一個父類有多個子類,而有一些邏輯是所有子類都共用的。比如固定的流程,或者固定的分支邏輯。

如果不使用會怎樣?

如果不使用模板方法模式,每個子類都自己實現一遍,那會產生很多重複代碼,這樣一旦要修改這些共用的邏輯,就需要修改每個子類。

遵循了什麼原則?

  • 里氏替換原則。模板方法中共有的邏輯定義在父類中,且不是abstract的,子類不應該去覆蓋這個方法(可以在父類用final關鍵字修飾)。需要子類去實現的方法定義為abstract的,子類必須實現。
  • 依賴倒置原則,面向接口或抽象類編程。客户端只需要依賴父類就行了,不需要依賴子類。

示例代碼?

我們舉一個適合使用模板方法的例子,比如註冊。註冊的流程是比較固定的,但註冊的方式可以有很多種,比如郵箱註冊或者手機註冊。

首先我們來定義一個註冊到發送驗證碼的流程:

  • 輸入用户名
  • 驗證用户名格式是否正確(郵箱或者手機)
  • 如果格式不正確,拋出異常;如果格式正確,就註冊成功,併發送驗證碼。

先定義抽象的父類:

public abstract class SignUpTemplate {

    public final void signUp(String username, String password) {
        if (!isValidUserName(username)) {
            throw new RuntimeException("用户名格式不正確");
        }
        save(username, password);
        sendCode(generateCode());
    }

    protected abstract boolean isValidUserName(String username);

    protected abstract void sendCode(String code);

    private String generateCode() {
        return "隨機生成一個6位數的驗證碼";
    }

    private void save(String username, String password) {
        // 二次加密,落庫等操作
    }
}
複製代碼

然後分別定義子類的實現:

// email註冊子類
public class EmailSignUp extends SignUpTemplate {

    @Override
    protected boolean isValidUserName(String username) {
        // 驗證是否符合email格式
        return false;
    }

    @Override
    protected void sendCode(String code) {
        // 發送code到email
    }
}

// 手機號註冊子類
public class PhoneSignUp extends SignUpTemplate {

    @Override
    protected boolean isValidUserName(String username) {
        // 驗證是否符合手機號格式
        return false;
    }

    @Override
    protected void sendCode(String code) {
        // 發送code到手機
    }
}
複製代碼

客户端大概長這樣:

SignUpTemplate signUp = context.getSignUp();
signUp.signUp(context.getUsername, context.getPassword);
複製代碼

使用這個模式的開源代碼

Spring的AbstractApplicationContext類的refresh方法,就是典型的模板方法模式的應用。瞭解過Spring源碼的朋友應該看過這段代碼。

我們把上面的“套路”總結成一個思維導圖,大家可以依樣畫葫蘆,對其它設計模式也試試用這個套路,看是否能夠快速理解一個設計模式。

思維導圖
思維導圖

其中,最重要的是要明確第一步,它解決了什麼問題。也是我們最需要記住的。如果你怕記不住,可以用一篇文檔或者一個表格記錄下來,這樣你在寫代碼時感覺隱約需要用到某種設計模式,又想不起來具體該用哪個的時候,可以隨時去查閲。

至於具體的實現細節,其實不那麼重要,需要用到的時候再去查也是來得及的。

有了這個套路,還覺得設計模式難嗎?

關於作者

我是Yasin,一個有顏有料又有趣的程序員。

微信公眾號:編了個程

個人網站:http://yasinshaw.com

關注我的公眾號,和我一起成長~

公眾號
公眾號

本文使用 mdnice 排版