軟件架構設計系列總結

架構引用維基百科:軟件體系結構是構建計算機軟件實踐的基礎。與建築師設定建築項目的設計原則和目標,做爲繪圖員畫圖的基礎同樣,一個軟件架構師或者系統架構師陳述軟件構架以做爲知足不一樣客戶需求的實際系統設計方案的基礎。從和目的、主題、材料和結構的聯繫上來講,軟件架構能夠和建築物的架構相比擬。一個軟件架構師須要有普遍的軟件理論知識和相應的經驗來實施和管理軟件產品的高級設計。軟件架構師定義和設計軟件的模塊化,模塊之間的交互,用戶界面風格,對外接口方法,創新的設計特性,以及高層事物的對象操做、邏輯和流程。
軟件架構師與客戶商談概念上的事情,與經理商談普遍的設計問題,與軟件工程師商談創新的結構特性,與程序員商談實現技巧,外觀和風格。

軟件架構是一個系統的草圖。軟件架構描述的對象是直接構成系統的抽象組件。各個組件之間的鏈接則明確和相對細緻地描述組件之間的通信。在實現階段,這些抽象組件被細化爲實際的組件,好比具體某個類或者對象。在面向對象領域中,組件之間的鏈接一般用接口來實現。

架構來源於建築工程學,描述對軟件密集型系統設計藍圖。在不一樣軟件領域,有其不一樣特徵,但有一部分共同基礎設計原則和共性。css

目錄:html

1—面向對象設計原則理解java

2—一些軟件設計的原則程序員

3—邏輯層 vs 物理層web

4—服務層的簡單理解面試

5—SOA面向服務架構簡述算法

6—業務邏輯層簡述sql

7—設計箴言理解數據庫

8—數據訪問層簡述編程

9—存儲過程傳言

10—表現層模式-MVC

1—面向對象設計原則理解

面向對象設計(OOD)核心原則讓個人程序模塊達到「高內聚低耦合」,這是來自於30年前興起的結構化設計(structured Design),可是一樣適用於咱們的OOD。

1.高內聚:

高內聚是指某個特定模塊(程序,類型)都應完成一系列相關功能,描述了不一樣程序,類型中方法,方法中不一樣操做描述的邏輯之間的距離相近。高內聚意味可維護性,可從新性,由於模塊對外部的依賴少(功能的完備性)。若是兩個模塊之間的修改,互不影響這說明模塊之間是高內聚的。模塊的內聚和其擔當的職責成反比,即,模塊的職責越多,模塊的內聚性越低,這也是模塊的單一原則(SRP),SRP提倡每一個類型都最好只承擔單一的職責,只有單一的改變因素。

2.低耦合:

耦合是描述模塊之間的依賴程度,若是一個模塊的修改,都有影響另外一個模塊則,兩模塊之間是相互依賴耦合的。(依賴具備傳遞性,耦合的兩個模塊可能間接依賴),低耦合是咱們的設計目的,但不是不存在耦合不存依賴,依賴是必須的,由於模塊之間必須通訊交互,不過個人設計依賴應該依賴於不變或者不易變的接口,無需瞭解模塊的具實現(OO封裝性)。

在面向對象:咱們能夠簡述爲功能完備(高內聚)的對象之間的交互是依賴於不變或不易變的接口契約(低耦合)。

實現高內聚低耦合:行之有效的方式是分了關注點(SOC),將系統拆分紅功能不一樣沒有重疊功能集。每一個功能只關注一個方面(Aspect)保證模塊之間功能沒有或者儘可能少的重複。模塊化內部實現細節隱藏,只暴露必須的接口,使得模塊之間依賴於抽象,達到穩定。分離關注點的思想存在於咱們軟件設計的各個領域。如在.net的世界裏SOA(面向服務架構)服務就是關注點,只暴露出必要的契約。分層架構從邏輯上利用接口抽象信息隱藏,減小依賴。MVC,MVP也是遵循分了關注點原則,達到表現層和邏輯的分離。

面向對象設計原則:

1.下降耦合度:對象直接須要交互,這就存在依賴,爲了實現低耦合就必須減小依賴,依賴於穩定或不易變抽象。考慮以下訂單日誌記錄場景:咱們須要在訂單每部操做記錄更改日誌。

public class OrderManager
{
   public void Create(Order order)
  {
      //訂單處理.
     Logger log = new Logger();
     String history=GetHistory();
     log.log(history);
 }
}

在這裏咱們的OrderManager和Logger存在高耦合,Logger類的修改可能致使OrderManager的修改,並且不能隨意切換咱們的日誌記錄方式,好比文件,控制檯,數據庫等日誌方式。

面向抽象編程提出抽象(接口,abstract類)是不易變的穩定抽象;對於OrderManager來講我不須要了解日誌記錄組件內部,只須要明白提供那些接口可用,怎麼用。

public interface ILogger
{
  void Log(History history);
}
public class Logger
{
  public void Log(History history)
{
//內部實現
};
}

那麼咱們能夠從設計模式工廠模式(工廠模式是負責一些列類似對象的建立)Create 日誌組件ILogger。

咱們的OrderManager 就能夠實現爲:

ILogger log =LoggerFactory.Create();
log.Log(history);

這樣咱們的OrderManager就依賴於ILogger,而隔離Logger具體實現,將依賴於抽象,把變化縮小到Factory內部(一樣也能夠用抽象工廠),若是日誌實現變化咱們能夠從新實現ILogger ,修改Factory邏輯,若是內部利用配置個人需求變動轉移到配置。這就是面向對象第一原則,依賴於抽象而隱藏實現。(利用IOC是一種更好的方式)

2.代碼的重用性:儘可能保證相同功能代碼只出現一次(Code once run anywhere)。代碼的重用在面對對象設計中有繼承和組合兩種方式,通常推薦組合優先。組合依賴於接口,組合更安全,易於維護,測試。繼承存在父類訪問權限,父類的修改致使子類的變化,太多的繼承也有致使派生類的膨脹,維護管理也是件頭痛的事。

3.開閉原則(OCP):表述擁抱需求變化,儘可能作到對模塊的擴展開發,修改關閉。對於新增需求咱們完美的作法是新增類型而不是修改邏輯,這就意味着咱們必須使用組合或者是繼承體系(爲了不上一條重用性,個人繼承應該是乾淨的繼承體系,派生類應該只是新增功能而不是修改來自父類上下文),

4.里氏替換(LSP):表述派生類應該能夠在任何地方替代父類使用。並非全部的子類均可以徹底替換子類,好比設計父類私有上下文信息的訪問,致使子類沒法訪問。

5.依賴倒置(DIP):描述組件之間高層組件不該該依賴於底層組件。依賴倒置是指實現和接口倒置,採用自頂向下的方式關注所需的底層組件接口,而不是其實現。DI框架實現IOC(控制反轉)就是DIP很好的插入底層組件構造框架(分構造注入,函數注入,屬性注入)。微軟Unity,Castle windsor,Ninject等框架支持。

最後分離關注點,衍生出聲明式編程,面向方面編程(AOP)實現縱切關注點,把具體業務邏輯和日誌安全等框架集公用邏輯分離。

2—一些軟件設計的原則

之前本站向你們介紹過一些軟件開發的原則,好比優質代碼的十誡和Unix傳奇(下篇)中因此說的UNIX的設計原則。相信你們從中可以從中學瞭解到一些設計原理方面的知識,正如我在《再談「我是怎麼招聘程序」》中所說的,一個好的程序員一般由其操做技能、知識水平,經驗層力和能力四個方面組成。在這裏想和你們說說設計中的一些原則,我認爲這些東西屬於長期經驗總結出來的知識。這些原則,每個程序員都應該瞭解。可是請不要教條主義,在使用的時候仍是要多多考慮實際狀況。其實,下面這些原則,不僅僅只是軟件開發,能夠推廣到其它生產活動中,甚至咱們的生活中。

Don’t Repeat Yourself (DRY)

DRY 是一個最簡單的法則,也是最容易被理解的。但它也多是最難被應用的(由於要作到這樣,咱們須要在泛型設計上作至關的努力,這並非一件容易的事)。它意味着,當咱們在兩個或多個地方的時候發現一些類似的代碼的時候,咱們須要把他們的共性抽象出來形一個惟一的新方法,而且改變現有的地方的代碼讓他們以一些合適的參數調用這個新的方法。

參考:http://en.wikipedia.org/wiki/Don%27t_repeat_yourself

Keep It Simple, Stupid (KISS)

KISS原則在設計上可能最被推崇的,在家裝設計,界面設計 ,操做設計上,複雜的東西愈來愈被衆人所BS了,而簡單的東西愈來愈被人所承認,好比這些UI的設計和咱們中國網頁(尤爲是新浪的網頁)者是負面的例子。「宜家」(IKEA)簡約、效率的家居設計、生產思路;「微軟」(Microsoft)「所見即所得」的理念;「谷歌」(Google)簡約、直接的商業風格,無一例外的遵循了「kiss」原則,也正是「kiss」原則,成就了這些看似神奇的商業經典。而蘋果公司的iPhone/iPad將這個原則實踐到了極至。

把一個事情搞複雜是一件簡單的事,但要把一個複雜的事變簡單,這是一件複雜的事。

參考:http://en.wikipedia.org/wiki/KISS_principle

Program to an interface, not an implementation

這是設計模式中最根本的哲學,注重接口,而不是實現,依賴接口,而不是實現。接口是抽象是穩定的,實現則是多種多樣的。之後面咱們會面向對象的SOLID原則中會提到咱們的依賴倒置原則,就是這個原則的的另外一種樣子。還有一條原則叫 Composition over inheritance(喜歡組合而不是繼承),這兩條是那23個經典設計模式中的設計原則。

Command-Query Separation (CQS) – 命令-查詢分離原則

  • 查詢:當一個方法返回一個值來回應一個問題的時候,它就具備查詢的性質;
  • 命令:當一個方法要改變對象的狀態的時候,它就具備命令的性質;

一般,一個方法多是純的Command模式或者是純的Query模式,或者是二者的混合體。在設計接口時,若是可能,應該儘可能使接口單一化,保證方法的行爲嚴格的是命令或者是查詢,這樣查詢方法不會改變對象的狀態,沒有反作用,而會改變對象的狀態的方法不可能有返回值。也就是說:若是咱們要問一個問題,那麼就不該該影響到它的答案。實際應用,要視具體狀況而定,語義的清晰性和使用的簡單性之間須要權衡。將Command和Query功能合併入一個方法,方便了客戶的使用,可是,下降了清晰性,並且,可能不便於基於斷言的程序設計而且須要一個變量來保存查詢結果。

在系統設計中,不少系統也是以這樣原則設計的,查詢的功能和命令功能的系統分離,這樣有則於系統性能,也有利於系統的安全性。

參考:http://en.wikipedia.org/wiki/Command-query_separation

You Ain’t Gonna Need It (YAGNI)

這個原則簡而言之爲——只考慮和設計必須的功能,避免過分設計。只實現目前須要的功能,在之後您須要更多功能時,能夠再進行添加。

  • 如無必要,勿增複雜性。
  • 軟件開發先是一場溝通博弈。

之前本站有一篇關於過分重構的文章,這個示例就是這個原則的反例。而,WebSphere的設計者就表示過他過分設計了這個產品。咱們的程序員或是架構師在設計系統的時候,會考慮不少擴展性的東西,致使在架構與設計方面使用了大量折衷,最後致使項目失敗。這是個使人感到諷刺的教訓,由於原本但願儘量延長項目的生命週期,結果反而縮短了生命週期。

參考:http://en.wikipedia.org/wiki/You_Ain%27t_Gonna_Need_It

Law of Demeter – 迪米特法則

迪米特法則(Law of Demeter),又稱「最少知識原則」(Principle of Least Knowledge),其來源於1987年荷蘭大學的一個叫作Demeter的項目。Craig Larman把Law of Demeter又稱做「不要和陌生人說話」。在《程序員修煉之道》中講LoD的那一章叫做「解耦合與迪米特法則」。關於迪米特法則有一些很形象的比喻:

  • 若是你想讓你的狗跑的話,你會對狗狗說仍是對四條狗腿說?
  • 若是你去店裏買東西,你會把錢交給店員,仍是會把錢包交給店員讓他本身拿?

和狗的四肢說話?讓店員本身從錢包裏拿錢?這聽起來有點荒唐,不過在咱們的代碼裏這幾乎是見怪不怪的事情了。

對於LoD,正式的表述以下:

對於對象 ‘O’ 中一個方法’M',M應該只可以訪問如下對象中的方法:

  1. 對象O;
  2. 與O直接相關的Component Object;
  3. 由方法M建立或者實例化的對象;
  4. 做爲方法M的參數的對象。

在《Clean Code》一書中,有一段Apache framework中的一段違反了LoD的代碼:

final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();

這麼長的一串對其它對象的細節,以及細節的細節,細節的細節的細節……的調用,增長了耦合,使得代碼結構複雜、僵化,難以擴展和維護。

在《重構》一書中的代碼的環味道中有一種叫作「Feature Envy」(依戀情結),形象的描述了一種違反了LoC的狀況。Feature Envy就是說一個對象對其它對象的內容更有興趣,也就是說總是羨慕別的對象的成員、結構或者功能,大老遠的調用人家的東西。這樣的結構顯然是不合理的。咱們的程序應該寫得比較「害羞」。不能像前面例子中的那個不把本身當外人的店員同樣,拿過客人的錢包本身把錢拿出來。「害羞」的程序只和本身最近的朋友交談。這種狀況下應該調整程序的結構,讓那個對象本身擁有它羨慕的feature,或者使用合理的設計模式(例如Facade和Mediator)。

參考:http://en.wikipedia.org/wiki/Principle_of_Least_Knowledge

面向對象的S.O.L.I.D 原則

通常來講這是面向對象的五大設計原則,可是,我以爲這些原則可適用於全部的軟件開發。

Single Responsibility Principle (SRP) – 職責單一原則

關於單一職責原則,其核心的思想是:一個類,只作一件事,並把這件事作好,其只有一個引發它變化的緣由。單一職責原則能夠看做是低耦合、高內聚在面向對象原則上的引伸,將職責定義爲引發變化的緣由,以提升內聚性來減小引發變化的緣由。職責過多,可能引發它變化的緣由就越多,這將致使職責依賴,相互之間就產生影響,從而極大的損傷其內聚性和耦合度。單一職責,一般意味着單一的功能,所以不要爲一個模塊實現過多的功能點,以保證明體只有一個引發它變化的緣由。

  • Unix/Linux是這一原則的完美體現者。各個程序都獨立負責一個單一的事。
  • Windows是這一原則的反面示例。幾乎全部的程序都交織耦合在一塊兒。

Open/Closed Principle (OCP) – 開閉原則

關於開發封閉原則,其核心的思想是:模塊是可擴展的,而不可修改的。也就是說,對擴展是開放的,而對修改是封閉的。

  • 對擴展開放,意味着有新的需求或變化時,能夠對現有代碼進行擴展,以適應新的狀況。
  • 對修改封閉,意味着類一旦設計完成,就能夠獨立完成其工做,而不要對類進行任何修改。

對於面向對象來講,須要你依賴抽象,而不是實現,23個經典設計模式中的「策略模式」就是這個實現。對於非面向對象編程,一些API須要你傳入一個你能夠擴展的函數,好比咱們的C 語言的qsort()容許你提供一個「比較器」,STL中的容器類的內存分配,ACE中的多線程的各類鎖。對於軟件方面,瀏覽器的各類插件屬於這個原則的實踐。

Liskov substitution principle (LSP) – 里氏代換原則

軟件工程大師Robert C. Martin把里氏代換原則最終簡化爲一句話:「Subtypes must be substitutable for their base types」。也就是,子類必須可以替換成它們的基類。即:子類應該能夠替換任何基類可以出現的地方,而且通過替換之後,代碼還能正常工做。另外,不該該在代碼中出現if/else之類對子類類型進行判斷的條件。里氏替換原則LSP是使代碼符合開閉原則的一個重要保證。正是因爲子類型的可替換性才使得父類型的模塊在無需修改的狀況下就能夠擴展。

這麼說來,彷佛有點教條化,我很是建議你們看看這個原則個兩個最經典的案例——「正方形不是長方形」和「鴕鳥不是鳥」。經過這兩個案例,你會明白《墨子 小取》中說的 ——「娣,美人也,愛娣,非愛漂亮人也….盜,人也;惡盜,非惡人也。」——妹妹雖然是美人,但喜歡妹妹並不表明喜歡美人。盜賊是人,但討厭盜賊也並不表明就討厭人類。這個原則讓你考慮的不是語義上對象的間的關係,而是實際需求的環境。

在不少狀況下,在設計初期咱們類之間的關係不是很明確,LSP則給了咱們一個判斷和設計類之間關係的基準:需不須要繼承,以及怎樣設計繼承關係。

Interface Segregation Principle (ISP) – 接口隔離原則

接口隔離原則意思是把功能實如今接口中,而不是類中,使用多個專門的接口比使用單一的總接口要好。

舉個例子,咱們對電腦有不一樣的使用方式,好比:寫做,通信,看電影,打遊戲,上網,編程,計算,數據等,若是咱們把這些功能都聲明在電腦的抽類裏面,那麼,咱們的上網本,PC機,服務器,筆記本的實現類都要實現全部的這些接口,這就顯得太複雜了。因此,咱們能夠把其這些功能接口隔離開來,好比:工做學習接口,編程開發接口,上網娛樂接口,計算和數據服務接口,這樣,咱們的不一樣功能的電腦就能夠有所選擇地繼承這些接口。

這個原則能夠提高咱們「搭積木式」的軟件開發。對於設計來講,Java中的各類Event Listener和Adapter,對於軟件開發來講,不一樣的用戶權限有不一樣的功能,不一樣的版本有不一樣的功能,都是這個原則的應用。

Dependency Inversion Principle (DIP) – 依賴倒置原則

高層模塊不該該依賴於低層模塊的實現,而是依賴於高層抽象。

舉個例子,牆面的開關不該該依賴於電燈的開關實現,而是應該依賴於一個抽象的開關的標準接口,這樣,當咱們擴展程序的時候,咱們的開關一樣能夠控制其它不一樣的燈,甚至不一樣的電器。也就是說,電燈和其它電器繼承並實現咱們的標準開關接口,而咱們的開關產商就可不須要關於其要控制什麼樣的設備,只須要關心那個標準的開關標準。這就是依賴倒置原則。

這就好像瀏覽器並不依賴於後面的web服務器,其只依賴於HTTP協議。這個原則實在是過重要了,社會的分工化,標準化都是這個設計原則的體現。

參考:http://en.wikipedia.org/wiki/Solid_(object-oriented_design)

Common Closure Principle(CCP)– 共同封閉原則

一個包中全部的類應該對同一種類型的變化關閉。一個變化影響一個包,便影響了包中全部的類。一個更簡短的說法是:一塊兒修改的類,應該組合在一塊兒(同一個包裏)。若是必須修改應用程序裏的代碼,咱們但願全部的修改都發生在一個包裏(修改關閉),而不是遍及在不少包裏。CCP原則就是把由於某個一樣的緣由而須要修改的全部類組合進一個包裏。若是2個類從物理上或者從概念上聯繫得很是緊密,它們一般一塊兒發生改變,那麼它們應該屬於同一個包。

CCP延伸了開閉原則(OCP)的「關閉」概念,當由於某個緣由須要修改時,把須要修改的範圍限制在一個最小範圍內的包裏。

參考:http://c2.com/cgi/wiki?CommonClosurePrinciple

Common Reuse Principle (CRP) – 共同重用原則

包的全部類被一塊兒重用。若是你重用了其中的一個類,就重用所有。換個說法是,沒有被一塊兒重用的類不該該被組合在一塊兒。CRP原則幫助咱們決定哪些類應該被放到同一個包裏。依賴一個包就是依賴這個包所包含的一切。當一個包發生了改變,併發布新的版本,使用這個包的全部用戶都必須在新的包環境下驗證他們的工做,即便被他們使用的部分沒有發生任何改變。由於若是包中包含有未被使用的類,即便用戶不關心該類是否改變,但用戶仍是不得不升級該包並對原來的功能加以從新測試。

CCP則讓系統的維護者受益。CCP讓包儘量大(CCP原則加入功能相關的類),CRP則讓包儘量小(CRP原則剔除不使用的類)。它們的出發點不同,但不相互衝突。

參考:http://c2.com/cgi/wiki?CommonReusePrinciple

Hollywood Principle – 好萊塢原則

好萊塢原則就是一句話——「don’t call us, we’ll call you.」。意思是,好萊塢的經紀人們不但願你去聯繫他們,而是他們會在須要的時候來聯繫你。也就是說,全部的組件都是被動的,全部的組件初始化和調用都由容器負責。組件處在一個容器當中,由容器負責管理。

簡單的來說,就是由容器控制程序之間的關係,而非傳統實現中,由程序代碼直接操控。這也就是所謂「控制反轉」的概念所在:

  1. 不建立對象,而是描述建立對象的方式。
  2. 在代碼中,對象與服務沒有直接聯繫,而是容器負責將這些聯繫在一塊兒。

控制權由應用代碼中轉到了外部容器,控制權的轉移,是所謂反轉。

好萊塢原則就是IoC(Inversion of Control)或DI(Dependency Injection )的基礎原則。這個原則很像依賴倒置原則,依賴接口,而不是實例,可是這個原則要解決的是怎麼把這個實例傳入調用類中?你可能把其聲明成成員,你能夠經過構造函數,你能夠經過函數參數。可是 IoC可讓你經過配置文件,一個由Service Container 讀取的配置文件來產生實際配置的類。可是程序也有可能變得不易讀了,程序的性能也有可能還會降低。

參考:

  • http://en.wikipedia.org/wiki/Hollywood_Principle
  • http://en.wikipedia.org/wiki/Inversion_of_Control

High Cohesion & Low/Loose coupling & – 高內聚, 低耦合

這個原則是UNIX操做系統設計的經典原則,把模塊間的耦合降到最低,而努力讓一個模塊作到精益求精。

  • 內聚:一個模塊內各個元素彼此結合的緊密程度
  • 耦合:一個軟件結構內不一樣模塊之間互連程度的度量

內聚意味着重用和獨立,耦合意味着多米諾效應牽一髮動全身。

參考:

  • http://en.wikipedia.org/wiki/Coupling_(computer_science)
  • http://en.wikipedia.org/wiki/Cohesion_(computer_science)

Convention over Configuration(CoC)– 慣例優於配置原則

簡單點說,就是將一些公認的配置方式和信息做爲內部缺省的規則來使用。例如,Hibernate的映射文件,若是約定字段名和類屬性一致的話,基本上就能夠不要這個配置文件了。你的應用只須要指定不convention的信息便可,從而減小了大量convention而又不得不花時間和精力囉裏囉嗦的東東。配置文件不少時候至關的影響開發效率。

Rails 中不多有配置文件(但不是沒有,數據庫鏈接就是一個配置文件),Rails 的fans號稱期開發效率是 java 開發的 10 倍,估計就是這個緣由。Maven也使用了CoC原則,當你執行mvn -compile命令的時候,不須要指源文件放在什麼地方,而編譯之後的class文件放置在什麼地方也沒有指定,這就是CoC原則。

參考:http://en.wikipedia.org/wiki/Convention_over_Configuration

Separation of Concerns (SoC) – 關注點分離

SoC 是計算機科學中最重要的努力目標之一。這個原則,就是在軟件開發中,經過各類手段,將問題的各個關注點分開。若是一個問題能分解爲獨立且較小的問題,就是相對較易解決的。問題太過於複雜,要解決問題須要關注的點太多,而程序員的能力是有限的,不能同時關注於問題的各個方面。正如程序員的記憶力相對於計算機知識來講那麼有限同樣,程序員解決問題的能力相對於要解決的問題的複雜性也是同樣的很是有限。在咱們分析問題的時候,若是咱們把全部的東西混在一塊兒討論,那麼就只會有一個結果——亂。

我記得在上一家公司有一個項目,討論就討論了1年多,項目原本不復雜,可是沒有使用SoC,所有的東西混爲一談,再加上一堆程序員注入了各類不一樣的觀點和想法,整個項目一會兒就失控了。最後,原本一個1年的項目作了3年。

實現關注點分離的方法主要有兩種,一種是標準化,另外一種是抽象與包裝。標準化就是制定一套標準,讓使用者都遵照它,將人們的行爲統一塊兒來,這樣使用標準的人就不用擔憂別人會有不少種不一樣的實現,使本身的程序不能和別人的配合。Java EE就是一個標準的大集合。每一個開發者只須要關注於標準自己和他所在作的事情就好了。就像是開發鏍絲釘的人只專一於開發鏍絲釘就好了,而不用關注鏍帽是怎麼生產的,反正鏍帽和鏍絲釘按標來就必定能合得上。不斷地把程序的某些部分抽像差包裝起來,也是實現關注點分離的好方法。一旦一個函數被抽像出來並實現了,那麼使用函數的人就不用關心這個函數是如何實現的,一樣的,一旦一個類被抽像並實現了,類的使用者也不用再關注於這個類的內部是如何實現的。諸如組件,分層,面向服務,等等這些概念都是在不一樣的層次上作抽像和包裝,以使得使用者不用關心它的內部實現細節。

說白了仍是「高內聚,低耦合」。

參考:http://sulong.me/archives/99

Design by Contract (DbC) – 契約式設計

DbC的核心思想是對軟件系統中的元素之間相互合做以及「責任」與「義務」的比喻。這種比喻從商業活動中「客戶」與「供應商」達成「契約」而得來。例如:

  • 供應商必須提供某種產品(責任),而且他有權指望客戶已經付款(權利)。
  • 客戶必須付款(責任),而且有權獲得產品(權利)。
  • 契約雙方必須履行那些對全部契約都有效的責任,如法律和規定等。

一樣的,若是在程序設計中一個模塊提供了某種功能,那麼它要:

  • 指望全部調用它的客戶模塊都保證必定的進入條件:這就是模塊的先驗條件(客戶的義務和供應商的權利,這樣它就不用去處理不知足先驗條件的狀況)。
  • 保證退出時給出特定的屬性:這就是模塊的後驗條件——(供應商的義務,顯然也是客戶的權利)。
  • 在進入時假定,並在退出時保持一些特定的屬性:不變式。

契約就是這些權利和義務的正式形式。咱們能夠用「三個問題」來總結DbC,而且做爲設計者要常常問:

  • 它指望的是什麼?
  • 它要保證的是什麼?
  • 它要保持的是什麼?

根據Bertrand Meyer氏提出的DBC概念的描述,對於類的一個方法,都有一個前提條件以及一個後續條件,前提條件說明方法接受什麼樣的參數數據等,只有前提條件獲得知足時,這個方法才能被調用;同時後續條件用來講明這個方法完成時的狀態,若是一個方法的執行會致使這個方法的後續條件不成立,那麼這個方法也不該該正常返回。

如今把前提條件以及後續條件應用到繼承子類中,子類方法應該知足:

  1. 前提條件不強於基類.
  2. 後續條件不弱於基類.

換句話說,經過基類的接口調用一個對象時,用戶只知道基類前提條件以及後續條件。所以繼承類不得要求用戶提供比基類方法要求的更強的前提條件,亦即,繼承類方法必須接受任何基類方法能接受的任何條件(參數)。一樣,繼承類必須順從基類的全部後續條件,亦即,繼承類方法的行爲和輸出不得違反由基類創建起來的任何約束,不能讓用戶對繼承類方法的輸出感到困惑。

這樣,咱們就有了基於契約的LSP,基於契約的LSP是LSP的一種強化。

參考:http://en.wikipedia.org/wiki/Design_by_contract

Acyclic Dependencies Principle (ADP) – 無環依賴原則

包之間的依賴結構必須是一個直接的無環圖形,也就是說,在依賴結構中不容許出現環(循環依賴)。若是包的依賴造成了環狀結構,怎麼樣打破這種循環依賴呢?有2種方法能夠打破這種循環依賴關係:第一種方法是建立新的包,若是A、B、C造成環路依賴,那麼把這些共同類抽出來放在一個新的包D裏。這樣就把C依賴A變成了C依賴D以及A依賴D,從而打破了循環依賴關係。第二種方法是使用DIP(依賴倒置原則)和ISP(接口分隔原則)設計原則。

無環依賴原則(ADP)爲咱們解決包之間的關係耦合問題。在設計模塊時,不能有循環依賴。

參考:http://c2.com/cgi/wiki?AcyclicDependenciesPrinciple

上面這些原則可能有些學院派,也可能太爲理論,我在這裏說的也比較模糊和簡單,這裏只是給你們一個概貌,若是想要了解更多的東西,你們能夠多google一下。

不過這些原則看上去都不難,可是要用好卻並不那麼容易。要能把這些原則用得好用得精,而不教條,個人經驗以下:(我覺得這是一個理論到應用的過程)

  1. 你能夠先粗淺或是表面地知道這些原則。
  2. 但不要急着立刻就使用。
  3. 在工做學習中觀察和總結別人或本身的設計。
  4. 再回過頭來了回顧一下這些原則,相信你會有一些本身的心得。
  5. 有適度地去實踐一下。
  6. Goto第 3步。

3—邏輯層 vs 物理層

Layer 和Tier都是層,可是他們所表現的含義不一樣,Tier指的是軟件系統中物理上的軟件和硬件,具體指部署在某服務器上,而Layer(邏輯層)指軟件系統中完成特定功能的邏輯模塊,邏輯概念。

Layer是邏輯上 組織代碼的形式。好比邏輯分層中表現層,服務層,業務層,領域層,他們是軟件功能來劃分的。並不指代部署在那臺具體的服務器上或者,物理位置。

Tier這指代碼運行部署的具體位置,是一個物理層次上的劃爲,Tier就是指邏輯層Layer具體的運行位置。因此邏輯層能夠部署或者遷移在不一樣物理層,一個物理層能夠部署運行多個邏輯層。

從Layer和Tier就會延伸到邏輯架構和物理架構。咱們一個邏輯分層(N-Layer)的部署運行環境能夠在一臺或者是多臺服務器,因爲物理環境的多樣性,邏輯層次的部署也具備多樣性。這就須要咱們必須瞭解物理架構和邏輯架構。

大多數狀況下咱們所說的N層應用系統指的是物理模型,具體模塊的分佈物理位置。客戶端,服務層,邏輯層,數據庫服務器,與咱們的邏輯模型之間並非一對一的關係。邏輯上的分層架構與物理位置上的服務器數量和網絡邊界多少無關,邏輯架構層次只與咱們的功能劃分相關,是按照功能劃分。經典的3-Layer架構:表現層,業務層,數據訪問層,他們可能運行在同一物理位置上。也能夠是3臺計算機上,這並非邏輯架構所關注的。邏輯層次和物理分層數量關係爲:邏輯層數必須不小於物理層數,由於一個物理層能夠部署一個或者多個邏輯層次,邏輯層次只能遷移在不一樣的物理環境。

邏輯層次的架構能幫助咱們解決邏輯耦合,達到靈活配置,遷移。

一個良好的邏輯分層能夠帶來:

  1. 邏輯組織代碼
  2. 易於維護
  3. 代碼更好的重用
  4. 更好的團隊開發體驗
  5. 代碼邏輯的清晰度

一個良好的物理架構能夠帶來:

  1. 性能的提高
  2. 可伸縮性
  3. 容錯性
  4. 安全性

邏輯層次越多會影響程序運行的性能,但代碼層次的低耦合,鬆散化,是須要架構師的權衡的,我以爲通常應用程序的瓶頸並不在這裏。

4—服務層的簡單理解

在ddd設計中咱們常常會提到服務層,服務層是什麼?職責是什麼?有什麼好處?。

先看簡單的層次圖(注:這裏並無考慮其餘多餘的領域邏輯數據層存儲,或者UOW這些細節)

個人理解是服務層是處於個人應用程序業務層和表現層之間的應用程序邊界,邊界多是很薄的一層類設計或者是分佈式服務網絡躍點。它是一個與技術無關的名詞。由表現層直接調用,契約,執行命令(修改狀態(CUD))或者是查詢返回dto(數據遷移對象)(cms,命令-查詢分離)。他對業務邏輯層接口很清楚,組織業務邏輯 微服務造成宏服務,適配表現層。

這裏談到宏服務和微服務,宏服務有一些列粗粒度的服務組成。用戶的一次操做usecase,好比電子商務下單,CreateOrder就是一個宏服務,而不是下單中的細粒度的商品庫存檢查,訂單合法性等。而與之對應的微服務(有時也叫應用程序服務),則表現爲問題領域邏輯細節,就如上面的庫存檢查和合法性檢查這些細粒度的服務。宏服務是由一個或者多個微服務組成,有時咱們的usecase邏輯很簡單服務層僅由單一微服務組成,變現爲很簡單的幾句微服務調用。

服務層的職責:

1:在面軟件開發無論是結構化編程(sp)仍是面向對象編程(oop)咱們一直都強調高內聚低耦合,分離關注點(soc)。服務層處於應用程序和業務層之間,應用邊界,使得兩次直接解耦,利用第三個對象破壞兩對象直接的依賴,並轉化適配領域對象(do)和試圖對象(vo)的差別。

2:服務層隱藏了業務邏輯層的細節,其內部須要組織業務微服務,提供更宏觀,面向表現層的服務邏輯,利用契約接口暴露,包裝。系統全部的交互都是從表現層進入。

目前流行SOA架構,提供了一種分佈式服務架構,以服務爲關注點,提升服務和業務邏輯的重用,可是這裏說的服務並非特定的技術wcf或者webservice,服務同時候多是一次規定契約的一些列粗粒度組織的類組成。可是利用SOA或者MTS創建服務會讓咱們的服務獲得跟多的附加優點,例如安全,事物,日誌,擴展性的提高。

服務層帶來的優點:如上所述服務層爲表現層提供的同一的接口契約和入口。讓咱們的業務層能夠關注與實現問題領域邏輯,問題領域實際需求。組織微服務避免太多的細粒度服務的調用充斥在咱們的項目表現層和問題領域中,過多的交互。若是採用soa等服務領域可讓咱們的應用程序輕易的跨過應用程序邊界和網絡躍點。可是須要付出一點的性能代價。

數據遷移對象(dto)就是攜帶數據穿過應用程序邊界的對象,減小數據的交互次數,經常咱們將其做爲值對象,只是一組簡單的get,set屬性組成,不存在行爲操做,僅僅爲數據的載體。在領域設計中dto是一個很重要的模式,不是咱們全部的領域對象都能輕鬆的到達表現層,僅僅表現層和領域層部署在同一物理位置。若是須要穿過網絡躍點或者進程邊界,由於領域對象使咱們的業務的核心存在不少的天然世界的關係,依賴,甚至可能存在循環依賴好比電商用戶和訂單,用戶用戶一組訂單的集合,而每一個訂單都指向一個特定的用戶,咱們就必須破換掉這種循環依賴,纔可能使其可序列化,穿過躍點。其次咱們的領域對象每每都是一堆領域富對象,存在大量數據,不少時候咱們的場景並不須要所有的數據信息。有了dto的存在就能很好的解決這些問題,是的咱們的項目變得simple(keep it simple,Stupid。 KISS原則)。

可是與此同時dto存在會爲咱們帶來一些額外的複雜度,咱們必須有一層do到dto的映射適配層。

理論上完美的設計咱們須要爲每個應用定義一個dto,可是在一個複雜的系統中咱們可能存在不少的領域對象,加入500個do,每一個do通常都會存在多個dto,這將一個增長一個龐大的集合和mapping邏輯,對於維護也存在不小的挑戰。在軟件領域存在一句話就是bug的數量隨着代碼量增長,代碼量增長鬚要測試點也隨着增長。除非咱們必須跨越應用程序網絡躍點邊界,我以爲不然咱們也能夠存在一些簡單do的直接使用。根據世界項目,情形由咱們的架構師決定。

5—SOA面向服務架構簡述

在上篇中咱們簡單談了下架構設計中服務層的簡單理解,在這裏咱們將繼續服務層的架構,在本節咱們將重點在於分佈式服務。在分佈式系統中表現層和業務邏輯層 並不處於同一物理部署,因此咱們必須存在分佈式服務,以契約方式發佈於網絡中,咱們的關注點在於服務,面向服務編程,這種經過組合業務邏輯暴露可用服務的架構叫作面向服務架構(SOA)。

SOA強調一個鬆耦合,基於宏服務的架構,經過契約暴露給服務消費者可用的服務交互。SOA是以服務爲組成構建,原則有:

1.邊界清晰:

服務層是消費者交互到系統業務的惟一入口,全部咱們的服務必須可以被消費者所理解,以及最好處理Request/Response基於消息交換RPC調用,職責明確單一.還有咱們更但願咱們的服務爲做用明確的,CQS(命令-查詢分離原則).

2.服務的自治性

服務自治主要表如今每一個服務都是獨立的,其系統部署,管理監控都是獨立的。自治體現了服務的鬆耦合,但並非服務就是一個孤島,其能夠經過消息交換消費其餘服務。

3.使用契約(接口和數據載體),而非實現

這也是面向對象設計第一原則。在咱們的服務設計中SOA一個重要目標就是互操做,基於SOAP等標準協議實現跨平臺互操做,可能存在異構系統。因此咱們該選擇接口而不是語言具體的類以及基於消息交互。服務對於開發就是一些列行爲的組合,數據契約就是數據遷移對象,數據載體。契約使得咱們並不關心服務的內部實現,而只關心提供了那些服務,服務的簽名如何,怎麼調用之類的。

4.兼容性基於策越

對於消費者來講服務是否能知足他的需求,這須要服務語義兼容,語義兼容也應該經過可訪問方式暴露。是的服務可發現。

SOA是一種設計原則規範,其目標在於爲複雜系統提供互操做性和以服務爲基礎組件構造系統邏輯。把具體的業務邏輯和流程屏蔽,暴露出用戶可用的行爲集合。SOA是一中原則而非集體技術。wcf,webservice是具體SOA技術。同時SOA也不是咱們的目標,客戶是不與關心咱們採用soa與否,這只是咱們對系統的一種解決方案。

SOA優點在於給咱們提供更好的代碼重用,版本控制,安全控制,擴展延伸性。同時下降和服務的耦合,交互必須依賴於服務契約和數據契約,並不關心服務的內部實現。在咱們的版本升級,修改過程當中能夠徹底能夠從新實現替換原有服務,並不會影響消費程序的使用。

最後咱們必須的說下當下流行的restfull,一般咱們認爲這是一種風格,而非架構,是由Roy Thomas Fielding在其博士論文 《Architectural Styles and the Design of Network-based Software Architectures》中提出REST是英文Representational State Transfer的縮寫,中文翻譯爲「表述性狀態轉移」。是一種基於web的架構,它很好的利用http協議的method。根據不一樣的method表示對資源的不一樣語義操做。其核心在於將發佈在網絡的一切事物歸屬爲資源,每一個資源定位於一個資源定位符(URI)。以及無狀態,緩存,分層架構。在微軟最新的WCF resetfull,web api應用框架。以及wcf ria ,wcf data service,須要的注意的是微軟同時候加入的本身的oData協議(開元數據協議)。

最後說一點:我以爲無論是服務或者resetfull服務咱們都必須定義契約,依賴於契約,雖然微軟的而技術容許咱們直接寄宿服務類,可是對於服務的擴展和延伸而言,說這句話的緣由在於我最近看見一些直接寄宿服務類的resetfull架構。

6—業務邏輯層簡述

業務邏輯層是專門處理軟件業務需求的一層,處於數據庫之上,服務層之下,完成一些列對Domain Object的CRUD,做爲一組微服務提供給服務層來組織在暴露給表現層,如庫存檢查,用法合法性檢查,訂單建立。

業務邏輯層包含領域對象模型,領域實體,業務規則,驗證規則,業務流程。1:領域對象模型爲系統結構描述,包含實體功能描述,實體之間的關係。領域模型處於天生的複雜性:2:領域實體:業務層是一些操做業務對象(BO)的處理。業務對象包含數據和行爲,是一個完整的業務對象。其不一樣於上節架構設計中服務層的簡單理解提到的數據遷移對象(dto),對於dto存在數據的,不存在行爲,dto是bo(ddd中又稱do)的子集,負責與特定界面需求的扁平化實體,dto僅僅是一個數據載體,須要跨越應用程序邊界,而業務對象則不會存在複製遷移,每每一個業務對象存在一個或者多個數據遷移對象。3:業務最大的邏輯就在處理一些列現實世界的規則,這也是軟件中最容易變化的部分,這裏一般會出現咱們衆多的if-else或者switch-case的地方。也這由於若是說以我的以爲在咱們的項目最應該關係和分離需求的層次。4:驗證規則:業務規則很大程度上也是對對象的數據驗證,驗證業務對象的當前數據狀態。我以爲在每一個業務對象上都應該存在一個對外部對象暴露的驗證接口,能夠考慮微軟企業庫的VAB 基於Attribute聲明式驗證或者上節FluentValidation驗證組件基於IOC的解耦。

業務層模式:在常見的業務層模式中主要分爲過程是模式和麪向對象模式。過程模式有是事務性腳本和表模式,而面向對象模式爲活動記錄模式和領域驅動模式。理論上說事務性腳本模式是最簡單的開發模式,其前期投入下,但隨着項目週期和複雜度上升明顯,而領域模型(DDD)前期投入較大,可是理論上說是隨着項目週期和複雜度呈線性增長,固然這些都是理論值。

1:事務腳本模式是業務邏輯層最簡單的模式,面向過程模式。該模式以用於的操做爲起點,設計業務組件,即業務邏輯直接映射到用戶界面的操做。這一般是從表現層邏輯出發,表現層我須要什麼業務層提供什麼,直到數據層。針對沒一個用戶的新功能都須要新增一個從UI到關係數據庫的分支流程。其使用與邏輯不是很複雜或者變化不大穩定的應用系統開發。其不須要付出與業務無關的額外代價,而且在現代VS之類的IDE幫助下可以很快的進行快速應用開發(RAD)。也因爲這種優點,也是其最大的劣勢,程序中充滿了IF-else,switch-case之類的邏輯或者大量的static的方法,每一個功能都是一個程序分支,這對代碼沒法重用。編碼不易於維護,對複雜項目和變化需求不適應。

2:表模式:爲每一個數據庫表定義一個表模塊類,包含操做該數據的全部行爲方法。做爲一個容器,將數據和行爲組織在一塊兒。其對數據的粒度針對於數據表,而非數據行,所以須要以集合或者表傳遞數據信息。表模式基於對象可是徹底又數據庫驅動開發,在業務模型和數據庫關係模型顯著差別的狀況下,應對需求,並非那麼適合。可是在.net中提供的一些列如強類型DataSet等IDE的輔助下自動生成大量的代碼,也是一個不錯的選擇,由於部分數據庫的操做趨於自動化。表模式沒太過於關注業務,而是關注數據庫表結構。而業務邏輯和領域問題纔是軟件核心。

3:活動記錄模式:一個以數據庫表一行Row爲對象,而且對象中包含行爲和數據的模式方法。其數據對象很大程度的接近數據庫表結構。在活動記錄模式對象中一般也包含操做對象的CRUD行爲,數據驗證等業務規則。對於業務不是很複雜,對象關係與關係模型映射不具備很大差別狀況,活動記錄模式會運用的很好。活動模式比較簡單化設計,在上現行的不少如Linq to sql,ActiveRecord框架的輔助下,將針對問題領域不是太過複雜的項目十分有用。可是其模式和數據庫表結構的相互依賴,致使若你修改數據庫結構,你不得不一樣時修改對象以及相關邏輯。若是不能保證數據庫關係模型和對象模式的很大程度的類似這就進入的困境。

4:領域模型:在前面的幾種模式都是項目開始站在了以數據爲中心的角度,而不是業務自己的問題領域。而領域模型關注系統問題領域,首先開始爲領域對象設計。與活動記錄模式來講,領域模型徹底站在了問題領域業務概念模型一邊,與數據庫,持久化完成獨立,其推崇持久化透明(POCO)。其能夠充分利用面向對象設計,不受持久化機制的任何約束。其實徹底又業務驅動出來的。可是其最大的優點如上各個模式同樣也是其最大的劣勢對象模型和關係模型具備自然的阻抗,咱們的領域實體遲早須要映射到持久化機制。還好的是當前有NHibearnate,EF,Fluent NHibearnate這類ORM框架輔助。在DDD中包含UOW,倉儲,值類型和聚合根,領域事件,領域跟蹤一類的概念,這將在之後具體說明。

模式的選擇在與架構師的決定,這也是架構師具備挑戰意義的職責,須要根據具體的項目需求,團隊,我的等外界因素最終決定,不存在萬能的模式,也不存在完美的設計。

7—設計箴言理解

今天和師弟聊天聊到他們項目開發,有些同事老是提早考慮性能優化,需求變動又是一大堆的重寫,讓我想起了Donald Knuth 提到的:對軟件的過早地優化是萬惡的根源。這裏就簡單的說幾條重要的軟件名人哲學。

1:軟件中惟一不變的就是變化。

在軟件開發過程當中需求是不停的變化,隨着客戶對系統的認識,和現有開發功能和軟件的認識,也許以開始他提出的需求就是背離的。記得網上有一句笑話,師說需求變化的:

程序員XX遭遇車禍成植物人,醫生說活下來的但願只有萬分之一,喚醒更爲渺茫。可他的Lead和親人沒有放棄,他們根據XX工做如命的做風,天天都在他身邊念:「XX,需求又改了,該幹活了,你快來呀!」,奇蹟終於發生了,XX醒來了,第一句話:「需求又改了

在設計和架構中,凡事無絕對,做爲架構師或者項目負責人你必須永遠的清晰認識到沒有完美的架構和設計,沒有萬能的軟件。只存在當前環境,需求方案,團隊人員素質,物理環境,安全等綜合因素下的合適方案,因爲總總緣由你的解決方案可能不是某一個單一因素下的最優解。站在這個位置你須要作的是找到這個綜合下的最優解,權衡。不要只從表面說某我的某個團隊的解決方案怎麼查怎麼很差,或者這就是當時綜合因素的最優解,站在一樣的位置環境你不必定作得更好。在架構設計和人生,在我看來很類似,老是有一堆抉擇,每一次的抉擇都會帶來得和失,權衡得失取捨。

2:KISS:(Keep It Simple,Stupid):

保持簡單,但不過於太簡單。在《UNIX下的編程哲學》中提到不少保持設計簡單,咱們能清晰看到這條原則。如今視覺設計,都崇尚簡約設計,簡單而不庸俗,而不是一大堆的豪華奢侈打造。VB編程開始的可視化設計,可見便可得,google的首頁,商業風格。在咱們的軟件設計中也須要簡潔的設計,用戶須要的是可見可量化的功能的正確性,而是你運用了多牛b的技術模式,但毫不是一味的太過於簡單。你想把意見簡單的事情作複雜化是很容易的事情,可是把一件複雜的事情簡單化卻不那麼容易。簡單的人生就是幸福。可是這裏須要說明的是簡單是優秀的,但簡單是有底線邊界的,超過底線的簡單也有變得稚幼。好比事務性腳本模式比其餘3中常見模式都簡單,但每每複雜的需求它不是最優解,由於他太過於簡單了(若是你還不瞭解是事務性腳本能夠參見這裏架構設計-業務邏輯層簡述)。

3:面向抽象編程。

在設計模式,架構模式,OO中都是一條徹底的主線,做爲oo第一原則存在。我不起那個軟件牛人曾說過:請牢記沒有接口的話就不要開始實現。這句話也許過於偏激,可是若是你接口理解爲不變或者不易變的話,理解或契約(公司和你的合同)更貼切些吧(多是一個不變的類,若是你能確定的說出你的這個實如今之後,在項目開發維護中是不會變得,我以爲這也是接口,接口在於不變和不易變),你也許會贊成這句話。對於目前的需求你確定可以沒有抽象沒夠接口徹底寫出完美的代碼,可是第一條中咱們說明的軟件中惟一不變的就是變化,在將來的需求中你可以很好的同樣的優秀嗎?若是不能,那麼我認爲面對當前需求就該爲之後提供擴展延伸。

我我的理解23中設計模式中大多數基本都是圍繞着這個Program to an interface, not an implementation(依賴接口而不是實現)第一原則爲目的。固然咱們也不能不說還有第二原則:組合優先於繼承。之後的什麼DIP(依賴倒置,IOC的原則),LSP(里氏替換),OCP(開閉原則)等等都是他們的延伸和擴展。在追溯的話這一些列都是爲了軟件系統「高內聚,低耦合」(能夠簡敘述爲:功能完備(高內聚)的對象之間是靠接口(低耦合)通信交互的),內聚是描述的功能性完備程度,耦合是表述模塊間的依賴程度。這裏插一句話某同事給我說依賴接口不是還有依賴嘛,我但願的是沒有耦合,個人回答是:計算機二八原則說明了這一切,既然事務出如今一塊兒了,那毫不是偶然狀況,因此他們之間一定存在依賴,在軟件設計中咱們所能作的就是引入中間對象使其變爲間接依賴,而減小他們之間的依賴,而咱們但願這個中間對象是個相對穩定的,設計中一切都是一個詞:間接,分層,mvc,mvp,soa,中間件等等都是體現直接依賴變爲間接依賴。說這個話題的緣由是引出咱們「高內聚,低耦合」行之有效的方法SOC(分離關注點),這不僅是OO的任然對面向過程編程行之有效,他是在20年前 SP(結構化編程)中提出來的。

若是你想對設計原則有更多的瞭解,能夠參見這裏《java與模式》讀書心得。

4:首先考慮可維護,延伸性,過後優化

這裏也是本文的原由,正如開篇所說,Donald Knuth 提到的:對軟件的過早地優化是萬惡的根源。在開發的時候咱們不須要進行任何性能的優化,即便你認爲這裏可能存在性能的瓶頸,你須要考慮的更多的是設計的擴展和延伸性,之後的繼續添加新功能和維護。對於用戶需話要的需求,性能優化不少時候只是做爲一個更好的體驗存在。只有當真正出現性能瓶頸的時候,你才須要作性能的優化。一個可延伸可擴展,井井有條,代碼清晰的模塊,對於你的優化也是件容易的事情,在對項目後期對於項目的整體需求明白下你也有獲得更多的優化方案。在重構模式中一樣也提倡時候優化。過早的優化致使你的項目會越陷越深,到最後才知道用戶其實根本不須要這麼高的需求,或者是用戶根本不經常使用的功能模塊。優化也須要有標準,多少時間是用戶能忍受的,目前是多少時間。每每用戶對性能要求的只有那個少許經常使用的操做,而對於功能性需求的變動倒是無止境的,維護成本倒是高昂的。

最後說一句,常常有人說反射性能低下,對咱們必須認可反射比其餘方案性能是很差,可是咱們有解決方案:緩存。在則說性能低下,是以什麼什麼標準?用戶的接受程度?反射咱們能夠有其替代方案Emit,Expression tree。從反射,Expression tree,Emit的選擇,其使用難度在提高,開發效率在增長,性能在改善。本人通常卻傾向於Expression tree,兩種劇中吧。

5:繼承是爲了多態而不是重用

OOP中能夠編寫一個類,而後我能夠不斷的繼承重用去擴展新需求。這是類的重用,是所有的重用?重用這個詞看上去也許更加的微妙。多態是面向對象的核心特徵之一,也不記不清那裏聽到的:重用只是繼承的附帶功能。在咱們的繼承體系中不宜龐大若是一個擁有4,5層的繼承體系,對你的理解也增長難度,並且集成體系必須是個乾淨的繼承體系,知足LSP(里氏替換原則):在全部用到父類的地方均可以替換爲子類,還能正常準確工做。這就要求你繼承更多的是修改擴展父類的行爲,儘可能避免狀態。繼承只是不要爲了重用的爲目的,在恰當的時機更好的辦法是實現一個徹底的類來替換不能知足現有需求的類。這也是oo原則第二原則吧,組合優先於繼承。組合好比設計模式中的策略模式,你獲得的是一個算法組合功能個數是一個笛卡爾積。但也是絕對的組合,只是優先,不是取代,軟件和現實世界都是充滿了矛盾的,就如開篇第一條「軟件中惟一不變的就是變化」就是最大的矛盾,來自辯證惟物主義,你要作的是權衡。組合表述的是總體的替換,如策略模式模式的算法總體替換。繼承是部分的少許的擴展修改行爲,好比設計模式中的模版方案,在父類的流程控制下,部分步驟的修改,數據,事務的流轉控制權在父類。這條在最後說一句:設計模式不是萬能的,只是前人的優秀經驗,是依賴於場景存在的,瞭解設計模式我以爲更重要的是其使用場景,在碰見同類場景的時候知道能夠有這種模式做爲解決方案或許更好,僅做爲供你選擇的解決問題方案。

6:用戶的一切輸入都是萬惡的

用戶的輸入是屬於咱們系統以外的,是沒法控制的,是不可羅列的。對於用戶來講軟件只是一個黑盒子,不須要,也不必瞭解具體內在實現。對於汽車銷售人員不須要了解發動機螺栓是怎麼上的同樣,他了解宣傳的是能有什麼優點,能給用戶帶來那些方面的知足,價格?性能?速度?豪華?….對於門戶網站來講你對應的用戶不只是可信任的用戶,可能還有競爭對手黑客攻擊行爲。若是你的系統信任於用戶的輸入,遲早一天總會「紙包不住火的」,用戶有意無心的一次輸入就可能致使你係統的功能性的全盤崩潰,你不該該限制用戶的操做,你是不能命令用戶該輸入什麼不能輸入什麼,好比某天某人使用用戶可能降工資了或者挨批了,心情很差,你也許會潛意思的對你的系統進行挑戰。

說到這裏隨便說一句,之前項目組有人層提過因爲自動化測試服務器運行時間太長了,把部分驗證等邏輯移到單元測試中保證。對於個人理解來講自動化測試近似於集成測試吧,功能性測試,應該是黑盒子。在單元測試中咱們老是假設輸入是正確的,某個依賴也是正確的,驗證輸出的正確。而集成測試重點在於這一些都是層次的組合,貫通,不存在假設的正確性,只有來自測試人員的測試用例獲得預期的輸出。

今天就寫到這裏吧,還有不少可是一下想不起來,後續有機會的話對於重要的也會繼續補上。

現實是矛盾的,沒有完美的設計,也沒有絕對的簡單。生活也是如此就如:簡單就是幸福,快樂就是幸福。那麼簡單的標準是什麼?怎樣纔是快樂?這在於你本身的抉擇,權衡。想起了某次面試和小公司面試官談話,面試官說ORM存在性能問題,並且一直在糾結的說反對DDD,反對模式。本人先說了若是存在了性能問題有什麼解決方案,首先怎麼作若是不能知足再怎麼作,從索引緩存到分表服務集羣,再總結性的一句話:架構如人生,老是要面臨獲得取捨。

8—數據訪問層簡述

在前面簡單描述了下服務層,SOA面向服務架構,架構設計-業務邏輯層,以及一些面面向設計原則理解和軟件架構設計箴言。這篇博客咱們將繼續進入咱們的下一層:數據訪問層。不管你用的是什麼開發模式或者是業務模式,到最後最必須具備持久化機制,持久化到持久化介質,並能對數據進行讀取和寫入CRUD。這就是數據訪問層。你多是利用xml等文件格式磁盤存儲,經常使用的關係數據庫存儲,或者NoSql(not only sql)的內存存儲或文檔存儲等等存儲介質。而這裏我只關心關係數據庫存儲。

數據層須要提供的職責有:

1:CRUD服務。做爲惟一能夠與存儲介質交互的中間層出現,負責業務對象的增長,修改,刪除,加載。

2:查詢服務。這不一樣於CRUD中的R(read),read傾向於的單個對象,元組。而這裏的查詢針對複雜查詢,好比一個國內電商的客戶爲四川的訂單。這裏會涉及倉儲層。所謂倉儲模式指的是一個提供業務對象查詢的類,他隱藏了數據查詢的解析步驟,封裝sql解析邏輯。

3:事務管理。這裏所說的是業務事務,在一個應用系統中每次請求都會產生屢次的多數據對象的新增,修改,刪除操做。若是咱們每次都依次代開數據庫鏈接,準備數據包,操做數據庫,關閉數據鏈接。這些將會給咱們帶來不少沒必要要的性能開銷。數據庫管理員常常會要求「儘可能少的與數據庫交互」,這也必須成爲咱們的開發原則。更好的操做是咱們在內存中創建一個和數據倉庫,維護變化的對象,在業務操做完成一次性提交到數據存儲介質,提供業務事務。業務事務有個很好聽的名字工做單元(UOW),在微軟給咱們提供的DataSet,orm框架都回必須存在業務事務。

4:併發處理。UOW應避免業務數據鏈接的屢次提交打開而出現,但在內存離線操做,這就可能致使數據一致性問題。在多用戶的環境,對數據併發處理須要制定一個策略。通常咱們會採用樂觀併發處理:用戶能夠任意的離線修改,在修改更新時候檢查對象是否被修改,若是被修改者本次更新失敗。簡單的說就是防止丟失修改。防止丟失修改,咱們能夠採用where 加上一系列原值,或者加上修改時間戳或者版本號標記。同時還有許多其餘的併發解決模式,但樂觀併發鎖用到更廣泛。

5:數據上下文:整和全部職責。在數據訪問層概念職責都會有一個共同的暴露給外部的接口。咱們須要一個高層次的組件,來同一提供對數據存儲介質的訪問操做。,同一訪問數據庫CRUD,事務,併發服務的高層次類,叫作數據上下文(Context)。EF中的ObjectContext,NHibernate的session,linq to sql 的DataContext等等。

數據訪問層的一些概念(這裏不會是所有,僅一些我的以爲重要的概念):

1: 數據映射器:將內存中修改的對象提交至存儲介質,則須要要映射邏輯來完成,數據映射器就是就是一個實現將某種類型的業務對象持久化的類(數據映射器模式定義如《P of EAA》)。

2:倉儲層(Repository):在上面提到:所謂倉儲模式指的是一個提供業務對象查詢的類,他隱藏了數據查詢的解析步驟,封裝sql解析邏輯。在面向對象的世界裏咱們用對象進行查詢,返回結果爲對象集。這裏的查詢多是從數據庫,或者來至緩存,這取決你的策略,你倉儲層的實現。

3:工做單元(UOW):Martin Fowler《P of EAA》 定義:工做單元記錄在業務事務過程當中對數據庫有影響的全部變化。操做結束後,做爲一種結果,工做單元瞭解全部須要對數據庫作的改變。在上面第3點業務事務講的差很少,這裏不是累述。

4:標示映射(Identity Map):其做用在於:便於跟蹤業務對象,調用者在一個業務事務中使用的是同一個實例,而不是每次執行產生一個新的對象。表示映射爲一個散列表存儲(散列具備快速定位O(1))。相似於緩存的實現方式,保證了在同一個業務事務數據上下文引用修改同一個業務對象。但毫不同於緩存。從持續時間來講,標示映射生命週期爲業務事務內。實現上等同於數據上下文期。但比起緩存來講其週期過短,根本不能對性能有多大的改善。緩存更重要的命中率,而標示映射保證同一數據上下文采用修改同一個業務對象的引用。

5:樂觀併發鎖:在上面也曾提到,其保證離線操做數據的對數據一致性的衝突解決方法。首先樂觀在於容許離線操做,容忍衝突,防止數據丟失修改,可利用原讀取數據值得where條件或者時間戳,版本號解決。

6:延時加載:對象並非一次性加載完成,而是按照需求屢次加載數據,到用時加載。業務對象太多關聯,數據量太多餘龐大,而咱們每次業務事務須要操做的對象都只會是部分,不須要太多的數據對象。同事業務對象中還存在循環引用,這樣不適於對象總體的一次性加載。延時加載提供了優化,意圖在「儘量的少加載,並按需加載,只加載須要的數據部分」。

7:持久化透明對象(PI或POCO):當對象模型不存在任何外部依賴,特別是對於數據訪問層的依賴,那麼這個模型就是持久化透明的,POCO。一個POCO的對象不須要繼承至某個特定的類,實現特定的接口,或提供專門的構造函數。一個非持久化透明的對象這覺得者存在外部的依賴,而咱們更喜歡領域對象只是一個簡單額c#類,能夠在持久化層等獨立切換。這就致使實現的時候咱們沒法很直接的跟蹤業務對象,這就是面向方面編程(AOP)或者代理模式的大顯身手。惋惜AOP在.net中不是那麼直接,不少ORM框架如NHibernate之類的利用代理模式Emit動態注入IL實現跟蹤,添加新的行爲。因此NHibernate中要求領域對象的全部字段屬性方法都必須是虛方法可重寫的。:

8:CQRS(Command Query Responsibility Segregation,命令查詢職責分離):CQRS是在DDD的實踐中引入CQS理論而出現的一種體系結構模式,命令和查詢被分離。

9—存儲過程傳言

在google搜了下「存儲過程 優劣」關鍵字,資料並很少,出現了一篇關於來至51cto的關於存儲過程的優缺點的文章,具體這裏也不指出了。看見文章中對存儲過程的幾個辯解,我的不敢苟同,我的已經很仔細的看了文章的時間是2011年,若是在更前寫年成的話,我的以爲徹底可以理解。因此有了這篇,存儲過程的一些傳言。

1:存儲過程只在創造時進行編譯,之後每次執行存儲過程都不需再從新編譯,而通常SQL 語句每執行一次就編譯一次,因此使用存儲過程可提升數據庫執行速度。

在sql server 2000版本,這個觀點沒錯,倒是如此。可是在sql server2005文檔中很清晰的寫到 sql server2005的執行任何sql,關係引擎會首先查看緩存,判斷是有有其執行計劃。若是有,則將會重用該執行計劃,以減小從新編譯sql語句生成執行計劃的影響。包括Oracle也是這麼作的,因此在咱們常見的數據庫中不存在這所謂的問題。

2:當對數據庫進行復雜操做時(如對多個表進行Update,Insert,Query,Delete 時),可將此複雜操做用存儲過程封裝起來與數據庫提供的事務處理結合一塊兒使用。這些操做,若是用程序來完成,就變成了一條條的SQL語句,可能要屢次鏈接數據庫。而換成存儲,只須要鏈接一次數據庫就能夠了。

這個問題在前面的架構設計-數據訪問層簡述中說過,DBA老是告訴咱們減小數據庫鏈接次數,這是徹底無爭議的,我表述很贊同。可是必定是存儲過程的優點?或者說除了存儲過程就沒其餘方式?在架構設計-數據訪問層簡述中介紹了來自Martin Fowler《P of EAA》的UOW(工做單元)模式,定義爲工做單元記錄在業務事務過程當中對數據庫有影響的全部變化。操做結束後,做爲一種結果,工做單元瞭解全部須要對數據庫作的改變。其主旨是在內存中創建一個和數據倉庫,維護變化的對象,業務對象變化跟蹤,在業務操做完成一次性提交到數據存儲介質,提供業務事務。這模式已經在咱們常見的ORM(EntityFramework,Nhibernate等)中很好的支持了,或許這麼說這也是ORM框架的一個重要特徵。在好比微軟的DataSet也支持,批量更新。

3:存儲過程能夠重複使用,可減小數據庫開發人員的工做量。

在項目中咱們糜爛的重複代碼僅僅在於數據層?更多或許在於業務邏輯的處理,複雜的條件判斷,數據操做的組合。存儲過程是由開發人員開發,仍是數據庫開發人員?每一個公司的數據庫開發人員就僅僅那幾個吧,我見過的公司。存儲過程是否包含業務規則?若是有的話,業務的不停變化,會不會不停的修改關係模型,修改存儲過程,sql的編寫和調試雖然如今工具備必定的支持,可是我以爲沒有開發語言這麼智能方便吧,至少我還沒看見。若是沒有至少簡單的查詢語句,那和普通的sql有什麼差異?減小開發量爲何不選擇ORM之類的動態sql,採用徹底的對象模型開發,只在少部分ORM失效的業務返璞歸真。

4:若是把全部的數據邏輯都放在存儲過程當中,那麼asp.net只須要負責界面的顯示功能,出錯的可能性最大就是在存儲過程。通常狀況下就是這樣。升級、維護方便。

這句話更離譜。邏輯放在存儲過程,便於維護,我也進入過這樣的公司參與過這樣的項目,因爲剛開始新員工,不能全盤否認,我看見的是惱人的存儲過程,惱人是sql,沒看過那個開發人員喜歡sql,特別在每次項目需求變動的時候。後來慢慢接受ddd模式,把業務從sql中掙脫出來。asp.net只須要負責界面的顯示功能,邏輯層次未免太簡單了,我猜想這應是 事務性腳本開發模式,其優劣點在架構設計-業務邏輯層簡述中說過,只能實用於簡單小型的項目。在加上可移植性差,若是你的客戶須要數據庫的升級,sql server到Oracle會怎麼樣。

5:安全性高,可設定只有某此用戶才具備對指定存儲過程的使用權。

安全對於項目來講不只僅在於數據庫,而應是分佈於咱們系統各處。安全關注點應該從表現層到數據庫各層之間都應該有處理。通常比較靈活有效基於角色(域)安全和數據庫安全,物理服務器安全共同使用,這和不適用存儲過程,使用sql並沒什麼衝突。雖然你可能說存儲過程能夠做爲數據庫內部資源實施安全策越。

6:還有些:存儲過程能夠防止sql注入

這個是固然的,毫無爭議。由於用的是參數化方式,你不能隨意拼接字符串,參數化方式可以幫助咱們防止大多數的sql注入。在ado.net中爲咱們提供了很好的參數化支持,使用sql咱們一樣能夠作到,再加上一切開源的安全組件的過濾。

最後存儲過程並非萬惡的,他有他的應用場景,對於複雜邏輯如報表的場景,我會絕不猶豫的放棄ORM,選擇它,由於orm不能知足這種複雜查詢,可是準確的說我選擇的是大量的T-SQL或者是P-SQL,存儲過程就是一堆sql子程序的固定格式。我以爲能夠徹底採用ibatis.net方式的xml配置,更爽些。選擇存儲過程是因爲複雜查詢業務,我相信你們也不會爲了一些複雜的統計把全表數據加載到內存吧。存儲過程開發技術流行與2005前數據爲中心的開發模式,在如今的模式,工具,技術下顯得有些蒼老,但並非一無可取。你也能夠徹底採用基於存儲過程的開發模式開發出很好的系統。

10—表現層模式-MVC

在前面簡述了從服務層到數據層。剩下了表現層,一個再好的中間層表現也必須有一個用戶界面,提供和用戶交互,將用戶行爲輸入轉化爲系統操做,進入後臺邏輯。在當下RAD(快速應用開發)工具的支持下,咱們能夠比較快速的完成UI設計,RAD追求所見即所得的快速反饋,快速應用。表現層也有必定其固定的邏輯(格式化,數據綁定,轉化等等,稱爲UI邏輯)和界面展示。這裏UI邏輯指的是全部用來處理數據顯示在UI界面的邏輯和,將UI用戶輸入行爲轉化爲中間層指令的邏輯,負責UI和中間層數據流和行爲的轉化。不少時候UI是最容易變化的以及最不易測試的邏輯(我一直相信,1:一段好的代碼必定要易於測試。2:重構的前提也必須有足夠的測試保證,才能讓咱們的重構更有節奏更自信),而很大部分UI邏輯卻每每比較穩定的。這Matin Fowler提出的分離表現層模式。表現層模式主要分爲3種大類:MVC,MVP,PM(微軟在sl和wpf起名爲MVVM),這3類模式下延伸了不少變異體mvc在web的 model2(asp.net mvc,主要特徵基於web特有uri路由)。mvp的變種:Passive View(被動視圖)和Supervising Controller(不清楚怎麼翻譯比較好),PM延伸MVVM。其目的都在於將多變的View和UI邏輯分離。

今天要說的是MVC(model-view-controller:模型-視圖-控制器)。在咱們一開始就說結構和編程或者面向對象原則都是爲了實現模塊的高內聚低耦合,而高內聚低耦合行之有效的方式就是分離關注點(SOC)。爲了實現表現UI和表現邏輯的分離,使得他們之間更靈活,而且自治視圖(包含全部表現層代碼的類)。在30年前Trygve Reenskaug提出的MVC模式,或者更確切的說模範。其將表現層分爲3類:model:是視圖展示數據,view用戶交互界面,Controller:將用戶輸入轉化爲中間層操做。

模型(Model):在MVC中模型保持着一個應用程序的狀態,和相應視圖中來自用戶交互的狀態變化。在上圖中咱們能夠看到model會接受來之控制器的狀態變化響應和視圖的顯示狀態查詢view渲染的數據來源。同時model還有經過事件機制(觀察者模式)通知view狀態的改變要求view渲染響應。view和模型之間存在必定的耦合,view必須瞭解model,這也是MVP模式出現緣由之一。正在這裏的模型model能夠是來之分佈式soap或者resetfull的dto(數據傳輸對象),也能夠直接是咱們的領域對象(do)或者數據層返回的數據集,並不嚴格的 要求。

控制器(Controller):控制器是又view觸發,響應用戶界面的交互,並根據表現層邏輯改變model狀態,以及中間層的交互,最終修改model,Controller不會關心視圖的渲染,是經過修改model,model的事件通知機制,觸發view的刷新渲染。控制器和模型的交互式一種「發出即忘」,或者分佈式OneWay的調用。控制器Controller不會主動了瞭解view和view交互,除了惟一的視圖選擇外,控制器須要選擇下一次顯示的視圖view是什麼。通常控制器會請求全局應用程序路由下一個須要顯示的view,在使其呈現出來。

視圖(View):視圖時表現層模式出現的緣由,由於他的多樣性和變化的頻繁性,不易測試(太多外界環境依賴),因此理想的視圖應該儘量的啞,被動,視圖只負責渲染呈現給用戶交互。視圖由一些列GUI組件組成,響應用戶行爲觸發控制器邏輯,修改model狀態使其保持view同步。視圖並須要相應model的變化被動的接受model狀態變化刷新相應給用戶。

MVC最早興起於桌面,但沒有流行起來,知道在web興起後,其變異體Model2在Web中流行起來,.net 下的ASP.NTE MVC。Model2中,未來自客戶端(瀏覽器)的請求,被服務端攔截器(asp.net中HttpModule)根據url格式請求方式等轉發到固定的控制器,調用固定的Action,在Action中隊模型狀態進行修改,並選擇view,view並根據控制器傳來的最新model生產html,css,js前段代碼,並輸出到前段渲染。

在Model2中和原始MVC最大的差異在於:

1:視圖view和模型model之間沒有直接依賴,model並不知道view也不須要事件通知view,view也不需知道model,view操做的都是ViewModel(asp.net mvc 中ViewData容器)。2:控制器顯示傳入視圖數據給view,相應用戶的操做不是來自view,而是出於服務端應用程序前段的攔截器,捕獲url並轉發到相應的控制器,已經調用相應的action方法。

在如今說的MVC每每指的就是Web中Model2模式。在Model2中view是被動的,啞的,簡單。

相關文章
相關標籤/搜索