(程序員:)我要成爲一個軟件架構師。
(資深架構師:)對一個年輕的工程師來講,這是一個很好的目標。
(程序員:)我要領導一個團隊,還要作全部關於數據庫、框架和Web服務器的重要決定。
(資深架構師: )好吧,若是是這樣,你就不必成爲一個軟件架構師了。
(程序員:)固然有必要了!我要成爲一個可以作全部重要決定的人。
(資深架構師:)這樣很好,只是你沒有列出哪些纔是重要的決定。你剛纔說的那些跟重要的決定沒有什麼關係。
(程序員:)你說什麼?難道數據庫不重要?你知道咱們在數據庫上面花了多少錢嗎?
(資深架構師:)可能不少。不過數據庫仍然不是最重要的。
(程序員:)你怎麼能這麼說呢?數據庫但是整個系統的心臟啊!全部的數據都保存在這裏,它們在這裏被排序,被索引,被訪問。若是沒有數據庫,整個系統就沒法運做!
(資深架構師:)數據庫只不過是一個IO設備,它提供了一些有用的工具對數據進行排序、查詢,並生成報表,但這些工具都只是整個系統的附屬品。
(程序員:)附屬品?真是難以想象。
(資深架構師:)是的,附屬品。你的系統業務邏輯或許會用到這些工具,但這些工具並不是業務邏輯固有的組成部分。若是有必要,你能夠隨時替換掉這些工具,但業務邏輯仍是那些業務邏輯。
(程序員:)好吧,不過若是把這些工具替換掉,咱們就要從新實現業務邏輯了。
(資深架構師:)那是你的問題。
(程序員:)爲何這麼說?
(資深架構師:)你認爲業務邏輯依賴數據庫,但實際上不是這樣的。若是你的架構足夠好,最起碼業務邏輯不該該依賴數據庫。
(程序員:)這太瘋狂了。我怎麼可能建立出不使用這些工具的業務邏輯?
(資深架構師:)我並無說業務邏輯不要使用數據庫工具,個人意思是它們不該該依賴這些工具。業務邏輯不該該知道使用的是哪種數據庫。
(程序員:)若是業務邏輯對數據庫一無所知,它怎麼使用這些工具呢?
(資深架構師:)依賴反轉。你要讓數據庫依賴業務邏輯,而不是讓業務邏輯依賴數據庫。
(程序員:)你的話讓人費解。
(資深架構師:)費解嗎?我講的但是軟件架構。這個就是依賴反轉原則,讓下層策略來依賴上層策略。
(程序員:)那就更加費解了!既然上層策略(假設你指的是業務邏輯)要調用下層策略(假設你指的是數據庫),那麼就應該是上層策略依賴依賴下層策略,就像調用者依賴被調用者同樣。這是衆所周知的!
(資深架構師:)在運行時確實是這樣的,但在編譯時咱們要把依賴反轉過來。上層策略的代碼裏不要引用任何下層策略的代碼。
(程序員:)拜託!不引用代碼就沒法調用它們。
(資深架構師:)固然能夠調用了。面向對象就能夠作到。
(程序員:)面向對象對真實世界進行建模,把數據和函數組合到對象裏,把代碼組織成直觀的結構。
(資深架構師:)這是他們告訴你的嗎?
(程序員:)全部人都知道的,這不是很明顯的事情嗎?
(資深架構師:)確實如此。不過,面向對象是能夠作到不引用也能調用的。
(程序員:)好吧,那它是怎麼作到的?
(資深架構師:)你應該知道,在面向對象系統裏對象會給其它對象發送消息的,對吧?
(程序員:)是的,固然。
(資深架構師:)那麼你就該知道,消息發送者是不知道消息接收者是什麼類型的。
(程序員:)這要看使用的是哪種語言了。在Java裏,發送者最起碼要知道接收者的基本類型。在Ruby裏,發送者知道接收者必定會處理它所發送的消息。
(資深架構師:)是的。不過不論是哪種狀況,發送者都不知道接收者具體的類型。
(程序員:)嗯,是的。
(資深架構師:)因此發送者能夠給接收者傳遞一個函數,讓接收者執行這個函數,這樣發送者就不須要知道接收者是什麼類型了。
(程序員:)沒錯。我瞭解你的意思。不過發送者仍然依賴接收者。
(資深架構師:)在運行時確實是的,但在編譯時不是這樣的。發送者的代碼裏並無引用接收者的代碼。實際上,是接收者的代碼依賴了發送者的代碼。
(程序員:)啊!但發送者仍然會依賴接收者的類。
(資深架構師:)看來須要用代碼來講明瞭,我用Java來寫些代碼。首先是發送者代碼:
public class Sender {
private Receiver receiver;
public Sender(Receiver r) {
receiver = r;
}
public void doSomething() {
receiver.receiveThis();
}
public interface Receiver {
void receiveThis();
}
}
(資深架構師:)下面是接收者代碼:
public class SpecificReceiver implements Sender.Receiver {
public void receiveThis() {
//這裏會作一些有趣的事情
}
}
(資深架構師:)能夠看到,接收者代碼依賴了發送者代碼,也就是說SpecificReceiver依賴了Sender。同時能夠看到,發送者代碼對接收者代碼一無所知。
(程序員:)哈,你做弊了。你把接收者的接口放到了發送者的類裏了。
(資深架構師:)你開始明白了。
(程序員:)明白什麼?
(資深架構師:)固然是架構原則啊。發送者持有接收者必須實現的接口。
(程序員:)若是這意味着我要使用內部類,那麼……
(資深架構師:)使用內部類只是方法之一,還有其它的方法。
(程序員:)請等一下。最開始咱們討論的是數據庫,那這些跟數據庫又有什麼關係呢?
(資深架構師:)讓咱們來看一下其它代碼吧。首先是一個簡單的業務邏輯
public class BusinessRule {
private BusinessRuleGateway gateway;
public BusinessRule(BusinessRuleGateway gateway) {
this.gateway = gateway;
}
public void execute(String id) {
gateway.startTransaction();
Something thing = gateway.getSomething(id);
thing.makeChanges();
gateway.saveSomething(thing);
gateway.endTransaction();
}
}
(程序員:)這個業務邏輯沒有作什麼事情啊。
(資深架構師:)這只是個例子。在實際實現業務邏輯的時候,不會有不少相似這樣的類的。
(程序員:)好吧。那麼Gateway是用來作什麼的呢?
(資深架構師:)它爲業務邏輯提供了全部訪問數據的方法。下面是它的代碼:
public interface BusinessRuleGateway {
Something getSomething(String id);
void startTransaction();
void saveSomething(Something thing);
void endTransaction();
}
(資深架構師:)要注意,這個接口是在businessRules包裏面的。
(程序員:)好吧。那Something這個類又是用來作什麼的呢?
(資深架構師:)它表明一個簡單的業務對象。我把它放在另外一個叫entities的包裏。
public class Something {
public void makeChanges() {
//...
}
}
(資深架構師:)最後須要實現BusinessRuleGateway接口,這個實現類會知道相關的數據庫細節:
public class MySqlBusinessRuleGateway implements BusinessRuleGateway {
public Something getSomething(String id) {
// 從MySQL裏讀取一些數據
}
public void startTransaction() {
// 開始一個事務
}
public void saveSomething(Something thing) {
// 把數據保存到MySQL
}
public void endTransaction() {
// 結束事務
}
}
(資深架構師:)能夠看到,業務邏輯是在運行時對數據庫進行調用的。而在編譯時,是database包引用了businessRules包。
(程序員:)好吧,我想我明白了。你用多態性隱藏了數據庫實現。不過在業務邏輯裏,仍然引用了數據庫的工具接口。
(資深架構師:)不,不是這樣的。咱們並無打算爲業務邏輯提供全部的數據庫工具接口,而是業務邏輯建立了它們所須要的接口。在實現這些接口的時候,能夠調用相應的工具。
(程序員:)嗯,這樣的話,若是業務邏輯須要全部的工具,那麼你必須把全部工具都放到Gateway接口裏。
(資深架構師:)哈,我以爲你仍是沒有明白。
(程序員:)不明白什麼?我以爲已經很清楚了。
(資深架構師:)每一個業務邏輯只定義它所須要的接口。
(程序員:)等等,什麼意思?
(資深架構師:)這個叫做接口分離原則。每一個業務邏輯只使用一部分數據庫工具,因此每一個業務邏輯只定義可以知足須要的接口。
(程序員:)這樣的話,你就會有不少接口,並且有不少實現類。
(資深架構師:)哈,是的。你開始明白了。
(程序員:)這樣子很浪費時間!我爲何要這樣作呢?
(資深架構師:)這樣作是爲了讓代碼更乾淨,而且節省時間。
(程序員:)算了吧,這樣只會增長更多的代碼。
(資深架構師:)相反,這實際上是很重要的架構決定,這跟你以前所說的那些所謂的重要決定是不同的。
(程序員:)什麼意思?
(資深架構師:)還記得你剛開始說你要成爲一個軟件架構師嗎?你還想要作全部重要的決定?
(程序員:)是啊,我是這麼想過。
(資深架構師:)你想作全部關於數據庫、Web服務和框架的決定。
(程序員:)是啊,而你卻說它們都不重要,還說它們其實跟重要的決定不相干。
(資深架構師:)沒錯,它們確實跟重要的決定不相干。一個軟件架構師真正要作的重要決定都在數據庫、Web服務器和框架以外。
(程序員:)但首先要先決定用什麼數據庫、Web服務器或框架啊!
(資深架構師:)實際上應該在開發後期纔開始作這些事情——在你掌握了更多信息以後。
(資深架構師:)當架構師草率地決定要使用一個數據庫,後來卻發現使用文件系統效率更高。
(資深架構師:)當架構師草率的決定使用一個Web服務器,後來卻發現團隊須要的不過是一個socket藉口。
(資深架構師:)當架構師草率地決定使用一個框架,後來卻發現框架提供的功能是團隊不須要的,反而給團隊帶來了諸多約束。
(資深架構師:)當架構師在掌握了足夠多的信息後才決定該用什麼數據庫、Web服務器或框架。
(資深架構師:)當架構師爲團隊鑑別出運行緩慢、耗費資源的IO設備和框架,這樣他們就能夠構建飛速運行的輕量級測試環境。
(資深架構師:)當架構師把注意力放在那些真正重要的事情上,並把那些不重要的事情放在一邊。
(程序員:)我徹底不知道你在說什麼了。
(資深架構師:)好吧,若是在若干年後你尚未轉作管理,或許會明白這一切的……程序員
文檔來源:IT蝦米網數據庫