前言 好的代碼需要有高內聚、低耦合、易擴展且擴展改動小等特點。說實話,我入行很久之後,才知道這些設計原則的名字,但是我並不覺得陌生,反而有一種理所當然的感覺。這得感謝自學時網路上前輩們推薦的書籍,培養了自己的代碼潔癖,還得感謝轉行後的第一個東家!以下只是我的理解,如有錯誤,請指正。 單一職責原則 顧 ...
前言
好的代碼需要有高內聚、低耦合、易擴展且擴展改動小等特點。說實話,我入行很久之後,才知道這些設計原則的名字,但是我並不覺得陌生,反而有一種理所當然的感覺。這得感謝自學時網路上前輩們推薦的書籍,培養了自己的代碼潔癖,還得感謝轉行後的第一個東家!以下只是我的理解,如有錯誤,請指正。
單一職責原則
顧名思義,單一職責就是函數或類只做一件事,但是"只做一件事"肯定是針對不同抽象層次的。
舉個例子,我要寫一個文件,於是我定義了write()
這麼一個函數,它確實只做一件事。但是這個write()
對於寫一個文件來說是不夠的,我肯定還需要open()
和close()
,自然這兩個函數也只做了一件事。
但我要真正寫一個文件,希望得到的是下麵的函數
int write_file()
{
open();
write();
close();
}
對於write_file()
的調用者來說,write_file()
確實只做了一件事,它幫我寫了一個文件。但對於寫write_file()
的程式員來說,write_file()
明明做了三件事。
單一職責的職責是對外抽象後的職責,只要函數或類所做的事不超過這個抽象定義,那就不算違背單一職責。
越底層的函數越容易確定職責,甚至只需要這個函數寫的足夠短小,其職責一般就能保證單一。高層的函數或者類就需要考驗程式員的抽象能力了。一個簡單的檢查該抽象是否能做到單一職責的方法,就是看看函數內是否只做了函數名要求的事,類內所有成員函數和成員變數是否只是為了做到類名要求的事。
當我們長期遵循單一職責原則時,我們會習慣於將一個任務拆分為多個最小步驟,而這通常意味著可以通過流水線,並行等方式來執行這些最小步驟,達到提升性能的目的。
里氏替換
里氏替換就是指子類可以出現在父類出現的任何地方。這是一個指導我們何時使用繼承的原則。A繼承B,我們說"A是一個B",既然"A是一個B",A自然能替換到B出現的任何地方。如果無法替換的話,說明A不應繼承B。
依賴倒置
依賴倒置就是為了做到面向介面編程。
那為什麼叫倒置呢?原本高層A直接調用底層B的介面,當高層A想用底層C時就可能需要修改大量代碼,原因是C和B是沒有約束的,C的介面與B可能完全不一樣。為了避免替換底層需要修改大量代碼的情況,高層A自己定義了抽象層D,讓底層B和底層C都依賴抽象層D的介面去實現。因為抽象層D是高層定義的,而底層B和C是依賴D去實現的,所以就叫依賴倒置。在C++中,這通過定義虛基類,並繼承該虛基類來實現。
依賴倒置可以使得高層切換底層實現類時,減少代碼修改量,只需要修改一下實例化的代碼即可,更靈活。
class D
{
public:
virtual foo1() = 0;
...
virtual foon() = 0;
}
class B : public D;
class C : public D;
void A()
{
D* a = new B; // 切換為C時,只需要修改這一行代碼。
a->foo1();
...
a->foon();
}
介面隔離原則
介面隔離是針對介面調用者做的隔離,只讓調用者看到其能調用的介面,本質上是最小許可權。大部分IT公司都會宣講信息安全,而為了保證信息安全的一個原則就是最小化原則。介面隔離就是為了程式安全,如果提供了超過調用者許可權的介面,那可能就會引發問題。
迪米特法則
迪米特法則就是最小依賴,依賴更少的類,更少的介面,這樣可以降低類之間的耦合和減少代碼複雜度。如果某個類依賴了很多類,很多介面,那以後其代碼就有更大的可能因為其依賴項的改動而改動,變得不穩定。
開閉原則
對擴展開放,對修改閉合。擴展可以是函數的擴展,也可以是類的擴展,這裡的對修改閉合指的是對本層的修改閉合,因為對於高層來說肯定是需要修改的,比如調用新介面,使用新類的實例。之所以對修改閉合,是為了避免修改引入BUG。
後話
總是遵頊這些設計原則,對自己的代碼能力是有很大益處的。再者設計原則是道,設計模式是術,如果對道理解通透的話,術是可以自行推演的,可能不知不覺中你已經用了某種設計模式了,甚至創造了新的設計模式。