架構(gòu)引用維基百科:軟件體系結(jié)構(gòu)是構(gòu)建計(jì)算機(jī)軟件實(shí)踐的基礎(chǔ),。與建筑師設(shè)定建筑項(xiàng)目的設(shè)計(jì)原則和目標(biāo),作為繪圖員畫圖的基礎(chǔ)一樣,,一個(gè)軟件架構(gòu)師或者系統(tǒng)架構(gòu)師陳述軟件構(gòu)架以作為滿足不同客戶需求的實(shí)際系統(tǒng)設(shè)計(jì)方案的基礎(chǔ),。從和目的,、主題,、材料和結(jié)構(gòu)的聯(lián)系上來說,軟件架構(gòu)可以和建筑物的架構(gòu)相比擬,。一個(gè)軟件架構(gòu)師需要有廣泛的軟件理論知識(shí)和相應(yīng)的經(jīng)驗(yàn)來實(shí)施和管理軟件產(chǎn)品的高級(jí)設(shè)計(jì)。軟件架構(gòu)師定義和設(shè)計(jì)軟件的模塊化,模塊之間的交互,,用戶界面風(fēng)格,,對(duì)外接口方法,創(chuàng)新的設(shè)計(jì)特性,,以及高層事物的對(duì)象操作,、邏輯和流程,。 目錄: 1—面向?qū)ο笤O(shè)計(jì)原則理解 2—一些軟件設(shè)計(jì)的原則 3—邏輯層 vs 物理層 4—服務(wù)層的簡(jiǎn)單理解 5—SOA面向服務(wù)架構(gòu)簡(jiǎn)述 6—業(yè)務(wù)邏輯層簡(jiǎn)述 7—設(shè)計(jì)箴言理解 8—數(shù)據(jù)訪問層簡(jiǎn)述 9—存儲(chǔ)過程傳言 10—表現(xiàn)層模式-MVC 1—面向?qū)ο笤O(shè)計(jì)原則理解 面向?qū)ο笤O(shè)計(jì)(OOD)核心原則讓我的程序模塊達(dá)到“高內(nèi)聚低耦合”,這是來自于30年前興起的結(jié)構(gòu)化設(shè)計(jì)(structured Design),但是同樣適用于我們的OOD,。 1.高內(nèi)聚: 高內(nèi)聚是指某個(gè)特定模塊(程序,類型)都應(yīng)完成一系列相關(guān)功能,,描述了不同程序,類型中方法,,方法中不同操作描述的邏輯之間的距離相近,。高內(nèi)聚意味可維護(hù)性,可重新性,,因?yàn)槟K對(duì)外部的依賴少(功能的完備性),。如果兩個(gè)模塊之間的修改,互不影響這說明模塊之間是高內(nèi)聚的,。模塊的內(nèi)聚和其擔(dān)當(dāng)?shù)穆氊?zé)成反比,,即,模塊的職責(zé)越多,,模塊的內(nèi)聚性越低,,這也是模塊的單一原則(SRP),SRP提倡每個(gè)類型都最好只承擔(dān)單一的職責(zé),,只有單一的改變因素,。 2.低耦合: 耦合是描述模塊之間的依賴程度,如果一個(gè)模塊的修改,,都有影響另一個(gè)模塊則,,兩模塊之間是相互依賴耦合的。(依賴具有傳遞性,,耦合的兩個(gè)模塊可能間接依賴),,低耦合是我們的設(shè)計(jì)目的,但不是不存在耦合不存依賴,,依賴是必須的,,因?yàn)槟K之間必須通信交互,不過我的設(shè)計(jì)依賴應(yīng)該依賴于不變或者不易變的接口,,無需了解模塊的具實(shí)現(xiàn)(OO封裝性),。 在面向?qū)ο螅何覀兛梢院?jiǎn)述為功能完備(高內(nèi)聚)的對(duì)象之間的交互是依賴于不變或不易變的接口契約(低耦合)。 實(shí)現(xiàn)高內(nèi)聚低耦合:行之有效的方式是分了關(guān)注點(diǎn)(SOC),,將系統(tǒng)拆分成功能不同沒有重疊功能集,。每個(gè)功能只關(guān)注一個(gè)方面(Aspect)保證模塊之間功能沒有或者盡量少的重復(fù)。模塊化內(nèi)部實(shí)現(xiàn)細(xì)節(jié)隱藏,,只暴露必須的接口,,使得模塊之間依賴于抽象,,達(dá)到穩(wěn)定。分離關(guān)注點(diǎn)的思想存在于我們軟件設(shè)計(jì)的各個(gè)領(lǐng)域,。如在.net的世界里SOA(面向服務(wù)架構(gòu))服務(wù)就是關(guān)注點(diǎn),,只暴露出必要的契約。分層架構(gòu)從邏輯上利用接口抽象信息隱藏,,減少依賴,。MVC,MVP也是遵循分了關(guān)注點(diǎn)原則,,達(dá)到表現(xiàn)層和邏輯的分離,。 面向?qū)ο笤O(shè)計(jì)原則: 1.降低耦合度:對(duì)象直接需要交互,這就存在依賴,,為了實(shí)現(xiàn)低耦合就必須減少依賴,,依賴于穩(wěn)定或不易變抽象??紤]如下訂單日志記錄場(chǎng)景:我們需要在訂單每部操作記錄更改日志,。
在這里我們的OrderManager和Logger存在高耦合,Logger類的修改可能導(dǎo)致OrderManager的修改,,而且不能隨意切換我們的日志記錄方式,,比如文件,控制臺(tái),,數(shù)據(jù)庫(kù)等日志方式,。 面向抽象編程提出抽象(接口,abstract類)是不易變的穩(wěn)定抽象,;對(duì)于OrderManager來說我不需要了解日志記錄組件內(nèi)部,只需要明白提供那些接口可用,,怎么用,。
那么我們可以從設(shè)計(jì)模式工廠模式(工廠模式是負(fù)責(zé)一些列相似對(duì)象的創(chuàng)建)Create 日志組件ILogger。 我們的OrderManager 就可以實(shí)現(xiàn)為:
這樣我們的OrderManager就依賴于ILogger,,而隔離Logger具體實(shí)現(xiàn),,將依賴于抽象,把變化縮小到Factory內(nèi)部(同樣也可以用抽象工廠),,如果日志實(shí)現(xiàn)變化我們可以重新實(shí)現(xiàn)ILogger ,,修改Factory邏輯,如果內(nèi)部利用配置我的需求變更轉(zhuǎn)移到配置,。這就是面向?qū)ο蟮谝辉瓌t,,依賴于抽象而隱藏實(shí)現(xiàn)。(利用IOC是一種更好的方式) 2.代碼的重用性:盡量保證相同功能代碼只出現(xiàn)一次(Code once run anywhere),。代碼的重用在面對(duì)對(duì)象設(shè)計(jì)中有繼承和組合兩種方式,,一般推薦組合優(yōu)先,。組合依賴于接口,組合更安全,,易于維護(hù),,測(cè)試。繼承存在父類訪問權(quán)限,,父類的修改導(dǎo)致子類的變化,,太多的繼承也有導(dǎo)致派生類的膨脹,維護(hù)管理也是件頭痛的事,。 3.開閉原則(OCP):表述擁抱需求變化,,盡量做到對(duì)模塊的擴(kuò)展開發(fā),修改關(guān)閉,。對(duì)于新增需求我們完美的做法是新增類型而不是修改邏輯,,這就意味著我們必須使用組合或者是繼承體系(為了避免上一條重用性,我的繼承應(yīng)該是干凈的繼承體系,,派生類應(yīng)該只是新增功能而不是修改來自父類上下文),, 4.里氏替換(LSP):表述派生類應(yīng)該可以在任何地方替代父類使用。并不是所有的子類都可以完全替換子類,,比如設(shè)計(jì)父類私有上下文信息的訪問,,導(dǎo)致子類無法訪問。 5.依賴倒置(DIP):描述組件之間高層組件不應(yīng)該依賴于底層組件,。依賴倒置是指實(shí)現(xiàn)和接口倒置,,采用自頂向下的方式關(guān)注所需的底層組件接口,而不是其實(shí)現(xiàn),。DI框架實(shí)現(xiàn)IOC(控制反轉(zhuǎn))就是DIP很好的插入底層組件構(gòu)造框架(分構(gòu)造注入,,函數(shù)注入,屬性注入),。微軟Unity,,Castle windsor,Ninject等框架支持,。 最后分離關(guān)注點(diǎn),,衍生出聲明式編程,面向方面編程(AOP)實(shí)現(xiàn)縱切關(guān)注點(diǎn),,把具體業(yè)務(wù)邏輯和日志安全等框架集公用邏輯分離,。 2—一些軟件設(shè)計(jì)的原則 以前本站向大家介紹過一些軟件開發(fā)的原則,比如優(yōu)質(zhì)代碼的十誡和Unix傳奇(下篇)中所以說的UNIX的設(shè)計(jì)原則,。相信大家從中能夠從中學(xué)了解到一些設(shè)計(jì)原理方面的知識(shí),,正如我在《再談“我是怎么招聘程序”》中所說的,一個(gè)好的程序員通常由其操作技能,、知識(shí)水平,,經(jīng)驗(yàn)層力和能力四個(gè)方面組成,。在這里想和大家說說設(shè)計(jì)中的一些原則,我認(rèn)為這些東西屬于長(zhǎng)期經(jīng)驗(yàn)總結(jié)出來的知識(shí),。這些原則,,每一個(gè)程序員都應(yīng)該了解。但是請(qǐng)不要教條主義,,在使用的時(shí)候還是要多多考慮實(shí)際情況,。其實(shí),下面這些原則,,不單單只是軟件開發(fā),,可以推廣到其它生產(chǎn)活動(dòng)中,甚至我們的生活中,。 Don’t Repeat Yourself (DRY) DRY 是一個(gè)最簡(jiǎn)單的法則,,也是最容易被理解的。但它也可能是最難被應(yīng)用的(因?yàn)橐龅竭@樣,,我們需要在泛型設(shè)計(jì)上做相當(dāng)?shù)呐?,這并不是一件容易的事)。它意味著,,當(dāng)我們?cè)趦蓚€(gè)或多個(gè)地方的時(shí)候發(fā)現(xiàn)一些相似的代碼的時(shí)候,,我們需要把他們的共性抽象出來形一個(gè)唯一的新方法,并且改變現(xiàn)有的地方的代碼讓他們以一些合適的參數(shù)調(diào)用這個(gè)新的方法,。 參考:http://en./wiki/Don%27t_repeat_yourself Keep It Simple, Stupid (KISS) KISS原則在設(shè)計(jì)上可能最被推崇的,,在家裝設(shè)計(jì),界面設(shè)計(jì) ,,操作設(shè)計(jì)上,,復(fù)雜的東西越來越被眾人所BS了,而簡(jiǎn)單的東西越來越被人所認(rèn)可,,比如這些UI的設(shè)計(jì)和我們中國(guó)網(wǎng)頁(尤其是新浪的網(wǎng)頁)者是負(fù)面的例子,。“宜家”(IKEA)簡(jiǎn)約,、效率的家居設(shè)計(jì)、生產(chǎn)思路,;“微軟”(Microsoft)“所見即所得”的理念,;“谷歌”(Google)簡(jiǎn)約、直接的商業(yè)風(fēng)格,,無一例外的遵循了“kiss”原則,,也正是“kiss”原則,成就了這些看似神奇的商業(yè)經(jīng)典,。而蘋果公司的iPhone/iPad將這個(gè)原則實(shí)踐到了極至,。 把一個(gè)事情搞復(fù)雜是一件簡(jiǎn)單的事,,但要把一個(gè)復(fù)雜的事變簡(jiǎn)單,這是一件復(fù)雜的事,。 參考:http://en./wiki/KISS_principle Program to an interface, not an implementation 這是設(shè)計(jì)模式中最根本的哲學(xué),,注重接口,而不是實(shí)現(xiàn),,依賴接口,,而不是實(shí)現(xiàn)。接口是抽象是穩(wěn)定的,,實(shí)現(xiàn)則是多種多樣的,。以后面我們會(huì)面向?qū)ο蟮腟OLID原則中會(huì)提到我們的依賴倒置原則,就是這個(gè)原則的的另一種樣子,。還有一條原則叫 Composition over inheritance(喜歡組合而不是繼承),,這兩條是那23個(gè)經(jīng)典設(shè)計(jì)模式中的設(shè)計(jì)原則。 Command-Query Separation (CQS) – 命令-查詢分離原則
通常,,一個(gè)方法可能是純的Command模式或者是純的Query模式,或者是兩者的混合體,。在設(shè)計(jì)接口時(shí),,如果可能,應(yīng)該盡量使接口單一化,,保證方法的行為嚴(yán)格的是命令或者是查詢,,這樣查詢方法不會(huì)改變對(duì)象的狀態(tài),沒有副作用,,而會(huì)改變對(duì)象的狀態(tài)的方法不可能有返回值,。也就是說:如果我們要問一個(gè)問題,那么就不應(yīng)該影響到它的答案,。實(shí)際應(yīng)用,,要視具體情況而定,語義的清晰性和使用的簡(jiǎn)單性之間需要權(quán)衡,。將Command和Query功能合并入一個(gè)方法,,方便了客戶的使用,但是,,降低了清晰性,,而且,可能不便于基于斷言的程序設(shè)計(jì)并且需要一個(gè)變量來保存查詢結(jié)果,。 在系統(tǒng)設(shè)計(jì)中,,很多系統(tǒng)也是以這樣原則設(shè)計(jì)的,,查詢的功能和命令功能的系統(tǒng)分離,這樣有則于系統(tǒng)性能,,也有利于系統(tǒng)的安全性,。 參考:http://en./wiki/Command-query_separation You Ain’t Gonna Need It (YAGNI) 這個(gè)原則簡(jiǎn)而言之為——只考慮和設(shè)計(jì)必須的功能,避免過度設(shè)計(jì),。只實(shí)現(xiàn)目前需要的功能,,在以后您需要更多功能時(shí),可以再進(jìn)行添加,。
以前本站有一篇關(guān)于過度重構(gòu)的文章,,這個(gè)示例就是這個(gè)原則的反例。而,,WebSphere的設(shè)計(jì)者就表示過他過度設(shè)計(jì)了這個(gè)產(chǎn)品,。我們的程序員或是架構(gòu)師在設(shè)計(jì)系統(tǒng)的時(shí)候,會(huì)考慮很多擴(kuò)展性的東西,,導(dǎo)致在架構(gòu)與設(shè)計(jì)方面使用了大量折衷,,最后導(dǎo)致項(xiàng)目失敗。這是個(gè)令人感到諷刺的教訓(xùn),,因?yàn)楸緛硐MM可能延長(zhǎng)項(xiàng)目的生命周期,,結(jié)果反而縮短了生命周期。 參考:http://en./wiki/You_Ain%27t_Gonna_Need_It Law of Demeter – 迪米特法則 迪米特法則(Law of Demeter),,又稱“最少知識(shí)原則”(Principle of Least Knowledge),,其來源于1987年荷蘭大學(xué)的一個(gè)叫做Demeter的項(xiàng)目。Craig Larman把Law of Demeter又稱作“不要和陌生人說話”,。在《程序員修煉之道》中講LoD的那一章叫作“解耦合與迪米特法則”,。關(guān)于迪米特法則有一些很形象的比喻:
和狗的四肢說話,?讓店員自己從錢包里拿錢?這聽起來有點(diǎn)荒唐,,不過在我們的代碼里這幾乎是見怪不怪的事情了,。 對(duì)于LoD,,正式的表述如下: 對(duì)于對(duì)象 ‘O’ 中一個(gè)方法’M',,M應(yīng)該只能夠訪問以下對(duì)象中的方法:
在《Clean Code》一書中,有一段Apache framework中的一段違反了LoD的代碼: final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath(); 這么長(zhǎng)的一串對(duì)其它對(duì)象的細(xì)節(jié),,以及細(xì)節(jié)的細(xì)節(jié),,細(xì)節(jié)的細(xì)節(jié)的細(xì)節(jié)……的調(diào)用,增加了耦合,,使得代碼結(jié)構(gòu)復(fù)雜,、僵化,難以擴(kuò)展和維護(hù),。 在《重構(gòu)》一書中的代碼的環(huán)味道中有一種叫做“Feature Envy”(依戀情結(jié)),,形象的描述了一種違反了LoC的情況。Feature Envy就是說一個(gè)對(duì)象對(duì)其它對(duì)象的內(nèi)容更有興趣,,也就是說老是羨慕別的對(duì)象的成員,、結(jié)構(gòu)或者功能,大老遠(yuǎn)的調(diào)用人家的東西,。這樣的結(jié)構(gòu)顯然是不合理的,。我們的程序應(yīng)該寫得比較“害羞”。不能像前面例子中的那個(gè)不把自己當(dāng)外人的店員一樣,,拿過客人的錢包自己把錢拿出來,。“害羞”的程序只和自己最近的朋友交談,。這種情況下應(yīng)該調(diào)整程序的結(jié)構(gòu),,讓那個(gè)對(duì)象自己擁有它羨慕的feature,或者使用合理的設(shè)計(jì)模式(例如Facade和Mediator),。 參考:http://en./wiki/Principle_of_Least_Knowledge 面向?qū)ο蟮腟.O.L.I.D 原則 一般來說這是面向?qū)ο蟮奈宕笤O(shè)計(jì)原則,,但是,我覺得這些原則可適用于所有的軟件開發(fā),。 Single Responsibility Principle (SRP) – 職責(zé)單一原則 關(guān)于單一職責(zé)原則,,其核心的思想是:一個(gè)類,只做一件事,,并把這件事做好,,其只有一個(gè)引起它變化的原因。單一職責(zé)原則可以看作是低耦合,、高內(nèi)聚在面向?qū)ο笤瓌t上的引申,,將職責(zé)定義為引起變化的原因,以提高內(nèi)聚性來減少引起變化的原因。職責(zé)過多,,可能引起它變化的原因就越多,,這將導(dǎo)致職責(zé)依賴,相互之間就產(chǎn)生影響,,從而極大的損傷其內(nèi)聚性和耦合度,。單一職責(zé),通常意味著單一的功能,,因此不要為一個(gè)模塊實(shí)現(xiàn)過多的功能點(diǎn),,以保證實(shí)體只有一個(gè)引起它變化的原因。
Open/Closed Principle (OCP) – 開閉原則 關(guān)于開發(fā)封閉原則,,其核心的思想是:模塊是可擴(kuò)展的,而不可修改的,。也就是說,,對(duì)擴(kuò)展是開放的,而對(duì)修改是封閉的,。
對(duì)于面向?qū)ο髞碚f,,需要你依賴抽象,,而不是實(shí)現(xiàn),23個(gè)經(jīng)典設(shè)計(jì)模式中的“策略模式”就是這個(gè)實(shí)現(xiàn),。對(duì)于非面向?qū)ο缶幊?,一些API需要你傳入一個(gè)你可以擴(kuò)展的函數(shù),比如我們的C 語言的qsort()允許你提供一個(gè)“比較器”,,STL中的容器類的內(nèi)存分配,,ACE中的多線程的各種鎖。對(duì)于軟件方面,,瀏覽器的各種插件屬于這個(gè)原則的實(shí)踐,。 Liskov substitution principle (LSP) – 里氏代換原則 軟件工程大師Robert C. Martin把里氏代換原則最終簡(jiǎn)化為一句話:“Subtypes must be substitutable for their base types”,。也就是,子類必須能夠替換成它們的基類,。即:子類應(yīng)該可以替換任何基類能夠出現(xiàn)的地方,,并且經(jīng)過替換以后,代碼還能正常工作,。另外,不應(yīng)該在代碼中出現(xiàn)if/else之類對(duì)子類類型進(jìn)行判斷的條件,。里氏替換原則LSP是使代碼符合開閉原則的一個(gè)重要保證,。正是由于子類型的可替換性才使得父類型的模塊在無需修改的情況下就可以擴(kuò)展。 這么說來,,似乎有點(diǎn)教條化,,我非常建議大家看看這個(gè)原則個(gè)兩個(gè)最經(jīng)典的案例——“正方形不是長(zhǎng)方形”和“鴕鳥不是鳥”。通過這兩個(gè)案例,,你會(huì)明白《墨子 小取》中說的 ——“娣,,美人也,愛娣,,非愛美人也….盜,,人也;惡盜,,非惡人也,。”——妹妹雖然是美人,,但喜歡妹妹并不代表喜歡美人,。盜賊是人,但討厭盜賊也并不代表就討厭人類,。這個(gè)原則讓你考慮的不是語義上對(duì)象的間的關(guān)系,,而是實(shí)際需求的環(huán)境。 在很多情況下,,在設(shè)計(jì)初期我們類之間的關(guān)系不是很明確,,LSP則給了我們一個(gè)判斷和設(shè)計(jì)類之間關(guān)系的基準(zhǔn):需不需要繼承,以及怎樣設(shè)計(jì)繼承關(guān)系,。 Interface Segregation Principle (ISP) – 接口隔離原則 接口隔離原則意思是把功能實(shí)現(xiàn)在接口中,,而不是類中,使用多個(gè)專門的接口比使用單一的總接口要好,。 舉個(gè)例子,,我們對(duì)電腦有不同的使用方式,比如:寫作,,通訊,,看電影,,打游戲,上網(wǎng),,編程,,計(jì)算,數(shù)據(jù)等,,如果我們把這些功能都聲明在電腦的抽類里面,,那么,我們的上網(wǎng)本,,PC機(jī),,服務(wù)器,筆記本的實(shí)現(xiàn)類都要實(shí)現(xiàn)所有的這些接口,,這就顯得太復(fù)雜了,。所以,我們可以把其這些功能接口隔離開來,,比如:工作學(xué)習(xí)接口,,編程開發(fā)接口,上網(wǎng)娛樂接口,,計(jì)算和數(shù)據(jù)服務(wù)接口,,這樣,我們的不同功能的電腦就可以有所選擇地繼承這些接口,。 這個(gè)原則可以提升我們“搭積木式”的軟件開發(fā),。對(duì)于設(shè)計(jì)來說,Java中的各種Event Listener和Adapter,,對(duì)于軟件開發(fā)來說,,不同的用戶權(quán)限有不同的功能,不同的版本有不同的功能,,都是這個(gè)原則的應(yīng)用,。 Dependency Inversion Principle (DIP) – 依賴倒置原則 高層模塊不應(yīng)該依賴于低層模塊的實(shí)現(xiàn),而是依賴于高層抽象,。 舉個(gè)例子,,墻面的開關(guān)不應(yīng)該依賴于電燈的開關(guān)實(shí)現(xiàn),而是應(yīng)該依賴于一個(gè)抽象的開關(guān)的標(biāo)準(zhǔn)接口,,這樣,,當(dāng)我們擴(kuò)展程序的時(shí)候,我們的開關(guān)同樣可以控制其它不同的燈,,甚至不同的電器,。也就是說,電燈和其它電器繼承并實(shí)現(xiàn)我們的標(biāo)準(zhǔn)開關(guān)接口,,而我們的開關(guān)產(chǎn)商就可不需要關(guān)于其要控制什么樣的設(shè)備,,只需要關(guān)心那個(gè)標(biāo)準(zhǔn)的開關(guān)標(biāo)準(zhǔn),。這就是依賴倒置原則。 這就好像瀏覽器并不依賴于后面的web服務(wù)器,,其只依賴于HTTP協(xié)議,。這個(gè)原則實(shí)在是太重要了,社會(huì)的分工化,,標(biāo)準(zhǔn)化都是這個(gè)設(shè)計(jì)原則的體現(xiàn),。 參考:http://en./wiki/Solid_(object-oriented_design) Common Closure Principle(CCP)– 共同封閉原則 一個(gè)包中所有的類應(yīng)該對(duì)同一種類型的變化關(guān)閉。一個(gè)變化影響一個(gè)包,,便影響了包中所有的類,。一個(gè)更簡(jiǎn)短的說法是:一起修改的類,應(yīng)該組合在一起(同一個(gè)包里),。如果必須修改應(yīng)用程序里的代碼,我們希望所有的修改都發(fā)生在一個(gè)包里(修改關(guān)閉),,而不是遍布在很多包里,。CCP原則就是把因?yàn)槟硞€(gè)同樣的原因而需要修改的所有類組合進(jìn)一個(gè)包里。如果2個(gè)類從物理上或者從概念上聯(lián)系得非常緊密,,它們通常一起發(fā)生改變,,那么它們應(yīng)該屬于同一個(gè)包。 CCP延伸了開閉原則(OCP)的“關(guān)閉”概念,,當(dāng)因?yàn)槟硞€(gè)原因需要修改時(shí),,把需要修改的范圍限制在一個(gè)最小范圍內(nèi)的包里。 參考:http:///cgi/wiki?CommonClosurePrinciple Common Reuse Principle (CRP) – 共同重用原則 包的所有類被一起重用,。如果你重用了其中的一個(gè)類,,就重用全部。換個(gè)說法是,,沒有被一起重用的類不應(yīng)該被組合在一起,。CRP原則幫助我們決定哪些類應(yīng)該被放到同一個(gè)包里。依賴一個(gè)包就是依賴這個(gè)包所包含的一切,。當(dāng)一個(gè)包發(fā)生了改變,,并發(fā)布新的版本,使用這個(gè)包的所有用戶都必須在新的包環(huán)境下驗(yàn)證他們的工作,,即使被他們使用的部分沒有發(fā)生任何改變,。因?yàn)槿绻邪形幢皇褂玫念悾词褂脩舨魂P(guān)心該類是否改變,,但用戶還是不得不升級(jí)該包并對(duì)原來的功能加以重新測(cè)試,。 CCP則讓系統(tǒng)的維護(hù)者受益。CCP讓包盡可能大(CCP原則加入功能相關(guān)的類),,CRP則讓包盡可能?。–RP原則剔除不使用的類),。它們的出發(fā)點(diǎn)不一樣,但不相互沖突,。 參考:http:///cgi/wiki?CommonReusePrinciple Hollywood Principle – 好萊塢原則 好萊塢原則就是一句話——“don’t call us, we’ll call you.”,。意思是,好萊塢的經(jīng)紀(jì)人們不希望你去聯(lián)系他們,,而是他們會(huì)在需要的時(shí)候來聯(lián)系你,。也就是說,所有的組件都是被動(dòng)的,,所有的組件初始化和調(diào)用都由容器負(fù)責(zé),。組件處在一個(gè)容器當(dāng)中,由容器負(fù)責(zé)管理,。 簡(jiǎn)單的來講,,就是由容器控制程序之間的關(guān)系,而非傳統(tǒng)實(shí)現(xiàn)中,,由程序代碼直接操控,。這也就是所謂“控制反轉(zhuǎn)”的概念所在:
控制權(quán)由應(yīng)用代碼中轉(zhuǎn)到了外部容器,,控制權(quán)的轉(zhuǎn)移,是所謂反轉(zhuǎn),。 好萊塢原則就是IoC(Inversion of Control)或DI(Dependency Injection )的基礎(chǔ)原則,。這個(gè)原則很像依賴倒置原則,依賴接口,,而不是實(shí)例,,但是這個(gè)原則要解決的是怎么把這個(gè)實(shí)例傳入調(diào)用類中?你可能把其聲明成成員,,你可以通過構(gòu)造函數(shù),,你可以通過函數(shù)參數(shù)。但是 IoC可以讓你通過配置文件,,一個(gè)由Service Container 讀取的配置文件來產(chǎn)生實(shí)際配置的類,。但是程序也有可能變得不易讀了,程序的性能也有可能還會(huì)下降,。 參考:
High Cohesion & Low/Loose coupling & – 高內(nèi)聚,, 低耦合 這個(gè)原則是UNIX操作系統(tǒng)設(shè)計(jì)的經(jīng)典原則,把模塊間的耦合降到最低,,而努力讓一個(gè)模塊做到精益求精,。
內(nèi)聚意味著重用和獨(dú)立,,耦合意味著多米諾效應(yīng)牽一發(fā)動(dòng)全身。 參考:
Convention over Configuration(CoC)– 慣例優(yōu)于配置原則 簡(jiǎn)單點(diǎn)說,,就是將一些公認(rèn)的配置方式和信息作為內(nèi)部缺省的規(guī)則來使用,。例如,Hibernate的映射文件,,如果約定字段名和類屬性一致的話,,基本上就可以不要這個(gè)配置文件了。你的應(yīng)用只需要指定不convention的信息即可,,從而減少了大量convention而又不得不花時(shí)間和精力啰里啰嗦的東東,。配置文件很多時(shí)候相當(dāng)?shù)挠绊戦_發(fā)效率。 Rails 中很少有配置文件(但不是沒有,,數(shù)據(jù)庫(kù)連接就是一個(gè)配置文件),,Rails 的fans號(hào)稱期開發(fā)效率是 java 開發(fā)的 10 倍,估計(jì)就是這個(gè)原因,。Maven也使用了CoC原則,,當(dāng)你執(zhí)行mvn -compile命令的時(shí)候,不需要指源文件放在什么地方,,而編譯以后的class文件放置在什么地方也沒有指定,,這就是CoC原則,。 參考:http://en./wiki/Convention_over_Configuration Separation of Concerns (SoC) – 關(guān)注點(diǎn)分離 SoC 是計(jì)算機(jī)科學(xué)中最重要的努力目標(biāo)之一,。這個(gè)原則,就是在軟件開發(fā)中,,通過各種手段,,將問題的各個(gè)關(guān)注點(diǎn)分開。如果一個(gè)問題能分解為獨(dú)立且較小的問題,,就是相對(duì)較易解決的,。問題太過于復(fù)雜,要解決問題需要關(guān)注的點(diǎn)太多,,而程序員的能力是有限的,,不能同時(shí)關(guān)注于問題的各個(gè)方面。正如程序員的記憶力相對(duì)于計(jì)算機(jī)知識(shí)來說那么有限一樣,,程序員解決問題的能力相對(duì)于要解決的問題的復(fù)雜性也是一樣的非常有限,。在我們分析問題的時(shí)候,如果我們把所有的東西混在一起討論,,那么就只會(huì)有一個(gè)結(jié)果——亂,。 我記得在上一家公司有一個(gè)項(xiàng)目,討論就討論了1年多,,項(xiàng)目本來不復(fù)雜,,但是沒有使用SoC,,全部的東西混為一談,再加上一堆程序員注入了各種不同的觀點(diǎn)和想法,,整個(gè)項(xiàng)目一下子就失控了,。最后,本來一個(gè)1年的項(xiàng)目做了3年,。 實(shí)現(xiàn)關(guān)注點(diǎn)分離的方法主要有兩種,,一種是標(biāo)準(zhǔn)化,另一種是抽象與包裝,。標(biāo)準(zhǔn)化就是制定一套標(biāo)準(zhǔn),,讓使用者都遵守它,將人們的行為統(tǒng)一起來,,這樣使用標(biāo)準(zhǔn)的人就不用擔(dān)心別人會(huì)有很多種不同的實(shí)現(xiàn),,使自己的程序不能和別人的配合。Java EE就是一個(gè)標(biāo)準(zhǔn)的大集合,。每個(gè)開發(fā)者只需要關(guān)注于標(biāo)準(zhǔn)本身和他所在做的事情就行了,。就像是開發(fā)鏍絲釘?shù)娜酥粚W⒂陂_發(fā)鏍絲釘就行了,而不用關(guān)注鏍帽是怎么生產(chǎn)的,,反正鏍帽和鏍絲釘按標(biāo)來就一定能合得上,。不斷地把程序的某些部分抽像差包裝起來,也是實(shí)現(xiàn)關(guān)注點(diǎn)分離的好方法,。一旦一個(gè)函數(shù)被抽像出來并實(shí)現(xiàn)了,,那么使用函數(shù)的人就不用關(guān)心這個(gè)函數(shù)是如何實(shí)現(xiàn)的,同樣的,,一旦一個(gè)類被抽像并實(shí)現(xiàn)了,,類的使用者也不用再關(guān)注于這個(gè)類的內(nèi)部是如何實(shí)現(xiàn)的。諸如組件,,分層,,面向服務(wù),等等這些概念都是在不同的層次上做抽像和包裝,,以使得使用者不用關(guān)心它的內(nèi)部實(shí)現(xiàn)細(xì)節(jié),。 說白了還是“高內(nèi)聚,低耦合”,。 參考:http:///archives/99 Design by Contract (DbC) – 契約式設(shè)計(jì) DbC的核心思想是對(duì)軟件系統(tǒng)中的元素之間相互合作以及“責(zé)任”與“義務(wù)”的比喻,。這種比喻從商業(yè)活動(dòng)中“客戶”與“供應(yīng)商”達(dá)成“契約”而得來。例如:
同樣的,如果在程序設(shè)計(jì)中一個(gè)模塊提供了某種功能,,那么它要:
契約就是這些權(quán)利和義務(wù)的正式形式,。我們可以用“三個(gè)問題”來總結(jié)DbC,并且作為設(shè)計(jì)者要經(jīng)常問:
根據(jù)Bertrand Meyer氏提出的DBC概念的描述,,對(duì)于類的一個(gè)方法,,都有一個(gè)前提條件以及一個(gè)后續(xù)條件,前提條件說明方法接受什么樣的參數(shù)數(shù)據(jù)等,,只有前提條件得到滿足時(shí),,這個(gè)方法才能被調(diào)用;同時(shí)后續(xù)條件用來說明這個(gè)方法完成時(shí)的狀態(tài),,如果一個(gè)方法的執(zhí)行會(huì)導(dǎo)致這個(gè)方法的后續(xù)條件不成立,,那么這個(gè)方法也不應(yīng)該正常返回。 現(xiàn)在把前提條件以及后續(xù)條件應(yīng)用到繼承子類中,,子類方法應(yīng)該滿足:
換句話說,,通過基類的接口調(diào)用一個(gè)對(duì)象時(shí),,用戶只知道基類前提條件以及后續(xù)條件,。因此繼承類不得要求用戶提供比基類方法要求的更強(qiáng)的前提條件,亦即,,繼承類方法必須接受任何基類方法能接受的任何條件(參數(shù)),。同樣,繼承類必須順從基類的所有后續(xù)條件,,亦即,,繼承類方法的行為和輸出不得違反由基類建立起來的任何約束,不能讓用戶對(duì)繼承類方法的輸出感到困惑,。 這樣,,我們就有了基于契約的LSP,基于契約的LSP是LSP的一種強(qiáng)化,。 參考:http://en./wiki/Design_by_contract Acyclic Dependencies Principle (ADP) – 無環(huán)依賴原則 包之間的依賴結(jié)構(gòu)必須是一個(gè)直接的無環(huán)圖形,,也就是說,,在依賴結(jié)構(gòu)中不允許出現(xiàn)環(huán)(循環(huán)依賴)。如果包的依賴形成了環(huán)狀結(jié)構(gòu),,怎么樣打破這種循環(huán)依賴呢,?有2種方法可以打破這種循環(huán)依賴關(guān)系:第一種方法是創(chuàng)建新的包,如果A,、B,、C形成環(huán)路依賴,那么把這些共同類抽出來放在一個(gè)新的包D里,。這樣就把C依賴A變成了C依賴D以及A依賴D,,從而打破了循環(huán)依賴關(guān)系。第二種方法是使用DIP(依賴倒置原則)和ISP(接口分隔原則)設(shè)計(jì)原則,。 無環(huán)依賴原則(ADP)為我們解決包之間的關(guān)系耦合問題,。在設(shè)計(jì)模塊時(shí),不能有循環(huán)依賴,。 參考:http:///cgi/wiki?AcyclicDependenciesPrinciple 上面這些原則可能有些學(xué)院派,,也可能太為理論,我在這里說的也比較模糊和簡(jiǎn)單,,這里只是給大家一個(gè)概貌,,如果想要了解更多的東西,大家可以多google一下,。 不過這些原則看上去都不難,,但是要用好卻并不那么容易。要能把這些原則用得好用得精,,而不教條,,我的經(jīng)驗(yàn)如下:(我以為這是一個(gè)理論到應(yīng)用的過程)
3—邏輯層 vs 物理層 Layer 和Tier都是層,,但是他們所表現(xiàn)的含義不同,,Tier指的是軟件系統(tǒng)中物理上的軟件和硬件,具體指部署在某服務(wù)器上,,而Layer(邏輯層)指軟件系統(tǒng)中完成特定功能的邏輯模塊,,邏輯概念。 Layer是邏輯上 組織代碼的形式。比如邏輯分層中表現(xiàn)層,,服務(wù)層,,業(yè)務(wù)層,領(lǐng)域?qū)?,他們是軟件功能來劃分的,。并不指代部署在那臺(tái)具體的服務(wù)器上或者,物理位置,。 Tier這指代碼運(yùn)行部署的具體位置,,是一個(gè)物理層次上的劃為,Tier就是指邏輯層Layer具體的運(yùn)行位置,。所以邏輯層可以部署或者遷移在不同物理層,,一個(gè)物理層可以部署運(yùn)行多個(gè)邏輯層。 從Layer和Tier就會(huì)延伸到邏輯架構(gòu)和物理架構(gòu),。我們一個(gè)邏輯分層(N-Layer)的部署運(yùn)行環(huán)境可以在一臺(tái)或者是多臺(tái)服務(wù)器,,由于物理環(huán)境的多樣性,邏輯層次的部署也具有多樣性,。這就需要我們必須了解物理架構(gòu)和邏輯架構(gòu),。 大多數(shù)情況下我們所說的N層應(yīng)用系統(tǒng)指的是物理模型,具體模塊的分布物理位置,??蛻舳耍?wù)層,,邏輯層,,數(shù)據(jù)庫(kù)服務(wù)器,與我們的邏輯模型之間并不是一對(duì)一的關(guān)系。邏輯上的分層架構(gòu)與物理位置上的服務(wù)器數(shù)量和網(wǎng)絡(luò)邊界多少無關(guān),邏輯架構(gòu)層次只與我們的功能劃分相關(guān),,是按照功能劃分。經(jīng)典的3-Layer架構(gòu):表現(xiàn)層,,業(yè)務(wù)層,數(shù)據(jù)訪問層,,他們可能運(yùn)行在同一物理位置上,。也可以是3臺(tái)計(jì)算機(jī)上,,這并不是邏輯架構(gòu)所關(guān)注的,。邏輯層次和物理分層數(shù)量關(guān)系為:邏輯層數(shù)必須不小于物理層數(shù),因?yàn)橐粋€(gè)物理層可以部署一個(gè)或者多個(gè)邏輯層次,,邏輯層次只能遷移在不同的物理環(huán)境,。 邏輯層次的架構(gòu)能幫助我們解決邏輯耦合,達(dá)到靈活配置,遷移,。 一個(gè)良好的邏輯分層可以帶來:
一個(gè)良好的物理架構(gòu)可以帶來:
邏輯層次越多會(huì)影響程序運(yùn)行的性能,,但代碼層次的低耦合,松散化,,是需要架構(gòu)師的權(quán)衡的,,我覺得一般應(yīng)用程序的瓶頸并不在這里。 4—服務(wù)層的簡(jiǎn)單理解 在ddd設(shè)計(jì)中我們經(jīng)常會(huì)提到服務(wù)層,,服務(wù)層是什么,?職責(zé)是什么?有什么好處,?,。 先看簡(jiǎn)單的層次圖(注:這里并沒有考慮其他多余的領(lǐng)域邏輯數(shù)據(jù)層存儲(chǔ),或者UOW這些細(xì)節(jié)) 我的理解是服務(wù)層是處于我的應(yīng)用程序業(yè)務(wù)層和表現(xiàn)層之間的應(yīng)用程序邊界,邊界可能是很薄的一層類設(shè)計(jì)或者是分布式服務(wù)網(wǎng)絡(luò)躍點(diǎn),。它是一個(gè)與技術(shù)無關(guān)的名詞,。由表現(xiàn)層直接調(diào)用,契約,,執(zhí)行命令(修改狀態(tài)(CUD))或者是查詢返回dto(數(shù)據(jù)遷移對(duì)象)(cms,,命令-查詢分離)。他對(duì)業(yè)務(wù)邏輯層接口很清楚,,組織業(yè)務(wù)邏輯 微服務(wù)形成宏服務(wù),,適配表現(xiàn)層。 這里談到宏服務(wù)和微服務(wù),,宏服務(wù)有一些列粗粒度的服務(wù)組成,。用戶的一次操作usecase,比如電子商務(wù)下單,,CreateOrder就是一個(gè)宏服務(wù),,而不是下單中的細(xì)粒度的商品庫(kù)存檢查,訂單合法性等,。而與之對(duì)應(yīng)的微服務(wù)(有時(shí)也叫應(yīng)用程序服務(wù)),,則表現(xiàn)為問題領(lǐng)域邏輯細(xì)節(jié),就如上面的庫(kù)存檢查和合法性檢查這些細(xì)粒度的服務(wù),。宏服務(wù)是由一個(gè)或者多個(gè)微服務(wù)組成,,有時(shí)我們的usecase邏輯很簡(jiǎn)單服務(wù)層僅由單一微服務(wù)組成,變現(xiàn)為很簡(jiǎn)單的幾句微服務(wù)調(diào)用,。 服務(wù)層的職責(zé): 1:在面軟件開發(fā)不管是結(jié)構(gòu)化編程(sp)還是面向?qū)ο缶幊蹋╫op)我們一直都強(qiáng)調(diào)高內(nèi)聚低耦合,,分離關(guān)注點(diǎn)(soc)。服務(wù)層處于應(yīng)用程序和業(yè)務(wù)層之間,,應(yīng)用邊界,,使得兩次直接解耦,,利用第三個(gè)對(duì)象破壞兩對(duì)象直接的依賴,并轉(zhuǎn)化適配領(lǐng)域?qū)ο螅╠o)和試圖對(duì)象(vo)的差異,。 2:服務(wù)層隱藏了業(yè)務(wù)邏輯層的細(xì)節(jié),,其內(nèi)部需要組織業(yè)務(wù)微服務(wù),提供更宏觀,,面向表現(xiàn)層的服務(wù)邏輯,,利用契約接口暴露,包裝,。系統(tǒng)所有的交互都是從表現(xiàn)層進(jìn)入,。 目前流行SOA架構(gòu),提供了一種分布式服務(wù)架構(gòu),,以服務(wù)為關(guān)注點(diǎn),,提高服務(wù)和業(yè)務(wù)邏輯的重用,但是這里說的服務(wù)并不是特定的技術(shù)wcf或者webservice,,服務(wù)同時(shí)候可能是一次規(guī)定契約的一些列粗粒度組織的類組成,。但是利用SOA或者M(jìn)TS建立服務(wù)會(huì)讓我們的服務(wù)得到跟多的附加優(yōu)勢(shì),例如安全,,事物,,日志,擴(kuò)展性的提升,。 服務(wù)層帶來的優(yōu)勢(shì):如上所述服務(wù)層為表現(xiàn)層提供的同一的接口契約和入口,。讓我們的業(yè)務(wù)層可以關(guān)注與實(shí)現(xiàn)問題領(lǐng)域邏輯,問題領(lǐng)域?qū)嶋H需求,。組織微服務(wù)避免太多的細(xì)粒度服務(wù)的調(diào)用充斥在我們的項(xiàng)目表現(xiàn)層和問題領(lǐng)域中,,過多的交互。如果采用soa等服務(wù)領(lǐng)域可以讓我們的應(yīng)用程序輕易的跨過應(yīng)用程序邊界和網(wǎng)絡(luò)躍點(diǎn),。但是需要付出一點(diǎn)的性能代價(jià),。 數(shù)據(jù)遷移對(duì)象(dto)就是攜帶數(shù)據(jù)穿過應(yīng)用程序邊界的對(duì)象,減少數(shù)據(jù)的交互次數(shù),,常常我們將其作為值對(duì)象,,只是一組簡(jiǎn)單的get,set屬性組成,,不存在行為操作,,僅僅為數(shù)據(jù)的載體。在領(lǐng)域設(shè)計(jì)中dto是一個(gè)很重要的模式,,不是我們所有的領(lǐng)域?qū)ο蠖寄茌p松的到達(dá)表現(xiàn)層,,僅僅表現(xiàn)層和領(lǐng)域?qū)硬渴鹪谕晃锢砦恢谩H绻枰┻^網(wǎng)絡(luò)躍點(diǎn)或者進(jìn)程邊界,,因?yàn)轭I(lǐng)域?qū)ο笫刮覀兊臉I(yè)務(wù)的核心存在很多的自然世界的關(guān)系,,依賴,,甚至可能存在循環(huán)依賴比如電商用戶和訂單,,用戶用戶一組訂單的集合,,而每個(gè)訂單都指向一個(gè)特定的用戶,我們就必須破換掉這種循環(huán)依賴,,才可能使其可序列化,,穿過躍點(diǎn)。其次我們的領(lǐng)域?qū)ο笸际且欢杨I(lǐng)域富對(duì)象,,存在大量數(shù)據(jù),,很多時(shí)候我們的場(chǎng)景并不需要全部的數(shù)據(jù)信息。有了dto的存在就能很好的解決這些問題,,是的我們的項(xiàng)目變得simple(keep it simple,,Stupid。 KISS原則),。 但是與此同時(shí)dto存在會(huì)為我們帶來一些額外的復(fù)雜度,,我們必須有一層do到dto的映射適配層。 理論上完美的設(shè)計(jì)我們需要為每一個(gè)應(yīng)用定義一個(gè)dto,,但是在一個(gè)復(fù)雜的系統(tǒng)中我們可能存在很多的領(lǐng)域?qū)ο?,加?00個(gè)do,每個(gè)do一般都會(huì)存在多個(gè)dto,,這將一個(gè)增加一個(gè)龐大的集合和mapping邏輯,,對(duì)于維護(hù)也存在不小的挑戰(zhàn)。在軟件領(lǐng)域存在一句話就是bug的數(shù)量隨著代碼量增加,,代碼量增加需要測(cè)試點(diǎn)也隨著增加,。除非我們必須跨越應(yīng)用程序網(wǎng)絡(luò)躍點(diǎn)邊界,我覺得否則我們也可以存在一些簡(jiǎn)單do的直接使用,。根據(jù)世界項(xiàng)目,,情形由我們的架構(gòu)師決定。 5—SOA面向服務(wù)架構(gòu)簡(jiǎn)述 在上篇中我們簡(jiǎn)單談了下架構(gòu)設(shè)計(jì)中服務(wù)層的簡(jiǎn)單理解,,在這里我們將繼續(xù)服務(wù)層的架構(gòu),,在本節(jié)我們將重點(diǎn)在于分布式服務(wù)。在分布式系統(tǒng)中表現(xiàn)層和業(yè)務(wù)邏輯層 并不處于同一物理部署,,所以我們必須存在分布式服務(wù),,以契約方式發(fā)布于網(wǎng)絡(luò)中,我們的關(guān)注點(diǎn)在于服務(wù),,面向服務(wù)編程,,這種通過組合業(yè)務(wù)邏輯暴露可用服務(wù)的架構(gòu)叫做面向服務(wù)架構(gòu)(SOA)。 SOA強(qiáng)調(diào)一個(gè)松耦合,,基于宏服務(wù)的架構(gòu),,通過契約暴露給服務(wù)消費(fèi)者可用的服務(wù)交互,。SOA是以服務(wù)為組成構(gòu)建,原則有: 1.邊界清晰: 服務(wù)層是消費(fèi)者交互到系統(tǒng)業(yè)務(wù)的唯一入口,所有我們的服務(wù)必須能夠被消費(fèi)者所理解,以及最好處理Request/Response基于消息交換RPC調(diào)用,職責(zé)明確單一.還有我們更希望我們的服務(wù)為作用明確的,CQS(命令-查詢分離原則). 2.服務(wù)的自治性 服務(wù)自治主要表現(xiàn)在每個(gè)服務(wù)都是獨(dú)立的,,其系統(tǒng)部署,,管理監(jiān)控都是獨(dú)立的。自治體現(xiàn)了服務(wù)的松耦合,,但并不是服務(wù)就是一個(gè)孤島,,其可以通過消息交換消費(fèi)其他服務(wù)。 3.使用契約(接口和數(shù)據(jù)載體),,而非實(shí)現(xiàn) 這也是面向?qū)ο笤O(shè)計(jì)第一原則,。在我們的服務(wù)設(shè)計(jì)中SOA一個(gè)重要目標(biāo)就是互操作,基于SOAP等標(biāo)準(zhǔn)協(xié)議實(shí)現(xiàn)跨平臺(tái)互操作,,可能存在異構(gòu)系統(tǒng),。所以我們?cè)撨x擇接口而不是語言具體的類以及基于消息交互。服務(wù)對(duì)于開發(fā)就是一些列行為的組合,,數(shù)據(jù)契約就是數(shù)據(jù)遷移對(duì)象,,數(shù)據(jù)載體。契約使得我們并不關(guān)心服務(wù)的內(nèi)部實(shí)現(xiàn),,而只關(guān)心提供了那些服務(wù),,服務(wù)的簽名如何,怎么調(diào)用之類的,。 4.兼容性基于策越 對(duì)于消費(fèi)者來說服務(wù)是否能滿足他的需求,,這需要服務(wù)語義兼容,語義兼容也應(yīng)該通過可訪問方式暴露,。是的服務(wù)可發(fā)現(xiàn),。 SOA是一種設(shè)計(jì)原則規(guī)范,其目標(biāo)在于為復(fù)雜系統(tǒng)提供互操作性和以服務(wù)為基礎(chǔ)組件構(gòu)造系統(tǒng)邏輯,。把具體的業(yè)務(wù)邏輯和流程屏蔽,,暴露出用戶可用的行為集合。SOA是一中原則而非集體技術(shù),。wcf,,webservice是具體SOA技術(shù)。同時(shí)SOA也不是我們的目標(biāo),,客戶是不與關(guān)心我們采用soa與否,,這只是我們對(duì)系統(tǒng)的一種解決方案。 SOA優(yōu)勢(shì)在于給我們提供更好的代碼重用,,版本控制,,安全控制,擴(kuò)展延伸性,。同時(shí)降低和服務(wù)的耦合,,交互必須依賴于服務(wù)契約和數(shù)據(jù)契約,,并不關(guān)心服務(wù)的內(nèi)部實(shí)現(xiàn)。在我們的版本升級(jí),,修改過程中可以完全可以重新實(shí)現(xiàn)替換原有服務(wù),,并不會(huì)影響消費(fèi)程序的使用。 最后我們必須的說下當(dāng)下流行的restfull,,通常我們認(rèn)為這是一種風(fēng)格,,而非架構(gòu),,是由Roy Thomas Fielding在其博士論文 《Architectural Styles and the Design of Network-based Software Architectures》中提出REST是英文Representational State Transfer的縮寫,,中文翻譯為“表述性狀態(tài)轉(zhuǎn)移”。是一種基于web的架構(gòu),,它很好的利用http協(xié)議的method,。根據(jù)不同的method表示對(duì)資源的不同語義操作。其核心在于將發(fā)布在網(wǎng)絡(luò)的一切事物歸屬為資源,,每個(gè)資源定位于一個(gè)資源定位符(URI),。以及無狀態(tài),緩存,,分層架構(gòu),。在微軟最新的WCF resetfull,web api應(yīng)用框架,。以及wcf ria ,,wcf data service,需要的注意的是微軟同時(shí)候加入的自己的oData協(xié)議(開元數(shù)據(jù)協(xié)議),。 最后說一點(diǎn):我覺得不管是服務(wù)或者resetfull服務(wù)我們都必須定義契約,,依賴于契約,雖然微軟的而技術(shù)允許我們直接寄宿服務(wù)類,,但是對(duì)于服務(wù)的擴(kuò)展和延伸而言,,說這句話的原因在于我最近看見一些直接寄宿服務(wù)類的resetfull架構(gòu)。 6—業(yè)務(wù)邏輯層簡(jiǎn)述 業(yè)務(wù)邏輯層是專門處理軟件業(yè)務(wù)需求的一層,,處于數(shù)據(jù)庫(kù)之上,,服務(wù)層之下,完成一些列對(duì)Domain Object的CRUD,,作為一組微服務(wù)提供給服務(wù)層來組織在暴露給表現(xiàn)層,,如庫(kù)存檢查,用法合法性檢查,,訂單創(chuàng)建,。 業(yè)務(wù)邏輯層包含領(lǐng)域?qū)ο竽P停I(lǐng)域?qū)嶓w,,業(yè)務(wù)規(guī)則,,驗(yàn)證規(guī)則,,業(yè)務(wù)流程。1:領(lǐng)域?qū)ο竽P蜑橄到y(tǒng)結(jié)構(gòu)描述,,包含實(shí)體功能描述,,實(shí)體之間的關(guān)系。領(lǐng)域模型處于天生的復(fù)雜性:2:領(lǐng)域?qū)嶓w:業(yè)務(wù)層是一些操作業(yè)務(wù)對(duì)象(BO)的處理,。業(yè)務(wù)對(duì)象包含數(shù)據(jù)和行為,,是一個(gè)完整的業(yè)務(wù)對(duì)象。其不同于上節(jié)架構(gòu)設(shè)計(jì)中服務(wù)層的簡(jiǎn)單理解提到的數(shù)據(jù)遷移對(duì)象(dto),,對(duì)于dto存在數(shù)據(jù)的,,不存在行為,dto是bo(ddd中又稱do)的子集,,負(fù)責(zé)與特定界面需求的扁平化實(shí)體,,dto僅僅是一個(gè)數(shù)據(jù)載體,需要跨越應(yīng)用程序邊界,,而業(yè)務(wù)對(duì)象則不會(huì)存在復(fù)制遷移,,往往一個(gè)業(yè)務(wù)對(duì)象存在一個(gè)或者多個(gè)數(shù)據(jù)遷移對(duì)象。3:業(yè)務(wù)最大的邏輯就在處理一些列現(xiàn)實(shí)世界的規(guī)則,,這也是軟件中最容易變化的部分,,這里通常會(huì)出現(xiàn)我們眾多的if-else或者switch-case的地方。也這因?yàn)槿绻f以個(gè)人覺得在我們的項(xiàng)目最應(yīng)該關(guān)系和分離需求的層次,。4:驗(yàn)證規(guī)則:業(yè)務(wù)規(guī)則很大程度上也是對(duì)對(duì)象的數(shù)據(jù)驗(yàn)證,,驗(yàn)證業(yè)務(wù)對(duì)象的當(dāng)前數(shù)據(jù)狀態(tài)。我覺得在每個(gè)業(yè)務(wù)對(duì)象上都應(yīng)該存在一個(gè)對(duì)外部對(duì)象暴露的驗(yàn)證接口,,可以考慮微軟企業(yè)庫(kù)的VAB 基于Attribute聲明式驗(yàn)證或者上節(jié)FluentValidation驗(yàn)證組件基于IOC的解耦,。 業(yè)務(wù)層模式:在常見的業(yè)務(wù)層模式中主要分為過程是模式和面向?qū)ο竽J健_^程模式有是事務(wù)性腳本和表模式,,而面向?qū)ο竽J綖榛顒?dòng)記錄模式和領(lǐng)域驅(qū)動(dòng)模式,。理論上說事務(wù)性腳本模式是最簡(jiǎn)單的開發(fā)模式,其前期投入下,,但隨著項(xiàng)目周期和復(fù)雜度上升明顯,,而領(lǐng)域模型(DDD)前期投入較大,但是理論上說是隨著項(xiàng)目周期和復(fù)雜度呈線性增加,,當(dāng)然這些都是理論值,。 1:事務(wù)腳本模式是業(yè)務(wù)邏輯層最簡(jiǎn)單的模式,面向過程模式,。該模式以用于的操作為起點(diǎn),,設(shè)計(jì)業(yè)務(wù)組件,即業(yè)務(wù)邏輯直接映射到用戶界面的操作。這通常是從表現(xiàn)層邏輯出發(fā),,表現(xiàn)層我需要什么業(yè)務(wù)層提供什么,,直到數(shù)據(jù)層。針對(duì)沒一個(gè)用戶的新功能都需要新增一個(gè)從UI到關(guān)系數(shù)據(jù)庫(kù)的分支流程,。其使用與邏輯不是很復(fù)雜或者變化不大穩(wěn)定的應(yīng)用系統(tǒng)開發(fā),。其不需要付出與業(yè)務(wù)無關(guān)的額外代價(jià),并且在現(xiàn)代VS之類的IDE幫助下能夠很快的進(jìn)行快速應(yīng)用開發(fā)(RAD),。也由于這種優(yōu)勢(shì),,也是其最大的劣勢(shì),程序中充滿了IF-else,,switch-case之類的邏輯或者大量的static的方法,,每個(gè)功能都是一個(gè)程序分支,這對(duì)代碼無法重用,。編碼不易于維護(hù),,對(duì)復(fù)雜項(xiàng)目和變化需求不適應(yīng),。 2:表模式:為每個(gè)數(shù)據(jù)庫(kù)表定義一個(gè)表模塊類,,包含操作該數(shù)據(jù)的所有行為方法。作為一個(gè)容器,,將數(shù)據(jù)和行為組織在一起,。其對(duì)數(shù)據(jù)的粒度針對(duì)于數(shù)據(jù)表,而非數(shù)據(jù)行,,因此需要以集合或者表傳遞數(shù)據(jù)信息,。表模式基于對(duì)象但是完全又?jǐn)?shù)據(jù)庫(kù)驅(qū)動(dòng)開發(fā),在業(yè)務(wù)模型和數(shù)據(jù)庫(kù)關(guān)系模型顯著差異的情況下,,應(yīng)對(duì)需求,,并不是那么適合。但是在.net中提供的一些列如強(qiáng)類型DataSet等IDE的輔助下自動(dòng)生成大量的代碼,,也是一個(gè)不錯(cuò)的選擇,,因?yàn)椴糠謹(jǐn)?shù)據(jù)庫(kù)的操作趨于自動(dòng)化。表模式?jīng)]太過于關(guān)注業(yè)務(wù),,而是關(guān)注數(shù)據(jù)庫(kù)表結(jié)構(gòu),。而業(yè)務(wù)邏輯和領(lǐng)域問題才是軟件核心。 3:活動(dòng)記錄模式:一個(gè)以數(shù)據(jù)庫(kù)表一行Row為對(duì)象,,并且對(duì)象中包含行為和數(shù)據(jù)的模式方法,。其數(shù)據(jù)對(duì)象很大程度的接近數(shù)據(jù)庫(kù)表結(jié)構(gòu)。在活動(dòng)記錄模式對(duì)象中通常也包含操作對(duì)象的CRUD行為,,數(shù)據(jù)驗(yàn)證等業(yè)務(wù)規(guī)則,。對(duì)于業(yè)務(wù)不是很復(fù)雜,對(duì)象關(guān)系與關(guān)系模型映射不具有很大差異情況,活動(dòng)記錄模式會(huì)運(yùn)用的很好,?;顒?dòng)模式比較簡(jiǎn)單化設(shè)計(jì),在上現(xiàn)行的很多如Linq to sql,,ActiveRecord框架的輔助下,,將針對(duì)問題領(lǐng)域不是太過復(fù)雜的項(xiàng)目十分有用。但是其模式和數(shù)據(jù)庫(kù)表結(jié)構(gòu)的相互依賴,,導(dǎo)致若你修改數(shù)據(jù)庫(kù)結(jié)構(gòu),,你不得不同時(shí)修改對(duì)象以及相關(guān)邏輯。如果不能保證數(shù)據(jù)庫(kù)關(guān)系模型和對(duì)象模式的很大程度的相似這就進(jìn)入的困境,。 4:領(lǐng)域模型:在前面的幾種模式都是項(xiàng)目開始站在了以數(shù)據(jù)為中心的角度,,而不是業(yè)務(wù)本身的問題領(lǐng)域。而領(lǐng)域模型關(guān)注系統(tǒng)問題領(lǐng)域,,首先開始為領(lǐng)域?qū)ο笤O(shè)計(jì),。與活動(dòng)記錄模式來說,領(lǐng)域模型完全站在了問題領(lǐng)域業(yè)務(wù)概念模型一邊,,與數(shù)據(jù)庫(kù),,持久化完成獨(dú)立,其推崇持久化透明(POCO),。其可以充分利用面向?qū)ο笤O(shè)計(jì),,不受持久化機(jī)制的任何約束。其實(shí)完全又業(yè)務(wù)驅(qū)動(dòng)出來的,。但是其最大的優(yōu)勢(shì)如上各個(gè)模式一樣也是其最大的劣勢(shì)對(duì)象模型和關(guān)系模型具有天然的阻抗,,我們的領(lǐng)域?qū)嶓w早晚需要映射到持久化機(jī)制。還好的是當(dāng)前有NHibearnate,EF,Fluent NHibearnate這類ORM框架輔助,。在DDD中包含UOW,,倉(cāng)儲(chǔ),值類型和聚合根,,領(lǐng)域事件,,領(lǐng)域跟蹤一類的概念,這將在以后具體說明,。 模式的選擇在與架構(gòu)師的決定,,這也是架構(gòu)師具有挑戰(zhàn)意義的職責(zé),需要根據(jù)具體的項(xiàng)目需求,,團(tuán)隊(duì),,個(gè)人等外界因素最終決定,不存在萬能的模式,,也不存在完美的設(shè)計(jì),。 7—設(shè)計(jì)箴言理解 今天和師弟聊天聊到他們項(xiàng)目開發(fā),有些同事總是提前考慮性能優(yōu)化,需求變更又是一大堆的重寫,,讓我想起了Donald Knuth 提到的:對(duì)軟件的過早地優(yōu)化是萬惡的根源,。這里就簡(jiǎn)單的說幾條重要的軟件名人哲學(xué)。 1:軟件中唯一不變的就是變化,。 在軟件開發(fā)過程中需求是不停的變化,,隨著客戶對(duì)系統(tǒng)的認(rèn)識(shí),和現(xiàn)有開發(fā)功能和軟件的認(rèn)識(shí),,也許以開始他提出的需求就是背離的,。記得網(wǎng)上有一句笑話,師說需求變化的: 程序員XX遭遇車禍成植物人,,醫(yī)生說活下來的希望只有萬分之一,,喚醒更為渺茫??伤腖ead和親人沒有放棄,,他們根據(jù)XX工作如命的作風(fēng),每天都在他身邊念:“XX,,需求又改了,,該干活了,你快來呀,!”,,奇跡終于發(fā)生了,XX醒來了,,第一句話:“需求又改了 在設(shè)計(jì)和架構(gòu)中,凡事無絕對(duì),,作為架構(gòu)師或者項(xiàng)目負(fù)責(zé)人你必須永遠(yuǎn)的清晰認(rèn)識(shí)到?jīng)]有完美的架構(gòu)和設(shè)計(jì),,沒有萬能的軟件。只存在當(dāng)前環(huán)境,,需求方案,,團(tuán)隊(duì)人員素質(zhì),物理環(huán)境,,安全等綜合因素下的合適方案,,由于總總原因你的解決方案可能不是某一個(gè)單一因素下的最優(yōu)解。站在這個(gè)位置你需要做的是找到這個(gè)綜合下的最優(yōu)解,,權(quán)衡,。不要只從表面說某個(gè)人某個(gè)團(tuán)隊(duì)的解決方案怎么查怎么不好,或者這就是當(dāng)時(shí)綜合因素的最優(yōu)解,,站在同樣的位置環(huán)境你不一定做得更好,。在架構(gòu)設(shè)計(jì)和人生,在我看來很相似,總是有一堆抉擇,,每一次的抉擇都會(huì)帶來得和失,,權(quán)衡得失取舍。 2:KISS:(Keep It Simple,Stupid): 保持簡(jiǎn)單,,但不過于太簡(jiǎn)單,。在《UNIX下的編程哲學(xué)》中提到很多保持設(shè)計(jì)簡(jiǎn)單,我們能清晰看到這條原則?,F(xiàn)在視覺設(shè)計(jì),,都崇尚簡(jiǎn)約設(shè)計(jì),簡(jiǎn)單而不庸俗,,而不是一大堆的豪華奢侈打造,。VB編程開始的可視化設(shè)計(jì),可見即可得,,google的首頁,,商業(yè)風(fēng)格。在我們的軟件設(shè)計(jì)中也需要簡(jiǎn)潔的設(shè)計(jì),,用戶需要的是可見可量化的功能的正確性,,而是你運(yùn)用了多牛b的技術(shù)模式,但絕不是一味的太過于簡(jiǎn)單,。你想把意見簡(jiǎn)單的事情做復(fù)雜化是很容易的事情,,但是把一件復(fù)雜的事情簡(jiǎn)單化卻不那么容易。簡(jiǎn)單的人生就是幸福,。但是這里需要說明的是簡(jiǎn)單是優(yōu)秀的,,但簡(jiǎn)單是有底線邊界的,超過底線的簡(jiǎn)單也有變得稚幼,。比如事務(wù)性腳本模式比其他3中常見模式都簡(jiǎn)單,,但往往復(fù)雜的需求它不是最優(yōu)解,因?yàn)樗^于簡(jiǎn)單了(如果你還不了解是事務(wù)性腳本可以參見這里架構(gòu)設(shè)計(jì)-業(yè)務(wù)邏輯層簡(jiǎn)述),。 3:面向抽象編程,。 在設(shè)計(jì)模式,架構(gòu)模式,,OO中都是一條完全的主線,,作為oo第一原則存在。我不起那個(gè)軟件牛人曾說過:請(qǐng)牢記沒有接口的話就不要開始實(shí)現(xiàn),。這句話也許過于偏激,,但是如果你接口理解為不變或者不易變的話,理解或契約(公司和你的合同)更貼切些吧(可能是一個(gè)不變的類,,如果你能肯定的說出你的這個(gè)實(shí)現(xiàn)在以后,,在項(xiàng)目開發(fā)維護(hù)中是不會(huì)變得,,我覺得這也是接口,接口在于不變和不易變),,你也許會(huì)同意這句話,。對(duì)于目前的需求你肯定能夠沒有抽象沒夠接口完全寫出完美的代碼,但是第一條中我們說明的軟件中唯一不變的就是變化,,在未來的需求中你能夠很好的一樣的優(yōu)秀嗎,?如果不能,那么我認(rèn)為面對(duì)當(dāng)前需求就該為以后提供擴(kuò)展延伸,。 我個(gè)人理解23中設(shè)計(jì)模式中大多數(shù)基本都是圍繞著這個(gè)Program to an interface, not an implementation(依賴接口而不是實(shí)現(xiàn))第一原則為目的,。當(dāng)然我們也不能不說還有第二原則:組合優(yōu)先于繼承。以后的什么DIP(依賴倒置,,IOC的原則),,LSP(里氏替換),OCP(開閉原則)等等都是他們的延伸和擴(kuò)展,。在追溯的話這一些列都是為了軟件系統(tǒng)“高內(nèi)聚,,低耦合”(可以簡(jiǎn)敘述為:功能完備(高內(nèi)聚)的對(duì)象之間是靠接口(低耦合)通訊交互的),內(nèi)聚是描述的功能性完備程度,,耦合是表述模塊間的依賴程度,。這里插一句話某同事給我說依賴接口不是還有依賴嘛,我希望的是沒有耦合,,我的回答是:計(jì)算機(jī)二八原則說明了這一切,,既然事務(wù)出現(xiàn)在一起了,那絕不是偶然情況,,所以他們之間必定存在依賴,,在軟件設(shè)計(jì)中我們所能做的就是引入中間對(duì)象使其變?yōu)殚g接依賴,而減少他們之間的依賴,,而我們希望這個(gè)中間對(duì)象是個(gè)相對(duì)穩(wěn)定的,,設(shè)計(jì)中一切都是一個(gè)詞:間接,分層,,mvc,mvp,,soa,,中間件等等都是體現(xiàn)直接依賴變?yōu)殚g接依賴。說這個(gè)話題的原因是引出我們“高內(nèi)聚,,低耦合”行之有效的方法SOC(分離關(guān)注點(diǎn)),,這不只是OO的任然對(duì)面向過程編程行之有效,他是在20年前 SP(結(jié)構(gòu)化編程)中提出來的,。 如果你想對(duì)設(shè)計(jì)原則有更多的了解,,可以參見這里《java與模式》讀書心得,。 4:首先考慮可維護(hù),延伸性,,事后優(yōu)化 最后說一句,,經(jīng)常有人說反射性能低下,,對(duì)我們必須承認(rèn)反射比其他方案性能是不好,但是我們有解決方案:緩存,。在則說性能低下,,是以什么什么標(biāo)準(zhǔn)?用戶的接受程度,?反射我們可以有其替代方案Emit,,Expression tree。從反射,,Expression tree,,Emit的選擇,,其使用難度在提升,開發(fā)效率在增加,,性能在改善,。本人一般卻傾向于Expression tree,兩種劇中吧,。 5:繼承是為了多態(tài)而不是重用 OOP中可以編寫一個(gè)類,,然后我可以不斷的繼承重用去擴(kuò)展新需求。這是類的重用,,是全部的重用,?重用這個(gè)詞看上去也許更加的微妙。多態(tài)是面向?qū)ο蟮暮诵奶卣髦?,也不記不清那里聽到的:重用只是繼承的附帶功能,。在我們的繼承體系中不宜龐大如果一個(gè)擁有4,5層的繼承體系,對(duì)你的理解也增加難度,,而且集成體系必須是個(gè)干凈的繼承體系,,滿足LSP(里氏替換原則):在所有用到父類的地方都可以替換為子類,還能正常準(zhǔn)確工作,。這就要求你繼承更多的是修改擴(kuò)展父類的行為,,盡量避免狀態(tài)。繼承只是不要為了重用的為目的,,在恰當(dāng)?shù)臅r(shí)機(jī)更好的辦法是實(shí)現(xiàn)一個(gè)完全的類來替換不能滿足現(xiàn)有需求的類,。這也是oo原則第二原則吧,組合優(yōu)先于繼承,。組合比如設(shè)計(jì)模式中的策略模式,,你得到的是一個(gè)算法組合功能個(gè)數(shù)是一個(gè)笛卡爾積。但也是絕對(duì)的組合,,只是優(yōu)先,,不是取代,軟件和現(xiàn)實(shí)世界都是充滿了矛盾的,,就如開篇第一條“軟件中唯一不變的就是變化”就是最大的矛盾,,來自辯證唯物主義,你要做的是權(quán)衡,。組合表述的是整體的替換,,如策略模式模式的算法整體替換。繼承是部分的少量的擴(kuò)展修改行為,,比如設(shè)計(jì)模式中的模版方案,在父類的流程控制下,,部分步驟的修改,,數(shù)據(jù),,事務(wù)的流轉(zhuǎn)控制權(quán)在父類。這條在最后說一句:設(shè)計(jì)模式不是萬能的,,只是前人的優(yōu)秀經(jīng)驗(yàn),,是依賴于場(chǎng)景存在的,了解設(shè)計(jì)模式我覺得更重要的是其使用場(chǎng)景,,在遇見同類場(chǎng)景的時(shí)候知道可以有這種模式作為解決方案或許更好,,僅作為供你選擇的解決問題方案。 6:用戶的一切輸入都是萬惡的 用戶的輸入是屬于我們系統(tǒng)之外的,,是無法控制的,,是不可羅列的。對(duì)于用戶來說軟件只是一個(gè)黑盒子,,不需要,,也沒必要了解具體內(nèi)在實(shí)現(xiàn)。對(duì)于汽車銷售人員不需要了解發(fā)動(dòng)機(jī)螺栓是怎么上的一樣,,他了解宣傳的是能有什么優(yōu)勢(shì),,能給用戶帶來那些方面的滿足,價(jià)格,?性能,?速度?豪華,?….對(duì)于門戶網(wǎng)站來說你對(duì)應(yīng)的用戶不僅是可信任的用戶,,可能還有競(jìng)爭(zhēng)對(duì)手黑客攻擊行為。如果你的系統(tǒng)信任于用戶的輸入,,早晚一天總會(huì)“紙包不住火的”,,用戶有意無意的一次輸入就可能導(dǎo)致你系統(tǒng)的功能性的全盤崩潰,你不應(yīng)該限制用戶的操作,,你是不能命令用戶該輸入什么不能輸入什么,,比如某天某人使用用戶可能降工資了或者挨批了,心情不好,,你也許會(huì)潛意思的對(duì)你的系統(tǒng)進(jìn)行挑戰(zhàn),。 說到這里隨便說一句,以前項(xiàng)目組有人層提過由于自動(dòng)化測(cè)試服務(wù)器運(yùn)行時(shí)間太長(zhǎng)了,,把部分驗(yàn)證等邏輯移到單元測(cè)試中保證,。對(duì)于我的理解來說自動(dòng)化測(cè)試近似于集成測(cè)試吧,功能性測(cè)試,,應(yīng)該是黑盒子,。在單元測(cè)試中我們總是假設(shè)輸入是正確的,某個(gè)依賴也是正確的,,驗(yàn)證輸出的正確,。而集成測(cè)試重點(diǎn)在于這一些都是層次的組合,,貫通,不存在假設(shè)的正確性,,只有來自測(cè)試人員的測(cè)試用例得到預(yù)期的輸出,。 今天就寫到這里吧,還有很多但是一下想不起來,,后續(xù)有機(jī)會(huì)的話對(duì)于重要的也會(huì)繼續(xù)補(bǔ)上,。 現(xiàn)實(shí)是矛盾的,沒有完美的設(shè)計(jì),,也沒有絕對(duì)的簡(jiǎn)單,。生活也是如此就如:簡(jiǎn)單就是幸福,快樂就是幸福,。那么簡(jiǎn)單的標(biāo)準(zhǔn)是什么,?怎樣才是快樂?這在于你自己的抉擇,,權(quán)衡,。想起了某次面試和小公司面試官談話,面試官說ORM存在性能問題,,而且一直在糾結(jié)的說反對(duì)DDD,,反對(duì)模式。本人先說了如果存在了性能問題有什么解決方案,,首先怎么做如果不能滿足再怎么做,,從索引緩存到分表服務(wù)集群,再總結(jié)性的一句話:架構(gòu)如人生,,總是要面臨得到取舍,。 8—數(shù)據(jù)訪問層簡(jiǎn)述 在前面簡(jiǎn)單描述了下服務(wù)層,SOA面向服務(wù)架構(gòu),,架構(gòu)設(shè)計(jì)-業(yè)務(wù)邏輯層,,以及一些面面向設(shè)計(jì)原則理解和軟件架構(gòu)設(shè)計(jì)箴言。這篇博客我們將繼續(xù)進(jìn)入我們的下一層:數(shù)據(jù)訪問層,。無論你用的是什么開發(fā)模式或者是業(yè)務(wù)模式,,到最后最必須具有持久化機(jī)制,持久化到持久化介質(zhì),,并能對(duì)數(shù)據(jù)進(jìn)行讀取和寫入CRUD,。這就是數(shù)據(jù)訪問層。你可能是利用xml等文件格式磁盤存儲(chǔ),,常用的關(guān)系數(shù)據(jù)庫(kù)存儲(chǔ),,或者NoSql(not only sql)的內(nèi)存存儲(chǔ)或文檔存儲(chǔ)等等存儲(chǔ)介質(zhì)。而這里我只關(guān)心關(guān)系數(shù)據(jù)庫(kù)存儲(chǔ)。 數(shù)據(jù)層需要提供的職責(zé)有: 1:CRUD服務(wù),。作為唯一可以與存儲(chǔ)介質(zhì)交互的中間層出現(xiàn),,負(fù)責(zé)業(yè)務(wù)對(duì)象的增加,修改,,刪除,加載,。 2:查詢服務(wù),。這不同于CRUD中的R(read),read傾向于的單個(gè)對(duì)象,,元組,。而這里的查詢針對(duì)復(fù)雜查詢,比如一個(gè)國(guó)內(nèi)電商的客戶為四川的訂單,。這里會(huì)涉及倉(cāng)儲(chǔ)層,。所謂倉(cāng)儲(chǔ)模式指的是一個(gè)提供業(yè)務(wù)對(duì)象查詢的類,他隱藏了數(shù)據(jù)查詢的解析步驟,,封裝sql解析邏輯,。 3:事務(wù)管理。這里所說的是業(yè)務(wù)事務(wù),,在一個(gè)應(yīng)用系統(tǒng)中每次請(qǐng)求都會(huì)產(chǎn)生多次的多數(shù)據(jù)對(duì)象的新增,,修改,刪除操作,。如果我們每次都依次代開數(shù)據(jù)庫(kù)連接,,準(zhǔn)備數(shù)據(jù)包,操作數(shù)據(jù)庫(kù),,關(guān)閉數(shù)據(jù)連接,。這些將會(huì)給我們帶來很多不必要的性能開銷。數(shù)據(jù)庫(kù)管理員經(jīng)常會(huì)要求“盡量少的與數(shù)據(jù)庫(kù)交互”,,這也必須成為我們的開發(fā)原則,。更好的操作是我們?cè)趦?nèi)存中建立一個(gè)和數(shù)據(jù)倉(cāng)庫(kù),維護(hù)變化的對(duì)象,,在業(yè)務(wù)操作完成一次性提交到數(shù)據(jù)存儲(chǔ)介質(zhì),,提供業(yè)務(wù)事務(wù)。業(yè)務(wù)事務(wù)有個(gè)很好聽的名字工作單元(UOW),,在微軟給我們提供的DataSet,,orm框架都回必須存在業(yè)務(wù)事務(wù)。 4:并發(fā)處理,。UOW應(yīng)避免業(yè)務(wù)數(shù)據(jù)連接的多次提交打開而出現(xiàn),,但在內(nèi)存離線操作,這就可能導(dǎo)致數(shù)據(jù)一致性問題。在多用戶的環(huán)境,,對(duì)數(shù)據(jù)并發(fā)處理需要制定一個(gè)策略,。一般我們會(huì)采用樂觀并發(fā)處理:用戶可以任意的離線修改,在修改更新時(shí)候檢查對(duì)象是否被修改,,如果被修改者本次更新失敗,。簡(jiǎn)單的說就是防止丟失修改。防止丟失修改,,我們可以采用where 加上一系列原值,,或者加上修改時(shí)間戳或者版本號(hào)標(biāo)記。同時(shí)還有許多其他的并發(fā)解決模式,,但樂觀并發(fā)鎖用到更普遍,。 5:數(shù)據(jù)上下文:整和所有職責(zé)。在數(shù)據(jù)訪問層概念職責(zé)都會(huì)有一個(gè)共同的暴露給外部的接口,。我們需要一個(gè)高層次的組件,,來同一提供對(duì)數(shù)據(jù)存儲(chǔ)介質(zhì)的訪問操作。,,同一訪問數(shù)據(jù)庫(kù)CRUD,,事務(wù),并發(fā)服務(wù)的高層次類,,叫做數(shù)據(jù)上下文(Context),。EF中的ObjectContext,NHibernate的session,,linq to sql 的DataContext等等,。 數(shù)據(jù)訪問層的一些概念(這里不會(huì)是全部,僅一些個(gè)人覺得重要的概念): 1: 數(shù)據(jù)映射器:將內(nèi)存中修改的對(duì)象提交至存儲(chǔ)介質(zhì),,則需要要映射邏輯來完成,,數(shù)據(jù)映射器就是就是一個(gè)實(shí)現(xiàn)將某種類型的業(yè)務(wù)對(duì)象持久化的類(數(shù)據(jù)映射器模式定義如《P of EAA》)。 2:倉(cāng)儲(chǔ)層(Repository):在上面提到:所謂倉(cāng)儲(chǔ)模式指的是一個(gè)提供業(yè)務(wù)對(duì)象查詢的類,,他隱藏了數(shù)據(jù)查詢的解析步驟,,封裝sql解析邏輯。在面向?qū)ο蟮氖澜缋镂覀冇脤?duì)象進(jìn)行查詢,,返回結(jié)果為對(duì)象集,。這里的查詢可能是從數(shù)據(jù)庫(kù),或者來至緩存,,這取決你的策略,,你倉(cāng)儲(chǔ)層的實(shí)現(xiàn)。 3:工作單元(UOW):Martin Fowler《P of EAA》 定義:工作單元記錄在業(yè)務(wù)事務(wù)過程中對(duì)數(shù)據(jù)庫(kù)有影響的所有變化,。操作結(jié)束后,,作為一種結(jié)果,,工作單元了解所有需要對(duì)數(shù)據(jù)庫(kù)做的改變。在上面第3點(diǎn)業(yè)務(wù)事務(wù)講的差不多,,這里不是累述,。 4:標(biāo)示映射(Identity Map):其作用在于:便于跟蹤業(yè)務(wù)對(duì)象,調(diào)用者在一個(gè)業(yè)務(wù)事務(wù)中使用的是同一個(gè)實(shí)例,,而不是每次執(zhí)行產(chǎn)生一個(gè)新的對(duì)象,。表示映射為一個(gè)散列表存儲(chǔ)(散列具有快速定位O(1))。類似于緩存的實(shí)現(xiàn)方式,,保證了在同一個(gè)業(yè)務(wù)事務(wù)數(shù)據(jù)上下文引用修改同一個(gè)業(yè)務(wù)對(duì)象,。但絕不同于緩存。從持續(xù)時(shí)間來說,,標(biāo)示映射生命周期為業(yè)務(wù)事務(wù)內(nèi)。實(shí)現(xiàn)上等同于數(shù)據(jù)上下文期,。但比起緩存來說其周期太短,,根本不能對(duì)性能有多大的改善。緩存更重要的命中率,,而標(biāo)示映射保證同一數(shù)據(jù)上下文采用修改同一個(gè)業(yè)務(wù)對(duì)象的引用,。 5:樂觀并發(fā)鎖:在上面也曾提到,其保證離線操作數(shù)據(jù)的對(duì)數(shù)據(jù)一致性的沖突解決方法,。首先樂觀在于允許離線操作,,容忍沖突,防止數(shù)據(jù)丟失修改,,可利用原讀取數(shù)據(jù)值得where條件或者時(shí)間戳,,版本號(hào)解決。 6:延時(shí)加載:對(duì)象并不是一次性加載完成,,而是按照需求多次加載數(shù)據(jù),,到用時(shí)加載。業(yè)務(wù)對(duì)象太多關(guān)聯(lián),,數(shù)據(jù)量太多余龐大,,而我們每次業(yè)務(wù)事務(wù)需要操作的對(duì)象都只會(huì)是部分,不需要太多的數(shù)據(jù)對(duì)象,。同事業(yè)務(wù)對(duì)象中還存在循環(huán)引用,,這樣不適于對(duì)象整體的一次性加載。延時(shí)加載提供了優(yōu)化,,意圖在“盡可能的少加載,,并按需加載,只加載需要的數(shù)據(jù)部分”,。 7:持久化透明對(duì)象(PI或POCO):當(dāng)對(duì)象模型不存在任何外部依賴,,特別是對(duì)于數(shù)據(jù)訪問層的依賴,那么這個(gè)模型就是持久化透明的,POCO,。一個(gè)POCO的對(duì)象不需要繼承至某個(gè)特定的類,,實(shí)現(xiàn)特定的接口,或提供專門的構(gòu)造函數(shù),。一個(gè)非持久化透明的對(duì)象這以為者存在外部的依賴,,而我們更喜歡領(lǐng)域?qū)ο笾皇且粋€(gè)簡(jiǎn)單額c#類,可以在持久化層等獨(dú)立切換,。這就導(dǎo)致實(shí)現(xiàn)的時(shí)候我們無法很直接的跟蹤業(yè)務(wù)對(duì)象,,這就是面向方面編程(AOP)或者代理模式的大顯身手??上OP在.net中不是那么直接,,很多ORM框架如NHibernate之類的利用代理模式Emit動(dòng)態(tài)注入IL實(shí)現(xiàn)跟蹤,添加新的行為,。所以NHibernate中要求領(lǐng)域?qū)ο蟮乃凶侄螌傩苑椒ǘ急仨毷翘摲椒芍貙懙摹? 8:CQRS(Command Query Responsibility Segregation,,命令查詢職責(zé)分離):CQRS是在DDD的實(shí)踐中引入CQS理論而出現(xiàn)的一種體系結(jié)構(gòu)模式,命令和查詢被分離,。 9—存儲(chǔ)過程傳言 在google搜了下“存儲(chǔ)過程 優(yōu)劣”關(guān)鍵字,,資料并不多,出現(xiàn)了一篇關(guān)于來至51cto的關(guān)于存儲(chǔ)過程的優(yōu)缺點(diǎn)的文章,,具體這里也不指出了,。看見文章中對(duì)存儲(chǔ)過程的幾個(gè)辯解,,個(gè)人不敢茍同,,個(gè)人已經(jīng)很仔細(xì)的看了文章的時(shí)間是2011年,如果在更前寫年成的話,,個(gè)人覺得完全能夠理解,。所以有了這篇,存儲(chǔ)過程的一些傳言,。 1:存儲(chǔ)過程只在創(chuàng)造時(shí)進(jìn)行編譯,,以后每次執(zhí)行存儲(chǔ)過程都不需再重新編譯,而一般SQL 語句每執(zhí)行一次就編譯一次,所以使用存儲(chǔ)過程可提高數(shù)據(jù)庫(kù)執(zhí)行速度,。 在sql server 2000版本,,這個(gè)觀點(diǎn)沒錯(cuò),卻是如此,。但是在sql server2005文檔中很清晰的寫到 sql server2005的執(zhí)行任何sql,,關(guān)系引擎會(huì)首先查看緩存,判斷是有有其執(zhí)行計(jì)劃,。如果有,,則將會(huì)重用該執(zhí)行計(jì)劃,,以減少重新編譯sql語句生成執(zhí)行計(jì)劃的影響。包括Oracle也是這么做的,,所以在我們常見的數(shù)據(jù)庫(kù)中不存在這所謂的問題,。 2:當(dāng)對(duì)數(shù)據(jù)庫(kù)進(jìn)行復(fù)雜操作時(shí)(如對(duì)多個(gè)表進(jìn)行Update,Insert,Query,Delete 時(shí)),可將此復(fù)雜操作用存儲(chǔ)過程封裝起來與數(shù)據(jù)庫(kù)提供的事務(wù)處理結(jié)合一起使用,。這些操作,,如果用程序來完成,就變成了一條條的SQL語句,,可能要多次連接數(shù)據(jù)庫(kù),。而換成存儲(chǔ),只需要連接一次數(shù)據(jù)庫(kù)就可以了,。 這個(gè)問題在前面的架構(gòu)設(shè)計(jì)-數(shù)據(jù)訪問層簡(jiǎn)述中說過,,DBA總是告訴我們減少數(shù)據(jù)庫(kù)連接次數(shù),這是完全無爭(zhēng)議的,,我表述很贊同,。但是一定是存儲(chǔ)過程的優(yōu)勢(shì)?或者說除了存儲(chǔ)過程就沒其他方式,?在架構(gòu)設(shè)計(jì)-數(shù)據(jù)訪問層簡(jiǎn)述中介紹了來自Martin Fowler《P of EAA》的UOW(工作單元)模式,定義為工作單元記錄在業(yè)務(wù)事務(wù)過程中對(duì)數(shù)據(jù)庫(kù)有影響的所有變化,。操作結(jié)束后,,作為一種結(jié)果,工作單元了解所有需要對(duì)數(shù)據(jù)庫(kù)做的改變,。其主旨是在內(nèi)存中建立一個(gè)和數(shù)據(jù)倉(cāng)庫(kù),,維護(hù)變化的對(duì)象,業(yè)務(wù)對(duì)象變化跟蹤,,在業(yè)務(wù)操作完成一次性提交到數(shù)據(jù)存儲(chǔ)介質(zhì),,提供業(yè)務(wù)事務(wù)。這模式已經(jīng)在我們常見的ORM(EntityFramework,,Nhibernate等)中很好的支持了,,或許這么說這也是ORM框架的一個(gè)重要特征。在比如微軟的DataSet也支持,,批量更新,。 3:存儲(chǔ)過程可以重復(fù)使用,可減少數(shù)據(jù)庫(kù)開發(fā)人員的工作量,。 在項(xiàng)目中我們糜爛的重復(fù)代碼僅僅在于數(shù)據(jù)層,?更多或許在于業(yè)務(wù)邏輯的處理,復(fù)雜的條件判斷,,數(shù)據(jù)操作的組合,。存儲(chǔ)過程是由開發(fā)人員開發(fā),,還是數(shù)據(jù)庫(kù)開發(fā)人員?每個(gè)公司的數(shù)據(jù)庫(kù)開發(fā)人員就僅僅那幾個(gè)吧,,我見過的公司,。存儲(chǔ)過程是否包含業(yè)務(wù)規(guī)則?如果有的話,,業(yè)務(wù)的不停變化,,會(huì)不會(huì)不停的修改關(guān)系模型,修改存儲(chǔ)過程,,sql的編寫和調(diào)試雖然現(xiàn)在工具有一定的支持,,但是我覺得沒有開發(fā)語言這么智能方便吧,至少我還沒看見,。如果沒有至少簡(jiǎn)單的查詢語句,,那和普通的sql有什么差別?減少開發(fā)量為什么不選擇ORM之類的動(dòng)態(tài)sql,,采用完全的對(duì)象模型開發(fā),,只在少部分ORM失效的業(yè)務(wù)返璞歸真。 4:如果把所有的數(shù)據(jù)邏輯都放在存儲(chǔ)過程中,,那么asp.net只需要負(fù)責(zé)界面的顯示功能,,出錯(cuò)的可能性最大就是在存儲(chǔ)過程。一般情況下就是這樣,。升級(jí),、維護(hù)方便。 這句話更離譜,。邏輯放在存儲(chǔ)過程,,便于維護(hù),我也進(jìn)入過這樣的公司參與過這樣的項(xiàng)目,,由于剛開始新員工,,不能全盤否定,我看見的是惱人的存儲(chǔ)過程,,惱人是sql,,沒看過那個(gè)開發(fā)人員喜歡sql,特別在每次項(xiàng)目需求變更的時(shí)候,。后來慢慢接受ddd模式,,把業(yè)務(wù)從sql中掙脫出來。asp.net只需要負(fù)責(zé)界面的顯示功能,,邏輯層次未免太簡(jiǎn)單了,,我猜測(cè)這應(yīng)是 事務(wù)性腳本開發(fā)模式,其優(yōu)劣點(diǎn)在架構(gòu)設(shè)計(jì)-業(yè)務(wù)邏輯層簡(jiǎn)述中說過,,只能實(shí)用于簡(jiǎn)單小型的項(xiàng)目,。在加上可移植性差,,如果你的客戶需要數(shù)據(jù)庫(kù)的升級(jí),sql server到Oracle會(huì)怎么樣,。 5:安全性高,,可設(shè)定只有某此用戶才具有對(duì)指定存儲(chǔ)過程的使用權(quán)。 安全對(duì)于項(xiàng)目來說不僅僅在于數(shù)據(jù)庫(kù),,而應(yīng)是分布于我們系統(tǒng)各處,。安全關(guān)注點(diǎn)應(yīng)該從表現(xiàn)層到數(shù)據(jù)庫(kù)各層之間都應(yīng)該有處理。一般比較靈活有效基于角色(域)安全和數(shù)據(jù)庫(kù)安全,,物理服務(wù)器安全共同使用,,這和不適用存儲(chǔ)過程,使用sql并沒什么沖突,。雖然你可能說存儲(chǔ)過程可以作為數(shù)據(jù)庫(kù)內(nèi)部資源實(shí)施安全策越,。 6:還有些:存儲(chǔ)過程可以防止sql注入 這個(gè)是當(dāng)然的,毫無爭(zhēng)議,。因?yàn)橛玫氖菂?shù)化方式,,你不能隨意拼接字符串,參數(shù)化方式能夠幫助我們防止大多數(shù)的sql注入,。在ado.net中為我們提供了很好的參數(shù)化支持,,使用sql我們同樣可以做到,再加上一切開源的安全組件的過濾,。 最后存儲(chǔ)過程并不是萬惡的,,他有他的應(yīng)用場(chǎng)景,對(duì)于復(fù)雜邏輯如報(bào)表的場(chǎng)景,,我會(huì)毫不猶豫的放棄ORM,選擇它,,因?yàn)閛rm不能滿足這種復(fù)雜查詢,,但是準(zhǔn)確的說我選擇的是大量的T-SQL或者是P-SQL,存儲(chǔ)過程就是一堆sql子程序的固定格式,。我覺得可以完全采用ibatis.net方式的xml配置,,更爽些。選擇存儲(chǔ)過程是由于復(fù)雜查詢業(yè)務(wù),,我相信大家也不會(huì)為了一些復(fù)雜的統(tǒng)計(jì)把全表數(shù)據(jù)加載到內(nèi)存吧,。存儲(chǔ)過程開發(fā)技術(shù)流行與2005前數(shù)據(jù)為中心的開發(fā)模式,在現(xiàn)在的模式,,工具,,技術(shù)下顯得有些蒼老,但并不是一無是處,。你也可以完全采用基于存儲(chǔ)過程的開發(fā)模式開發(fā)出很好的系統(tǒng),。 10—表現(xiàn)層模式-MVC 在前面簡(jiǎn)述了從服務(wù)層到數(shù)據(jù)層,。剩下了表現(xiàn)層,一個(gè)再好的中間層表現(xiàn)也必須有一個(gè)用戶界面,,提供和用戶交互,,將用戶行為輸入轉(zhuǎn)化為系統(tǒng)操作,進(jìn)入后臺(tái)邏輯,。在當(dāng)下RAD(快速應(yīng)用開發(fā))工具的支持下,,我們可以比較快速的完成UI設(shè)計(jì),RAD追求所見即所得的快速反饋,,快速應(yīng)用,。表現(xiàn)層也有一定其固定的邏輯(格式化,數(shù)據(jù)綁定,,轉(zhuǎn)化等等,,稱為UI邏輯)和界面展現(xiàn)。這里UI邏輯指的是所有用來處理數(shù)據(jù)顯示在UI界面的邏輯和,,將UI用戶輸入行為轉(zhuǎn)化為中間層指令的邏輯,,負(fù)責(zé)UI和中間層數(shù)據(jù)流和行為的轉(zhuǎn)化。很多時(shí)候UI是最容易變化的以及最不易測(cè)試的邏輯(我一直相信,,1:一段好的代碼一定要易于測(cè)試,。2:重構(gòu)的前提也必須有足夠的測(cè)試保證,才能讓我們的重構(gòu)更有節(jié)奏更自信),,而很大部分UI邏輯卻往往比較穩(wěn)定的,。這Matin Fowler提出的分離表現(xiàn)層模式。表現(xiàn)層模式主要分為3種大類:MVC,,MVP,,PM(微軟在sl和wpf起名為MVVM),這3類模式下延伸了很多變異體mvc在web的 model2(asp.net mvc,,主要特征基于web特有uri路由),。mvp的變種:Passive View(被動(dòng)視圖)和Supervising Controller(不清楚怎么翻譯比較好),PM延伸MVVM,。其目的都在于將多變的View和UI邏輯分離,。 今天要說的是MVC(model-view-controller:模型-視圖-控制器)。在我們一開始就說結(jié)構(gòu)和編程或者面向?qū)ο笤瓌t都是為了實(shí)現(xiàn)模塊的高內(nèi)聚低耦合,,而高內(nèi)聚低耦合行之有效的方式就是分離關(guān)注點(diǎn)(SOC),。為了實(shí)現(xiàn)表現(xiàn)UI和表現(xiàn)邏輯的分離,使得他們之間更靈活,,并且自治視圖(包含所有表現(xiàn)層代碼的類),。在30年前Trygve Reenskaug提出的MVC模式,或者更確切的說模范。其將表現(xiàn)層分為3類:model:是視圖展現(xiàn)數(shù)據(jù),,view用戶交互界面,,Controller:將用戶輸入轉(zhuǎn)化為中間層操作。 模型(Model):在MVC中模型保持著一個(gè)應(yīng)用程序的狀態(tài),,和相應(yīng)視圖中來自用戶交互的狀態(tài)變化,。在上圖中我們可以看到model會(huì)接受來之控制器的狀態(tài)變化響應(yīng)和視圖的顯示狀態(tài)查詢view渲染的數(shù)據(jù)來源。同時(shí)model還有通過事件機(jī)制(觀察者模式)通知view狀態(tài)的改變要求view渲染響應(yīng),。view和模型之間存在一定的耦合,,view必須了解model,這也是MVP模式出現(xiàn)原因之一,。正在這里的模型model可以是來之分布式soap或者resetfull的dto(數(shù)據(jù)傳輸對(duì)象),,也可以直接是我們的領(lǐng)域?qū)ο螅╠o)或者數(shù)據(jù)層返回的數(shù)據(jù)集,并不嚴(yán)格的 要求,。 控制器(Controller):控制器是又view觸發(fā),,響應(yīng)用戶界面的交互,并根據(jù)表現(xiàn)層邏輯改變model狀態(tài),,以及中間層的交互,,最終修改model,Controller不會(huì)關(guān)心視圖的渲染,,是通過修改model,,model的事件通知機(jī)制,觸發(fā)view的刷新渲染,??刂破骱湍P偷慕换ナ揭环N“發(fā)出即忘”,或者分布式OneWay的調(diào)用,??刂破鰿ontroller不會(huì)主動(dòng)了了解view和view交互,除了唯一的視圖選擇外,,控制器需要選擇下一次顯示的視圖view是什么,。一般控制器會(huì)請(qǐng)求全局應(yīng)用程序路由下一個(gè)需要顯示的view,在使其呈現(xiàn)出來,。 視圖(View):視圖時(shí)表現(xiàn)層模式出現(xiàn)的原因,因?yàn)樗亩鄻有院妥兓念l繁性,,不易測(cè)試(太多外界環(huán)境依賴),,所以理想的視圖應(yīng)該盡可能的啞,被動(dòng),,視圖只負(fù)責(zé)渲染呈現(xiàn)給用戶交互,。視圖由一些列GUI組件組成,響應(yīng)用戶行為觸發(fā)控制器邏輯,,修改model狀態(tài)使其保持view同步,。視圖并需要相應(yīng)model的變化被動(dòng)的接受model狀態(tài)變化刷新相應(yīng)給用戶,。 MVC最先興起于桌面,但沒有流行起來,,知道在web興起后,,其變異體Model2在Web中流行起來,.net 下的ASP.NTE MVC,。Model2中,,將來自客戶端(瀏覽器)的請(qǐng)求,被服務(wù)端攔截器(asp.net中HttpModule)根據(jù)url格式請(qǐng)求方式等轉(zhuǎn)發(fā)到固定的控制器,,調(diào)用固定的Action,,在Action中隊(duì)模型狀態(tài)進(jìn)行修改,并選擇view,,view并根據(jù)控制器傳來的最新model生產(chǎn)html,,css,js前段代碼,,并輸出到前段渲染,。 在Model2中和原始MVC最大的差別在于: 1:視圖view和模型model之間沒有直接依賴,model并不知道view也不需要事件通知view,,view也不需知道m(xù)odel,,view操作的都是ViewModel(asp.net mvc 中ViewData容器)。2:控制器顯示傳入視圖數(shù)據(jù)給view,,相應(yīng)用戶的操作不是來自view,,而是出于服務(wù)端應(yīng)用程序前段的攔截器,捕獲url并轉(zhuǎn)發(fā)到相應(yīng)的控制器,,已經(jīng)調(diào)用相應(yīng)的action方法,。 在現(xiàn)在說的MVC往往指的就是Web中Model2模式。在Model2中view是被動(dòng)的,,啞的,,簡(jiǎn)單。 出處:http://www./zjjs/201212065.asp#6 |
|