小故事:架構師需要做什么?

jopen 10年前發布 | 6K 次閱讀 架構師 程序員

英文原文:A Little Architecture  

作者:Robert C. Martin  
譯者:孫薇

本文是一篇模仿問答的小故事,作者用幽默的風格簡單分析了架構師要做的工作: 我想要成為一名軟件架構師。 

這是年輕軟件開發者很好的選擇。

我想要帶領團隊,并在數據庫與框架、webserver 等方面作出重要的決策。 

噢,那你根本就不想成為軟件架構師。

我當然想了,我想要成為重要決策的制定者。 

那很好,不過你列出的內容中并不包含重要的決策,這些都是不相關的決策。

什么意思?你是說數據庫并不是重要的決策,你知道我們在上面花了多少錢嗎? 

也許花的太多了。但是,數據庫并不是重要的決策之一。

你怎么能這樣講?數據庫是系統的核心,是進行所有數據系統化、分類、編入索引和存取工作的地方;沒有數據庫的話,就不會有系統。 

數據庫只是一個 IO 設備,它恰巧為分類、查詢與信息報告提供了一些有用的工具,但這些都只是系統架構的輔助功能而已。

輔助?這太離譜了。 

沒錯,就是輔助。系統的業務規則也許能夠利用其中的一些工具,不過那些工具卻并非相應業務規則所固有的。需要的話,可以用不同的工具來替換現有的這些;而業務規則不會改變。

嗯,沒錯,不過必須重新進行編碼,因為在原本的數據庫中這些工具都用到了。 

那是你的問題。

什么意思? 

你的問題在于,你以為業務規則是依賴數據庫工具的,實際上并不是。或者說至少,在提供優秀架構前并不應當是這樣的。

這簡直是瘋了。如何創建不使用那些工具的業務規則呢? 

我不是說它們沒使用數據庫的工具,而是說它們并不依賴于此。業務規則無需知道你使用哪個數據庫。

那么如何在不了解使用什么工具的情況下,獲得業務規則呢? 

讓依賴倒置過來,使得數據庫依賴業務規則。確保業務規則不依賴于數據庫。

你在胡言亂語。 

恰恰相反,我在使用軟件架構的語言。這是依賴倒置原則:低層準則應當依賴高層準則。

一派胡言!高層準則(假設指的是業務規則)調用低層準則(假設指的是數據庫)。因此高層準則會根據調用方依賴被調用方的原則,而依賴低層準則。這個誰都知道! 

在運行時的確如此。不過在編譯時,我們想要的是依賴倒置。高層準則的源代碼應當不提及低層準則的源代碼。

得了吧!怎么能在不提及的情況下進行調用呢? 

當然沒問題。這就是面向對象的所涉及的內容。

面向對象是關于真實世界的模型創建,將數據、功能與有凝聚力的對象相結合。是關于將代碼組織成直觀的結構。 

他們是這么說的?

大家都知道,這是顯而易見的真相。 

沒錯,確實如此,然而,在使用面向對象準則時,的確可以在不提及的情況下進行調用。

好吧,那要怎么做? 

在面向對象設計中,各個對象會彼此發送消息。

沒錯,這是當然的。 

而 sender 在發送消息時,并不知道 receiver 的類型。

這取決于所使用的語言。在 Java 中,sender 至少知道 receiver 的基礎類型。在 Ruby 中,sender 至少知道 receiver 能夠處理所收到的消息。 

沒錯。不過在任何情況下,sender 都不知道 receiver 的具體類型。

是這樣,好吧,確實如此。 

因此,sender 可以在不提及 receiver 具體類型的情況下,設計 receiver 執行某個功能。

是這樣,沒錯。我了解了。不過 sender 仍舊依賴于 receiver。 

在運行時的確如此。不過編譯時則不同。sender 的源代碼并不會提及或者依賴 receiver 的源代碼。事實上 receiver 的源代碼依賴于 sender 的源代碼。

不會吧!sender 仍依賴于它所發送的類。 

也許從某些源代碼來看,會更清楚一些。下面這段是 Java 寫的。首先是 sender:

package sender;public class Sender {
      private Receiver receiver;
  public Sender (Receiver r) {
    receiver = r;
  }
  public void doSomething () {
    receiver.receiveThis ();
  }
  public interface Receiver {
    void receiveThis ();
  }
}

下面是 receiver: 

package receiver;import sender.Sender;public class SpecificReceiver implements Sender.Receiver {
  public void receiveThis () {
    //do something interesting.   }
}

注意:receiver 依賴于 sender,SpecificReceiver 依賴于 Sender,在 sender 中并沒有 receiver 相關的信息。

是啊,不過你在撒謊,你把 receiver 的接口放在 sender 類中了。 

你開始懂了。

懂什么? 

當然是架構的原則。Sender 擁有 receiver 必須實現的接口。

如果這意味著我必須使用嵌套類,那么…… 

嵌套類只是實現目的的手段之一,還有其他辦法。

好吧,等一下。這又跟數據庫有什么關系?我們最開始討論的可是數據庫。 

再看一點代碼吧。首先是一個簡單的業務規則:

package businessRules;import entities.Something;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 到底是什么?

它通過業務規則提供了所有數據存取方法。按以下方式實現:

package businessRules;import entities.Something;public interface BusinessRuleGateway {
  Something getSomething (String id);
  void startTransaction ();
  void saveSomething (Something thing);
  void endTransaction ();
}

注意:這是在 businessRules 之中。 

ok,Something 類又是什么? 

它代表著簡單的業務對象。我將它放在 entities 之中。

package entities;public class Something {
  public void makeChanges () {
    //...   }
}

最終 BusinessRuleGateway 實現,這個類知道真正的數據庫:

package database;import businessRules.BusinessRuleGateway;import entities.Something;public class MySqlBusinessRuleGateway implements BusinessRuleGateway {
  public Something getSomething (String id) {
    // use MySql to get a thing.   }
  public void startTransaction () {
    // start MySql transaction   }
  public void saveSomething (Something thing) {
    // save thing in MySql   }
  public void endTransaction () {
    // end MySql transaction   }
}

另外,注意業務規則在運行時調用數據庫;不過在編譯時,數據庫會涉及并依賴于 businessRules。 

好吧,我想我明白了。你只是在利用多態性來隱藏從業務規則實現數據庫的事實。不過仍需要一個接口,向業務規則提供所有的數據庫工具。 

不,完全不是這樣。我們沒有嘗試向業務規則提供數據庫工具。而是通過業務規則,為它們所需要的內容創建接口。實現這些接口就能調用合適的工具。 

是啊,不過如果所有業務規則需要用到每個工具,那么只需把工具放在 gateway 接口中。 

啊,我看你還是沒明白。 

明白什么?這已經很清楚了。 

每個業務規則只為自己所需的數據訪問工具定義一個接口。

等一下,你說什么? 

這就是接口隔離原則(Interface Segregation Principle)。每個業務規則類只用到數據庫的某些設施。因此,每個業務規則提供的接口只能訪問相應的設施。

不過,這意味著需要很多接口,以及很多的小型實現類,它們又會調用其他的數據庫類。 

很好,你開始理解了。

不過這太亂了,浪費時間。為什么要這樣做呢? 

這樣做能夠條理分明,節省時間。

得了吧,為了代碼,弄出來一大堆代碼。 

恰恰相反,通過重要的架構決策,可以延緩不相關的決策。

這是什么意思? 

記得最開始,你說想做軟件架構師不是嗎?你想要作出所有真正重要的決策。

是啊,我是這樣想的。 

你想要決策的是數據庫、webserver 和框架相關的方面,對嗎?

是啊,你說那些都不重要。只是不相關的內容。 

沒錯。就是這樣。軟件架構師所作出的重要決策指的是,讓你不對數據庫、webserver 和框架進行決策。

不過必須得先決定那些吧! 

不用的。事實上,在開發周期中,這些都可以稍后再決定,在信息更充足的時候再決定。 

如果架構師提前確定框架,卻發現框架無法提供所需的性能,或者帶來了無法忍受的約束,這就成了災難。

只有架構師決定推遲決策,待信息足夠時才作出決策;在架構師的決策下,不使用緩慢而過于耗費資源的 IO 設備和框架的團隊,才能創建快速、輕量級的測試環境;只有其架構師關心真正重要的東西,延緩那些不重要的,這樣的團隊才是幸運的團隊。

胡說,我完全不明白你的意思。 

好吧,還是好好看一下本文,不然只能再等 10 年你才能明白了。

來自: www.iteye.com

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!