2022項(xiàng)目代碼架構(gòu)_第1頁
2022項(xiàng)目代碼架構(gòu)_第2頁
2022項(xiàng)目代碼架構(gòu)_第3頁
2022項(xiàng)目代碼架構(gòu)_第4頁
2022項(xiàng)目代碼架構(gòu)_第5頁
已閱讀5頁,還剩117頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

項(xiàng)目代碼架構(gòu)第1第13.1.1需求?需求!1.2敏捷開發(fā)簡介1.3UI——用戶界面1.4數(shù)據(jù)庫第22.1MOL帶兄弟們?nèi)コ燥?.2動手寫一個三層結(jié)構(gòu)2.3簡說MVC2.4向三層代碼中加入面向?qū)ο?.5小說代碼管理13.2.6小結(jié)第3ORM實(shí)體關(guān)系映射3.1說說OCP開放封閉原則3.2解耦第一步——接口要上位3.3解耦第二步——工廠模式解決new的問題3.4Spring.NET橫空出世3.5我不想寫SQL語句20.3.6小結(jié)第2NoSQL和測試第44.1客戶總有一些非分的想法4.2MongoDB簡介4.3NET操作MongoDB4.4讓NoSQL面向?qū)ο?.5NoSQL題外話4.6關(guān)于日志29.4.7小結(jié)第55.1簡單說測試5.2冒煙測試5.3黑盒測試5.4單元測試5.5白盒測試5.6壓力測試5.7其他測試38.5.8小結(jié)第3第66.1網(wǎng)站崩潰了6.2緩存是什么6.3微軟提供了緩存6.4自己做緩存6.5第三方緩存6.6利用模板引擎生成靜態(tài)頁面6.7CDN的加入會大大減少服務(wù)器的壓力48.6.8小結(jié)第77.1常見的前端框架7.2嘗試EasyUI7.3其他的前端框架7.4小說HTML554.7.5小結(jié)第88.1網(wǎng)站又崩潰了!8.2從相親說起8.3簡述消息隊列8.4常見的消息隊列60.8.5小結(jié)第99.1李老板出場,請熱烈鼓掌9.2初探微信公眾號9.3微信小程序9.4微服務(wù)9.5MongoDB數(shù)據(jù)庫9.6大數(shù)據(jù)68.9.7小結(jié)第1篇 需求與三層架構(gòu)第1章 故事從一個電商網(wǎng)站開始第2章 為什么是三層第3章 ORM實(shí)體關(guān)系映射第1章 故事從一個電商網(wǎng)站開始按照慣例,一本書的開始一般會介紹一些基礎(chǔ)知識,如Java語法、XML結(jié)構(gòu)等。相信很多讀者都立志做一名“高大上”的Coder,但是一看這種開篇講語法的書就先泄氣了。為了不讓大家泄氣,我們將使用一個電商網(wǎng)站項(xiàng)目來作為開篇。說到項(xiàng)目,大家一定會想到某培訓(xùn)機(jī)構(gòu)的機(jī)票管理系統(tǒng)、通訊錄……估計大家都要被這些標(biāo)題黨搞瘋了。這些聽起來很好聽的系統(tǒng),不一定有什么實(shí)用價值。但是我們要介紹的項(xiàng)目,是一個實(shí)打?qū)嵉?、已上線的、微信上可查找的一個項(xiàng)目。如果各位摩絲在任何時候覺得學(xué)習(xí)有點(diǎn)累了,或者迷茫了,可以上微信搜索“川商卡”,這就是我們要講的系統(tǒng)。MOL在做這個系統(tǒng)的時候,用到的技術(shù)并不多,像緩存、消息隊列等這些技術(shù)在項(xiàng)目中沒有出現(xiàn),但在本書中MOL也會講到。MOL可以很負(fù)責(zé)任地說,當(dāng)大家學(xué)完這本書后,可以毫不費(fèi)力地給自己快速搭建一個“高大上”的代碼框架。下面的內(nèi)容,將是一些非常雜亂的、與代碼看似無關(guān)的基礎(chǔ)知識,請大家耐心看完。需求?需求!相信大家對一些翻譯軟件的使用已達(dá)到爐火純青的地步了,如果把“需求”這個詞輸入翻譯軟件,會出來一大堆對應(yīng)的英文單詞。這里只挑兩個容易混淆的單詞來說,即Request和Demand。通常出現(xiàn)需求的地方,一般都會用Demand來描述,但是偶爾也會看到Request。這兩個單詞都是需求的意思,有啥區(qū)別嘞?Demand是必須要完成的,沒有商量的余地。比如你的BOSS告訴你:我們的項(xiàng)目要加入某寶支付的功能。這就是Demand。雖然過兩天BOSS有可能把這個功能“砍掉”,但是令行禁止,你還是必須得把某寶支付的功能實(shí)現(xiàn)。Request是錦上添花的功能。比如你的BOSS是一位單身宅男,他問你:在網(wǎng)站某個頁面的右下角加入一個美女的圖片,可以嗎?這就是Request,它表示請求。也就是說,你不加這個圖片,項(xiàng)目照樣運(yùn)行。至于這個圖片是否要加,那就看你的心情嘍。下面,我們來描述一下本書中的項(xiàng)目需求。由于本書中的項(xiàng)目采用的是敏捷開發(fā),所以開發(fā)過程中的文檔特別少(關(guān)鍵文檔是不可以省略的)。這里不可能列出所有的需求文檔內(nèi)容,因此將使用BrainStorm(頭腦風(fēng)暴)的方式來描述需求:這是一個電商網(wǎng)站;這是一個O2O電商網(wǎng)站;這個網(wǎng)站有登錄注冊功能;這個網(wǎng)站有商品展示功能;這個網(wǎng)站有訂單功能;這個網(wǎng)站有支付功能;這個網(wǎng)站有積分功能。OK,需求就是這么多。估計很多小伙伴都已經(jīng)開始吐槽了吧:上面的描述也能算需求嗎?MOL可以很負(fù)責(zé)地告訴你,是的,而且這是溝通4個小時后的成果,你相信嗎?好了,先不吐槽需求發(fā)起人了。上面的7也沒什么難點(diǎn)啊。但如果你這樣想就錯了。至于哪里錯了,MOL不會做直接回答,在后面的每個章節(jié)中,都會對這個問題進(jìn)行回答,請大家自行品悟。如果你想要了解詳細(xì)的需求,那可能要失望了,因?yàn)镸OL并不打算在這里描述一個完整的需求,而且這并不是實(shí)際開發(fā)中的情況。在敏捷開發(fā)的項(xiàng)目里,一般都是到開發(fā)的最后階段,才能明白客戶想要的是什么東西。這里引用一幅項(xiàng)目開發(fā)領(lǐng)域流傳很廣的漫畫來說明,如圖1-1所示。圖1-1 項(xiàng)目開發(fā)漫畫敏捷開發(fā)簡介前面提到了敏捷開發(fā),聽起來是一個非常“高大上”的名詞。下面讓我們來看一下敏捷開發(fā)的真面目。獨(dú)立運(yùn)行的小項(xiàng)目,并分別完成,在此過程中軟件一直處于可使用狀態(tài)。(引自百度百科)如果專業(yè)的解釋看不太懂,沒關(guān)系。我們用簡單的語言來描述敏捷開發(fā)的過程。廠家收到需求以后,肯定是一頭霧水,一定在想,我們做的每一部手機(jī)都非常好用呀,此時的心情是崩潰的。但是本著顧客是上帝的原則,廠家開始做這部好看且好用的手機(jī)。首先,他們把手機(jī)殼做了出來,打電話把小明叫到工廠:小明,手機(jī)殼做好了,你看是否符合你的需求?小明看完以后,無非就兩種回答,符合或不符合。如果符合,那么廠家將繼續(xù)研發(fā)手機(jī)屏幕。如果不符合,那么將根據(jù)小明的需求繼續(xù)修改。又過了幾天,廠家打電話:小明,屏幕做好了,你看是否符合你的需求?……終于,在一個陽光明媚的下午,小明交完錢,拿到了自己心儀的手機(jī),留下孤寂的廠家負(fù)責(zé)人在風(fēng)中凌亂:就一個諾基亞1020,至于讓我們這么費(fèi)勁嗎。這就是一個敏捷開發(fā)的表現(xiàn)形式,它最明顯的特點(diǎn)就是小迭代,每個小功能都去找客戶確認(rèn),最后完成產(chǎn)品的同時,也就知道了客戶的具體需求。這樣描述敏捷開發(fā)肯定是不全面的,我們的目的是不給出一個敏捷開發(fā)的準(zhǔn)確定義,只是想讓大家對敏捷開發(fā)有一個感性認(rèn)識。敏捷開發(fā)越來越多地被很多開發(fā)團(tuán)體利用。它的好處就是開發(fā)周期短、與客戶交流密切,一旦有問題出現(xiàn),能很快做出響應(yīng)。在敏捷開發(fā)的團(tuán)隊里,逐漸地出現(xiàn)了一種人——“全棧工程師”,這種人的優(yōu)勢在于:他們是無所不知的。你說前端,他能給你講HTML5和CSS3;你說C#,他能和你探討.NET框架;你說Java,他能和你研究JVM;你說大數(shù)據(jù),他還對Hadoop的理解有獨(dú)到之處,仿佛是無所不能。相信很多摩絲都有一個“全棧夢”,加油!UI——用戶界面UI(UserInterface,用戶界面),這是一個可大可小的概念。往小了說,它就是一個圖形界面,如網(wǎng)頁、桌面窗口、手機(jī)窗口……只要是你看見的,都可以算是UI;往大了說,UI包括產(chǎn)品經(jīng)理、用戶體驗(yàn)工程師、美工(靜態(tài)切圖PS)、前端(HTML、CSS)、前端交互(JavaScript、jQuery、EasyUI)……數(shù)據(jù)庫別問我數(shù)據(jù)庫是什么。MOL說過,這本書里不講基礎(chǔ)知識。在這里講的是如何去設(shè)計數(shù)據(jù)庫。MOL相信很多大學(xué)老師一定是這樣教學(xué)的:設(shè)計數(shù)據(jù)庫一定要先設(shè)計數(shù)據(jù)字典,這個字典看起來像這樣,如表1-1所示。表1-1 登錄信息的數(shù)據(jù)字典這個字典描述了一個用戶登錄信息表。設(shè)計好這個字典以后,再去數(shù)據(jù)庫(SQLServer\Oracle\MySQL……)中去實(shí)現(xiàn)這個字典,實(shí)現(xiàn)的方法無非就是SQL語句createtable,或者用圖形化工具去設(shè)計表。大家有沒有覺得這個字典設(shè)計得有點(diǎn)雞肋?如果要改需求,比如加入注冊時間這個字段,試問,你會先修改字典,然后再去修改數(shù)據(jù)庫嗎?如果需求更改得比較頻繁,那么你一定會厭煩“數(shù)據(jù)字典”這個東西的。其實(shí)不然,數(shù)據(jù)字典是一個神器,只是你的打開方式不對。第2章 為什么是三層還記得在引言中提到的幾位主人公嗎?從這一章開始,他們就要粉墨登場了!“三層架構(gòu)”這個詞一定是新手程序員經(jīng)常聽到的,大家聽起來一定覺得它有點(diǎn)“高大上”的感覺,然后紛紛把自己的項(xiàng)目進(jìn)行分層,以求變成三層。那么,三層架構(gòu)到底是什么?為什么要分三層?我們慢慢說來。MOL帶兄弟們?nèi)コ燥垥r維九月,歲屬三秋,這是一個陽光明媚的金秋,MOL所在的公司又新招了三位同學(xué),MOL總算不是孤軍奮戰(zhàn)了,于是MOL帶大家去了樓下一家餐廳吃飯。吃飯不是目的,這是給大家上的第一堂課。到了餐廳以后,一個很水靈的妹子迎上前來,問道:“幾位帥哥要飯嗎?”MOL當(dāng)時就不高興了,回道:“你看我們長得像丐幫的嗎?”妹子臉一紅:“我的意思是,各位想吃點(diǎn)什么?”這時,月月搭腔了:“蒸羊羔、蒸熊掌、蒸鹿尾兒、燒花鴨、燒……”妹子趕緊打?。骸案绺?,我得問一下廚房師傅,看看能不能做。”不大一會兒,妹子出來了:“實(shí)在對不住,您幾位剛才點(diǎn)的那些,我們這里都做不了,因?yàn)檫@里是西餐廳。”月月:“那二尺長的龍蝦有嗎?”妹子:“二尺長的沒有,有二尺七的,要嗎?”MOL一看不對勁,這是要把我吃到破產(chǎn)的節(jié)奏啊,趕緊說:“二尺長的龍蝦都沒有,那就來小龍蝦吧?!泵米樱骸靶↓埼r暫時沒有,如果您可以等的話,等采購師傅回來了,就有了。”MOL:“可以等,不就幾分鐘嘛?!泵米討崙嵉叵氯チ耍略乱惨荒槻粷M意,MOL趕緊打圓場:“不就是龍蝦嘛,大小都是龍蝦,小怎么啦?小就不能滿足你了?”然后是等待吃飯……吃完飯回到公司,MOL招集大家開會。MOL:“今天這頓飯不能白吃,它將開始我給你們的第一課——三層架構(gòu)。”月月:“吃飯都能扯上架構(gòu),神了嘿。”鵬輝:“今天的飯吃得確實(shí)有點(diǎn)意思,不過還能和程序扯上關(guān)系就有點(diǎn)意思了。”MOL:“我們剛才在餐廳吃飯的時候,總共有這么4個角色,分別是我們3個“飯桶”、服務(wù)員、大廚和采購師傅。”(下面都是MOL口述,將不再以引號來包含)在程序員的世界里,這幾個角色分別對應(yīng)的關(guān)系是:我們3個“飯桶”對應(yīng)用戶,因?yàn)槲覀兪腔ㄥX享受服務(wù)的。服務(wù)員是UI(UserInterface用戶接口)層,她是要展示給用戶,并且和用戶進(jìn)行交互的,而且她還要和大廚進(jìn)行交互。大廚是BLL(BusinessLogicLayer業(yè)務(wù)邏輯層),他的任務(wù)是把食材加工成美食,并交給服務(wù)員,所以他既要和采購師傅交互,又要和服務(wù)員交互。采購師傅是DAL(DataAccessLayer數(shù)據(jù)訪問層),它的任務(wù)是把食材采購回來并交給大廚。除此之外,還有一個隱形的角色是菜市場的大媽,她負(fù)責(zé)把菜賣給采購師傅。她對應(yīng)我們軟件系統(tǒng)中的數(shù)據(jù)庫。當(dāng)然,我們對大媽是不感興趣的,所以這里先不說大媽的事。OK,根據(jù)上面的描述,我們可以把一個餐廳里的工作分成3個部分,分別是服務(wù)員、大廚和采購師傅,他們之間的相互關(guān)系如圖2-1所示。圖2-1 餐廳分工大家有沒有想過,如果我雇傭一個人,這個人既會炒菜又能采購,還會當(dāng)服務(wù)員,那么這些角色不就不用分開了嗎?非常好,我們假設(shè)有這樣一個人存在,他既要當(dāng)服務(wù)員與食客溝通,還要炒菜,還要去買菜,那么將會發(fā)生什么情況呢?這個人先要恭敬地問食客“您來點(diǎn)什么?”,然后再跑到廚房換上工作服開始炒菜,如果發(fā)現(xiàn)沒菜了還得自己去買菜。我相信,即使有這樣一個人存在,那么他也會累得夠嗆。而且他炒菜或者采購的時候,是不能與其他的食客溝通的。最后他累得實(shí)在不行了,辭職走人了,老板就哭了,因?yàn)樗蛔?,餐廳就沒人干活了,餐廳也得關(guān)門。所以,分開服務(wù)員、大廚、采購員這幾個角色,有利于每個角色都專注于自己的職責(zé)任不至于讓整個餐廳都變得很被動。好了,說完了餐廳的分工,再來說一下我們所關(guān)心的代碼中的三層架構(gòu)。通常意義上來說,三層架構(gòu)是UI、BLL、DAL這3層。這3層可以對應(yīng)到餐廳中的3個角色來對比理解。UI層負(fù)責(zé)與用戶交互,并將用戶的請求交給BLL層處理;BLL層負(fù)責(zé)從UI層獲取請求并將處理后的數(shù)據(jù)交給UI層,同時它還向DAL層發(fā)送請求,并獲取DAL層返回的數(shù)據(jù);DAL層負(fù)責(zé)接收BLL層的請求,并進(jìn)行數(shù)據(jù)處理然后返回給BLL層,在大多數(shù)情況下,DAL還需要從數(shù)據(jù)庫中獲取數(shù)據(jù)。它們之間的關(guān)系如圖2-2所示。圖2-2 程序中的三層結(jié)構(gòu)程序員寫的代碼。這樣的結(jié)構(gòu)將會面臨很大的風(fēng)險,如果業(yè)務(wù)邏輯變動,那么將會出現(xiàn)“牽一發(fā)而動全身”層(Layer)去做專業(yè)的事情,如果邏輯有變動,那么只需要修改相應(yīng)的層就可以了。PS:餐廳吃飯的例子非常經(jīng)典,它將貫穿于本書中的章節(jié),在后面的章節(jié)中經(jīng)常會說到“如果餐廳中的大廚有個助手……”,希望大家能立刻回想起MOL餐廳吃飯的例子。動手寫一個三層結(jié)構(gòu)說了這么多,都只是停留在概念的層次,接下來,我們要寫一個簡單的三層結(jié)構(gòu)的框架。這個框架只需要實(shí)現(xiàn)一個功能:用戶輸入兩個數(shù),并選擇運(yùn)算方法,運(yùn)算只包括+、-、*、/。程序通過計算,將計算結(jié)果輸出到用戶界面上。OK,需求已經(jīng)非常明確了,就是要做一個簡單的能進(jìn)行加、減、乘、除運(yùn)算的計算器。那么前端可以使用任何方式,比如控制臺、WinForm、WebForm、MVC……為了直觀和簡單,這里將采用WebForm來做為前臺界面。而BLL主要是將數(shù)據(jù)邏輯進(jìn)行處理,并調(diào)用DAL層計算結(jié)果,將得到的結(jié)果返回給界面。DAL只需要提供數(shù)據(jù)就OK了。在本例中,不考慮邊界異常(如太長的數(shù)字會溢出等情況)。首先需要把項(xiàng)目的框架搭建好。新建一個名為Mol.Calc的解決方案,并加入一個Web項(xiàng)目(Mol.Calc.Portal)和兩個類庫項(xiàng)目(Mol.Calc.Bll和Mol.Calc.Dal),如圖2-3所示。圖2-3 三層代碼框架PS:項(xiàng)目的命名一定要規(guī)范,一般來說,項(xiàng)目命名都是“公司.項(xiàng)目.模塊名”。MOL所使用的開發(fā)環(huán)境是Windows7+VisualStudio2015+SQLServer2012。關(guān)于環(huán)境的配置這里不會多說,否則顯得本書很low,大家也會很不耐煩。簡說MVC提到三層架構(gòu),很多人就會想到MVC(ModelViewController,模型-視圖-控制器)模型。MVC的結(jié)構(gòu)如圖2-9所示。圖2-9 MVC模型示意圖圖2-9描述了一個MVC框架處理用戶請求的流程。用戶發(fā)起請求,請求將被送給Controller。去Model中取數(shù)據(jù)。Controller。Controller將數(shù)據(jù)返回給View。View展示給用戶。如果這個流程讓你覺得難以理解,不要擔(dān)心,因?yàn)槲覀冞€沒有開始寫MVC的代碼,所以我們無法理解用戶請求為什么是到控制器(Controller)而不是到視圖界面(View),最后返回的時候不通過控制器返回,而通過視圖來返回……大家只需要對MVC有一個感性認(rèn)識即可,知道每一部分是干什么用的就OK了。向三層代碼中加入面向?qū)ο竺嫦驅(qū)ο笃鋵?shí)是一個非常寬泛的概念,寬泛到不足以用一個章節(jié)甚至是一本書來說明面向?qū)ο螅玀OL將盡量在書中的例子中浸透面向?qū)ο蟮乃季S。前面已經(jīng)講述了通常的三層代碼結(jié)構(gòu),本節(jié)將在三層代碼中加入面向?qū)ο蟮脑?。這種面向?qū)ο蟮乃枷朐诒竟?jié)中將體現(xiàn)在兩個地方:數(shù)據(jù)庫表的面向?qū)ο?;將所有的SQL操作都放到一個類(SqlHelper)中。小說代碼管理安撫一下沖沖受傷的心靈,MOL又開始借題發(fā)揮了。MOL:親愛的同學(xué)們,你們寫代碼的時候一定要經(jīng)常用Ctrl+S命令保存一下代碼,就算下一秒是世界末日,也要把你的勞動成果保存下來。沖沖:有一次,我的計算機(jī)丟了,心疼死了。倒不是心疼計算機(jī),而是心疼里面的好多學(xué)習(xí)資料。鵬輝:得了吧,你是心疼你那好幾百GB的“學(xué)習(xí)資料吧”。劉朋:瞎說啥實(shí)話。你把這些資料備份到網(wǎng)盤上不就得了?沖沖:瞧你們一個個齷齷齪齪的樣子,我的學(xué)習(xí)資料都是編程代碼神馬的。我還遇到一個問題,就是我在公司寫的代碼,如果要拿到家里看的話,就需要用U盤拷回家,或者用郵箱存儲起來,這樣拷貝非常不方便,并且很容易有錯拷、漏拷的現(xiàn)象,想想還是有點(diǎn)小糾結(jié)的。如果有一個軟件,可以記錄我每次修改的時間和修改的內(nèi)容,那就非常happy了。MOL:世界上不缺乏美,而是缺少發(fā)現(xiàn)美的眼睛。你剛才說的這種軟件很多,MOL接下來給大家展示一下這些軟件。小結(jié)本章主要講述了簡單的三層架構(gòu),進(jìn)而講到了MVC架構(gòu),并在代碼中加入了面向?qū)ο蟮脑兀詈筇岬搅舜a管理的一些軟件,著重講解了Git的使用。正如我們在2.4節(jié)中提到的,“面向?qū)ο蟆笔潜緯械囊粭l主線,希望大家能接受它,愛上它,并在你的代碼中使用它。第3章 ORM實(shí)體關(guān)系映射冰河解凍,彩蝶紛飛,狗熊撒歡。柳絮飄舞,萬物復(fù)蘇,這是一個……正當(dāng)MOL抒發(fā)感情到不能自已的時候,以劉朋為首的三人小分隊又如鬼魅一般地冒了出來。正所謂來者不善,善者不來,MOL先氣沉丹田,使了一個夜戰(zhàn)八方藏刀式:“呔,來將所謂何事?”劉朋:主帥莫慌,小的們有好東東要分享。MOL理了一下三七分的毛寸發(fā)型:“我慌了嗎?”MOL:言歸正傳,有何事分享?劉朋煞有介事地說:Thiswayplease。鵬輝:說人話!劉朋:請上坐,待吾慢慢呈于您敬觀。MOL不慌不忙地坐在工位上,劉朋打開VisualStudio。一邊點(diǎn)著鼠標(biāo)一邊解說:我們聽完三層架構(gòu)以后,做了一個簡單圖書查詢系統(tǒng),這是DAL層,負(fù)責(zé)訪問數(shù)據(jù)庫;這是BLL層,負(fù)責(zé)處理業(yè)務(wù)邏輯,UI層用MVC寫的。MOL看著,臉上露出了欣慰的微笑。劉朋:這個項(xiàng)目的功能非常簡單,但是……中文就是如此的博大精深,一個“但是”個“但是”把MOL的小心肝嚇得撲通撲通的。劉朋一臉壞笑,接著說道:但是,DAL層存在大量重復(fù)的SQL語句的片斷,無法抽象出來,而且存在大量的硬編碼,不利于代碼的擴(kuò)展。MOL:嚇?biāo)离蘖?,給我來杯82年的雪碧壓壓驚。MOL喝了一口水之后,接著說道:我以為多大點(diǎn)事呢。以你們現(xiàn)在的水平,能把這個項(xiàng)目寫完了,已經(jīng)很了不起了,寫完了還能自己進(jìn)行分析,甚至還能提出代碼擴(kuò)展方面的見解,這大大超出了我的預(yù)期。今天我就來帶著你們?nèi)グ裇QL語句“扼殺在搖籃里”。說說OCP開放封閉原則要操作數(shù)據(jù)庫,就不可避免地要寫大量的SQL語句。這些SQL語句可以滿足很多初級程序員的虛榮心,看著這些簡單或復(fù)雜的SQL語句被程序運(yùn)行以后就可以得到自己想要的數(shù)據(jù),是不是很有成就感?但是,問題也就隨之而來了。當(dāng)寫一個SQL語句的時候,就注定了這條SQL語句只能滿足一種業(yè)務(wù)場景或一種類型業(yè)務(wù)場景。當(dāng)業(yè)務(wù)場景發(fā)生變化的時候,SQL語句也有可能發(fā)生變化。SQL語句的變化又可能引起DAL層的變化,進(jìn)而影響到BLL層。這樣一系列的連鎖反應(yīng),是我們不想看到的。當(dāng)不得不修改某些代碼的時候,我們更希望這些修改的影響范圍最小,盡量不要涉及其他的層(Layer)。設(shè)計模式(DesignPattern)中的開放封閉原則(OCPOpenClosedPrinciple)的目的就是要解決上面提到的問題。開放封閉原則簡稱開閉原則,是設(shè)計模式中非?;A(chǔ)的一個原則,它的表述是:在軟件開發(fā)過程中,代碼應(yīng)該對擴(kuò)展是開放的,對修改是封閉的。也就是說如果你要新增功能,歡迎;如果要修改現(xiàn)有的功能,對不起,此路不通。那問題來了:如果業(yè)務(wù)有更改,就是要修改代碼,怎么辦?看來修改SQL是勢在必行了。既然繞不過這個問題,那么就只能讓修改SQL的影響范圍變?yōu)樽钚 W钇鸫a,DAL的修改不要影響到BLL層。但是BLL層調(diào)用DAL層的時候,經(jīng)常會這樣寫:dalClassdal=newdalClass();//實(shí)例化一個dal對象這樣的代碼導(dǎo)致DAL的修改必然會影響到BLL層,我們通常把這種關(guān)聯(lián)關(guān)系叫做“耦合”。那么怎么能讓DAL不影響到BLL層呢?如何讓DAL層和BLL層的耦合關(guān)系不要那么密切呢?甚至讓BLL層根本不知道DAL層的存在,就達(dá)到了讓BLL層和DAL層沒有耦合的目的,也叫“解耦”。PS:計算機(jī)科學(xué)領(lǐng)域的任何問題都可以通過增加一個間接的中間層來解決。這句話是計算機(jī)界中的格言,那我們就試著加一個“中間層”來解決上面提到的問題。解耦第一步——接口要上位從本節(jié)開始,我們要開始慢慢地搭建自己的代碼結(jié)構(gòu)了,我們將要做一個類似“川商卡”的項(xiàng)目。MOL是山西人,所以把項(xiàng)目名稱定為“晉商卡”。解耦第二步——工廠模式解決new劉朋:接口層就長這個樣子啊,好像也沒什么用嘛。鵬輝:如果沒有接口層的話,我們是這樣定義的:CustomerDALcustomerDAL=newCustomerDAL();有了接口層以后,我們的定義是這樣的:IDALcustomerDAL=newCUstomerDAL();在寫法上的差別并不是太大,而且增加了接口層還會增加開發(fā)量。接口層的意義也就是在設(shè)計層面吧。沖沖:你講的接口層的目的是阻斷BLL和DAL層之間的關(guān)聯(lián),但我們在寫代碼的時候還是會去new一個DAL對象,好像也沒有達(dá)到目的啊。MOL:你們提的問題,也是我們接下來要探討的知識。其實(shí),沖沖的疑問是比較關(guān)鍵的,只要把new的問題解決了,大家也就自然了解了接口層其實(shí)并不是雞肋。為了不使用new來實(shí)例化一個DAL,那么我們會考慮使用工廠模式來達(dá)到這個效果。工廠模式算是一種比較常見的設(shè)計模式(DesignPattern),工廠模式的目的是不用new來創(chuàng)建一個實(shí)例。接下來用“演繹法”來描述一下工廠模式。前面已經(jīng)說過,加入接口層以后,實(shí)例化一個對象可能是這樣:IDALcustomerDAL=newCUstomerDAL();我們不想用new的方式來創(chuàng)建實(shí)例,而希望是這樣:IDALcustomerDAL=工廠.創(chuàng)建(實(shí)例類型);第一反應(yīng),就是把new型來“制造”出不同的對象實(shí)例。最簡單的方式是使用switch…case來輸出對應(yīng)的對象實(shí)例。例如:publicstaticobjectGetInstences(stringclassName){switch(className){case"userDal":returnnewuserDal();case"orderDal":returnneworderDal();case"pageDal":returnnewpageDal();case"customerDal":returnnewcustomerDal();default:returnnull;}}這樣就不用再顯式地去new一個對象出來了,這種寫法就是傳說中的“簡單工廠”,因?yàn)樗銐蚝唵危詴砗芏鄦栴}。首先,這種寫法是違反DRY(DonotRepeaterYourself)的,我們可以看到大量重復(fù)的代碼(returnnew…)。其次,這種寫法需要窮舉項(xiàng)目中所有需要new的類名。這是一個不小的工作量,而且很有可能會遺漏掉某些類。如果直接使用簡單工廠來代替new其實(shí)并沒有用,反而會帶來額外的工作。那么就沒有其他的辦法了嗎?在.NET的機(jī)制里,有一種技術(shù)叫反射。反射可以動態(tài)地加載DLL,并實(shí)例化類(class)。例如://只有在當(dāng)前解決方案里添加了該dll的引用后才可以使用LoadAssemblyobjDALAss=Assembly.LoadFrom(@"E:\Project\Elands.JinCard.DAL.dll");//Elands.JinCard.DAL.userDAL類的全路徑Typet=objAss.GetType("Elands.JinCard.DAL.userDAL");//動態(tài)生成類StringUtil的實(shí)例IuserDalobj=System.Activator.CreateInstance(t)asIuserDal;上面的代碼就是反射的一個基本應(yīng)用,使用反射的基本步驟如下。找到dll所在的路徑。找到class/interface的全路徑。實(shí)例化class/interface。這意味著不需要再去判斷輸入的類型,也就不需要出現(xiàn)大量重復(fù)的代碼,最后完成的工廠代碼如下:publicclassDALSimpleFactory{publicstaticObjectGetInstences(stringassemblyName,stringtypeName){returnAssembly.Load(assemblyName).CreateInstance(typeName);}}是不是看起來干凈許多了?接下來再來創(chuàng)建一個userDAL,用來說明這個工廠代碼如何使用:IuserDALuserDal=DALSimpleFactory.GetInstences("Elands.JinCard.DAL","Elands.JinOK,到這里為止工廠模式的使用就已經(jīng)講述完了。鵬輝:用工廠模式的寫法確實(shí)是沒有new了,但是也不可避免地要寫入硬編碼,比如要實(shí)例化userDAL的時候,必須寫入userDAL的路徑,并且寫入userDAL的全路徑,這樣和直接new有什么本質(zhì)的區(qū)別呢?MOL:new屬于靜態(tài)編譯,也就是在編譯網(wǎng)站的時候userDAL就會被編譯放到網(wǎng)站中。而反射生成userDAL屬于動態(tài)編譯,生成網(wǎng)站的時候不會被編譯到網(wǎng)站中,只有在使用userDAL的時候才會生成。最重要的是,工廠模式有效地切斷了BLL和DAL層的強(qiáng)關(guān)聯(lián)。那么實(shí)例化userDAL的時候,我們需要傳入userDAL所在的DLL的路徑、userDAL的全路徑,這樣看似有點(diǎn)“剪不斷,理還亂”的關(guān)系,其實(shí)不然。我們傳入的參數(shù)是字符串(string)類型的。也就是說,傳入Elands.JinCard.DAL是正確的,傳入“阿貓阿狗”也未嘗不可。重點(diǎn)是“字符串”,作為字符串,傳入的參數(shù)就可以寫在配置文件中,當(dāng)需要新增或修改的時候,直接修改配置文件就可以了,而不用重新編譯項(xiàng)目。例如,配置文件是這樣的:<appSettings><addkey="userDalRef"value="Elands.JinCard.DAL,Elands.JinCard.DAL.userDal"/><addkey="customerDalRef"value="Elands.JinCard.DAL,Elands.JinCard.DAL.custome<addkey="orderDalRef"value="Elands.JinCard.DAL,Elands.JinCard.DAL.orderDal"/</appSettings>這樣,就把每一個DAL的配置放在了web.config中。每一個add節(jié)點(diǎn)都是一個DAL配置,其中,key值表示DAL配置名稱,value表示配置值。key和value中描述的配置名稱一定要一目了然,比如上面的配置中,key="userDalRef"表示userDal這個DAL的引用(Refrence);value中是一個以逗號分隔的字符串,其中,逗號前面的部分是DAL所在的DLL的路徑,逗號后面的部分是DAL類的全路徑。例如,要實(shí)例化一個userDAL,那么實(shí)例化的代碼就是:publicvoidGetuserDal(){string[]dalCfgArr=System.Configuration.ConfigurationManager.AppSettings["IuserDALuserDal=GetInstences(dalCfgArr[0],dalCfgArr[1])asIuserDAL;//調(diào)用IuserDAL的方法userDal.Select();}這樣就完美解決了鵬輝所提到的硬編碼的問題。劉朋:這樣做確實(shí)是達(dá)到了“解耦”的目的,但調(diào)用工廠去創(chuàng)建一個實(shí)體,也就是說,怎么老感覺有點(diǎn)沒有“解”干凈的意思呢?BLL雖然不依賴于DAL了,但卻依賴了工廠。MOL:對,BLL從依賴具體的DAL,變成了依賴抽象的工廠,這是工廠模式給我們帶來的最大好處。但是DIP(DependenceInversionPrinciple,依賴倒置原則)告訴我們,高層模塊不應(yīng)該依賴低層模塊,兩者都應(yīng)該依賴于抽象。顯然,BLL依賴了工廠,而工廠又依賴了DAL,這樣互相依賴,造成了解耦不徹底。那么如何徹底地解耦呢?PS:順便提一下,new一個class的時候,.NET會先在內(nèi)存堆中開辟一塊用來存放實(shí)例的空間,然后再對實(shí)例進(jìn)行初始化。而開辟內(nèi)在堆空間的時候,這些空間并不是連續(xù)的,所以當(dāng)一個項(xiàng)目中大量地使用了new的時候,就會造成內(nèi)存堆中存在大量的碎片。這種現(xiàn)象會對真實(shí)的系統(tǒng)造成故障,而且這種故障會給排查過程帶來很大的困難。例如,一個服務(wù)器的內(nèi)存是1GB,我們把一個網(wǎng)站發(fā)布到這臺服務(wù)器上,這個網(wǎng)站在運(yùn)行一段時間后,服務(wù)器的空間內(nèi)存可能只剩余200MB,這200MB由一些小于2KB的內(nèi)存碎片組成。如果這個時候再去new一個大于2KB的對象,那么程序就會報錯。Spring.NET橫空出世酒文化發(fā)展史MOL是一個喜歡喝酒的人,MOL一直認(rèn)為酒是人類一個很偉大的發(fā)明。追溯到遙遠(yuǎn)的母系氏族社會,我們的祖先就已經(jīng)將剩余的野果儲存起來,放置一段時間后,就釀成了香甜的甘醴。我們想一下,原始的祖先們想要喝酒,他們會怎么辦?首先采一堆野果,然后放在一個瓦罐中,等幾個月,就可以喝到酒了。這是不是類似于我們寫代碼的時候直接new一個對象出來?采一堆野果(輸出構(gòu)造函數(shù)的參數(shù)),放到一個瓦罐中(分配內(nèi)存),等幾個月(CPU工作時間),就可以喝到酒了(得到實(shí)例化對象)。從“想要”到“得到”中間所有的過程都是自己親力親為。進(jìn)入了封建社會后,出現(xiàn)了很多商鋪、酒坊。如果MOL想要喝酒,就不用自己采野果釀酒了,直接到咸亨酒店,把9個大錢排開放在柜臺上,說:“溫兩碗酒,要一碟茴香豆”,自有酒保把酒端上來。喝酒的時候,還可以和店小二討論關(guān)于茴字有幾種寫法的問題。我們再來想一下,這是不是就是工廠模式的過程?MOL想喝酒了,只需要找咸亨酒店(工廠)表明自己的需求(需要實(shí)例化的對象),工廠就會把相應(yīng)的對象實(shí)例化并輸出,而MOL完全不用關(guān)心酒是怎么做出來的(實(shí)例化過程)。到了21世紀(jì),MOL喝酒也不用去咸亨酒店了,直接在網(wǎng)上訂購就可以了,MOL甚至不用關(guān)心是哪個工廠做的酒,只需要表達(dá)“我要喝酒”的強(qiáng)烈愿望就可以了。那么,我們在寫程序的時候是否也可以做到這樣?只需要說“我需要一個IuserDal對象”,然后隨著一聲驚雷,這個對象就呱呱墜地了。聽起來是不是很神奇?但這并不是天方夜譚,接下來的內(nèi)容將帶你完成這個看似神奇的過程。我不想寫SQL語句劉朋:通過前面的講解,我知道了,Spring.NET提供的“依賴注入”可以降低任意兩層(Layer)之間的耦合度。我在寫代碼的時候又遇到了新的問題,就是在寫DAL層的時候,不可避免地會寫大量的SQL語句來滿足業(yè)務(wù)上的需求,比如有時候只查詢用戶表,有時候是用戶表和訂單表關(guān)聯(lián)查詢。于是DAL層充斥著大量的“select字段fromtable”的SQL語句,這算不算是一種重復(fù)代碼呢?MOL:對,只要你可以顯而易見地看到重復(fù)性的字眼,或者在編碼的過程中大量使用了Ctrl+C/Ctrl+V,這些都屬于重復(fù)代碼,當(dāng)然也違反了DRY原則。鵬輝:SQL語句都長得差不多啊,但SQL語句又不可能再抽象成什么對象,那么如何避免這種重復(fù)代碼呢?MOL:既然SQL語句的出現(xiàn)就必然導(dǎo)致重復(fù)代碼,那么可以不讓它出現(xiàn),這樣不就一勞永逸了嗎?眾人嘩然……李沖沖:不寫SQL語句,如何獲取數(shù)據(jù)庫中的數(shù)據(jù)呢?MOL:今天我們要講的內(nèi)容,就是讓你如何不寫SQL語句也能獲取數(shù)據(jù)庫中的數(shù)據(jù)。當(dāng)……當(dāng)……當(dāng),主角出場。今天的主角是ORM(ObjectRelationMapping,實(shí)體關(guān)系映射)。小結(jié)本章中,我們主要分享了關(guān)于ORM的使用,重點(diǎn)講述了如何使用EF來搭建一個代碼結(jié)構(gòu);第4章將詳細(xì)講解數(shù)據(jù)庫的內(nèi)容。第2篇 NoSQL和測試第4章 換個數(shù)據(jù)庫試試第5章 越俎代庖搞搞測試第4章?lián)Q個數(shù)據(jù)庫試試大家懷著飽滿的熱情搭建了自己的第一個項(xiàng)目結(jié)構(gòu),并樂此不疲地在數(shù)據(jù)庫中添加表,體驗(yàn)著高科技帶來的便捷。與此同時,領(lǐng)導(dǎo)也看到了大家的開發(fā)效率明顯升高,并對大家進(jìn)行了親切的慰問,與大家進(jìn)行了親切友好的會談,領(lǐng)導(dǎo)高度贊賞了大家的工作熱情,并對大家的工作效率加以肯定,所有的員工都一致表示要在Coding的路上漸行漸遠(yuǎn)??蛻艨傆幸恍┓欠值南敕ㄋ械倪@一切場景,讓我們感覺到大家仿佛生活在美好的世界里。理想總是很豐滿,現(xiàn)實(shí)又總是很骨感。這不,領(lǐng)導(dǎo)在與客戶進(jìn)行了親切友好的會談之后,向我們傳達(dá)了一個用戶的要求。本著“用戶是上帝”的理念,大家耐著性子聽完了所謂的客戶提出來的需求。為了不以訛傳訛,我們將使用對話的形式來還原客戶的需求。領(lǐng)導(dǎo):我們的團(tuán)隊是一個年輕有活力的團(tuán)隊。客戶:是的。領(lǐng)導(dǎo):我們只用短短兩周的時間,就完成了第一版的“晉商卡”,這完全得力于您對我們公司的大力支持。客戶:(含笑不語)。領(lǐng)導(dǎo):那您對我們這第一版的晉商卡有什么意見和建議嗎?客戶:我們需要的功能基本上都已經(jīng)實(shí)現(xiàn)了。我想問一下數(shù)據(jù)庫用的是什么?領(lǐng)導(dǎo):SQLServer。客戶:我聽說有一種數(shù)據(jù)庫叫MemCache,它要比SQL的讀寫效率高很多。我們想用MemCache來替換掉SQLServer。領(lǐng)導(dǎo):這個數(shù)據(jù)庫我還真沒聽說過。我們需要開會討論一下。客戶:基本的業(yè)務(wù)邏輯都不用動,只需要把數(shù)據(jù)庫換一下,應(yīng)該不難吧。我覺得大多數(shù)程序員在聽到這里的時候,都已經(jīng)開始親切地“問候”客戶家的各位家族成員了。暫且不論MemCache的讀寫效率有多高,SQLServer的讀寫效率又如何不盡人意。單說MemCache,它本就不是用來做數(shù)據(jù)存儲的。如果用MemCache來存儲數(shù)據(jù),就好像把數(shù)據(jù)直接扔進(jìn)內(nèi)存一樣可笑。不管客戶提的需求是多么的荒誕,我們還是要分析客戶想要什么??蛻粼谔岬組emCache的時候,表示使用MemCache的理由是“它要比SQL的讀寫效率高很多”,MOL可以很負(fù)責(zé)任地給大家保證,客戶是不知道什么叫讀寫效率的,他這樣說,無非是想顯擺一下,讓大家看到他還知道“讀寫效率”這樣一個高科技詞匯。我們把客戶所謂的“比SQL的讀寫效率高很多”翻譯成大家都能聽懂的話,其實(shí)就是“我們要更高的讀取速度”。劉朋:數(shù)據(jù)庫的操作是增、刪、改、查,為啥只要更高的讀取速度呢?MOL:在我們生活的這個世界里,有一個神奇的定律適用于任何行業(yè),這就是“二八法則”。放在當(dāng)前的場景中,就是用戶做的所有操作中,有80%都是在查詢,而其他20%才是增、刪、改。也就是說,我們只要讓用戶感覺他的查詢是非??斓模脩艟蜁詾椤白x寫效率很高”。挖掘了客戶對數(shù)據(jù)庫的需求以后,MOL親自與客戶聯(lián)系,并確認(rèn)了MOL的理解是對的。然后MOL告訴客戶,MemCache是不能用來存儲數(shù)據(jù)。不過我們可以使用一個叫MongoDB的數(shù)據(jù)庫來滿足客戶獵奇的心理。客戶聽完以后,也欣然同意。那么,MongoDB是什么呢?MongoDB簡介簡單來說,MongoDB是一種key-value數(shù)據(jù)庫,它是一種非常流行的NoSQL數(shù)據(jù)庫。相信上面的這個對MongoDB的定義是一個“史上”最簡單的定義。其實(shí)MongoDB有很多特性,但我們暫時不去關(guān)心其他特性。MOL把我們需要關(guān)心的MongoDB特性摘出來,就變成了這樣一個定義。在這個定義里,我們描述了MongoDB的兩個特性:MongoDB是一種NoSQL數(shù)據(jù)庫;MongoDB是一種key-value數(shù)據(jù)庫。很多人看到NoSQL的時候,會想當(dāng)然地把它翻譯為“不是SQL”,當(dāng)然,也有很多“二把刀”的老師也會這樣教。其實(shí)它真正的意思是NotOnlySQL,不僅僅是SQL。通常情況,我們也管它叫“非關(guān)系型數(shù)據(jù)庫”。也就是說,MongoDB首先是一個數(shù)據(jù)庫,它可以用來存儲數(shù)據(jù),并且可以持久化,其次它是一個非關(guān)系數(shù)據(jù)庫。顧名思義,非關(guān)系數(shù)據(jù)庫肯定是相對于關(guān)系數(shù)據(jù)庫來講的。非關(guān)系數(shù)據(jù)庫摒棄了關(guān)系數(shù)據(jù)庫賴以成名的很多技術(shù),如主外鍵、表關(guān)聯(lián)等,因?yàn)樗鼪]有復(fù)雜的表間關(guān)系,所以MongoDB的讀取速度非常快。MongoDB是一種key-value數(shù)據(jù)庫,表示MongoDB的數(shù)據(jù)結(jié)構(gòu)方式。鵬輝:咦?key-value不就是字典嗎?MOL:沒錯,MongoDB就是用鍵值對來描述數(shù)據(jù)的一種數(shù)據(jù)庫。用字典來描述它,還不太準(zhǔn)確,如果非要拿一種近似的數(shù)據(jù)結(jié)構(gòu)來說明的話,Json是最合適的。接下來我們來看看MongoDB如何來使用。.NET操作MongoDB相信大家對MongoDB的操作語句有了一定的認(rèn)識,那么,.NET是如何操作MongoDB的呢?我們都知道,.NET操作SQLServer的時候,一定是有一個叫ADO.NET的驅(qū)動在當(dāng)做.NET和數(shù)據(jù)庫間的橋梁。那么,請大家用大拇腳指頭想一下,.NET可以直接操作MongoDB嗎?鵬輝:那肯定不行嘍,我們應(yīng)該再找一個類似于ADO.NET的橋梁來連接.NET和MongoDB。MOL:其實(shí)有很多第三方組件可以充當(dāng).NET和MongoDB之間的橋梁,接下來我們要介紹的這個橋梁mongocsharpdriver也是在.NET中運(yùn)用比較普遍的一個“橋梁”。從名稱上來看,mongocsharpdriver是提供給C#訪問MongoDB的一個驅(qū)動程序,它的下載地址是/mongodb/mongo-csharp-driver/downloads。下載后,可以得到一個壓縮包,解壓后,可以看到如圖4-12所示的一些文件。圖4-12 解壓后的文件如果大家要學(xué)習(xí)mongocsharpdriver驅(qū)動的話,解壓文件里的CSHarpDriverDos.chm這個幫助文件一定是最好的教程。很明顯,MOL不會詳細(xì)地給大家介紹這個驅(qū)動中的所有元素和用法,我們采取類比ADO.NET的方式來介紹。大家可以回想一下,我們在使用ADO.NET的時候,大體分為哪幾步?沖沖:這個我知道,第1步,定義一個數(shù)據(jù)庫連接,并打開連接;第2步,定義一個SQL語句;第3步,用基于連接的sqlCommander或基于無連接的sqlDataAdapter來執(zhí)行SQL語句;第4步,得到操作結(jié)果。如果用偽代碼表示的話,就是這樣的:using(sqlConnectioncon=newsqlConnection("連接字符串")){con.Open();Stringsql="select*fromT_CustomerInfo_TB";DataSetds=newDataSet();sqlDataAdapterada=newSqlDataAdapter(con,sql);ada.Fill(ds);}MOL:非常好,我們操作MongoDB的步驟也是類似的。首先,需要把驅(qū)動庫引入到項(xiàng)目中,添加對MongoDB.Bson.dll、MongoDB.Driver.dll的引用。第1步:定義一個數(shù)據(jù)庫連接,并打開連接。//數(shù)據(jù)庫連接字符串conststringstrconn="mongodb://:27017";//數(shù)據(jù)庫名稱conststringdbName="JinCardDB";MongoClientclient=newMongoClient(strconn);//創(chuàng)建數(shù)據(jù)庫連接MongoServerserver=client.GetServer();MongoDatabasedb=server.GetDatabase(dbName);第2步:對數(shù)據(jù)庫中的集合進(jìn)行操作。MongoCollectioncol=db.GetCollection("T_CustomerInfo_TB");//將操作結(jié)果放到內(nèi)存集合中MongoCursorcusList=col.FindAllAs<Customer>();第3步:對內(nèi)存集合進(jìn)行操作。foreach(CustomercuincusList){Console.WriteLine("name:"++"|sex:"+cu.sex);}從第1步開始看,以ADO.NET的慣性思維來看,應(yīng)該有一個連接對象(如MongoConnection)來描述一個數(shù)據(jù)庫連接。但是代碼中并沒有這樣寫。這是因?yàn)镸ongoDB己的連接機(jī)制,必須先創(chuàng)建一個客戶端MongoClient來連接MongoDB。創(chuàng)建好客戶端之后,服務(wù)端也就隨之創(chuàng)建了,所以我們只需要通過“客戶端.GetServer()”的方式就可以獲取到服務(wù)端MongoServer。然后,我們可以通過服務(wù)端來獲取到服務(wù)端的數(shù)據(jù)庫MongoDatabase。第2步是操作數(shù)據(jù)庫,上面的示例是一個查詢用戶信息的功能。可以看到,我們并沒有寫MongoDB中的SQL語句,而是直接使用“數(shù)據(jù)庫.GetCollection("集合名稱")”來獲取集合,然后使用“集合.FindAllAs<對象類名>()”的方式來獲取集合中所有的文檔。獲取到的文檔是一個MongoCursor的集合。我們可以像操作List<T>集合一樣去操作MongoCursor集合。這個操作就是第3步的內(nèi)容。在示例中,我們是把對象內(nèi)容進(jìn)行了輸出,當(dāng)然,你也可以把這些內(nèi)容進(jìn)行其他處理。相比ADO.NET來說,MongoDB的操作更偏向面向?qū)ο蟆km然這是一個新知識,但MOL認(rèn)為,它比ADO.NET的開發(fā)步驟更容易理解。我們把上面的示例進(jìn)行整理,寫一個完整的函數(shù)如下:usingSystem;usingMongoDB.Driver;usingMongoDB.Bson;namespaceElands.JinCard.NoSql.DALLayer05 {06 publicclassClass107 {08 publicvoidModelFun()09 {//步驟1:先連接到數(shù)據(jù)庫//數(shù)據(jù)庫連接字符串conststringstrconn="mongodb://:27017";//數(shù)據(jù)庫名稱conststringdbName="JinCardDB";MongoClientclient=newMongoClient(strconn);//創(chuàng)建數(shù)據(jù)庫連接MongoServerserver=client.GetServer();//JinCardDBMongoDatabasedb=server.GetDatabase(dbName);//步驟2:對集合進(jìn)行操作MongoCollectioncol=db.GetCollection("T_CustomerInfo_TB");//將操作結(jié)果放到內(nèi)存集合中MongoCursorcusList=col.FindAllAs<Customer>();//操作內(nèi)存集合foreach(CustomercuincusList)26 {27 28 }29 }30 }///<summary>///實(shí)體定義///</summary>publicclassCustomer35 {publicObjectId_id;publicstringname{get;set;}publicstringsex{set;get;}39 }40 }讓NoSQL面向?qū)ο驧OL:MOL在4.3節(jié)介紹的操作,充其量也就是ADO.NET入門的水平。那我們的目的是什么?眾:懶出新境界!MOL:非常好,那我們來想一下,在使用EF的時候是如何減少代碼的呢?劉朋:用T4模板生成重復(fù)代碼。鵬輝:將公共代碼抽象到基類里面去實(shí)現(xiàn),并且把基類做成泛型類,子類去繼承基類。沖沖:還有用接口層減少依賴,用Spring.NET去掉討厭的new。MOL:其實(shí)沖沖所說的并不是讓我們變成一個懶人的重要技術(shù),所謂懶人,就是寫最少的代碼去做最多的事情。為了實(shí)現(xiàn)這樣的目的,我們就來想一下,涉及MongoDB操作的項(xiàng)目中,有哪些地方是可以抽象的。鵬輝:我記得咱們最開始是用基本的ADO.NET來操作數(shù)據(jù)庫的,后來封裝了一個叫SqlHelper的數(shù)據(jù)庫操作類,然后把數(shù)據(jù)表進(jìn)行了實(shí)例化,最后才引入EntityFrameWork的。所以我覺得應(yīng)該先把文檔(Document)進(jìn)行實(shí)例化。但是MongoDB里面的文檔可以是任何類型的對象。我覺得除了Ojbect類型以外,實(shí)在想不出其他的類型適合文檔了。MOL:關(guān)于NoSQL的ORM,其實(shí)是業(yè)界長久以來一直有爭議的一個話題,首先ORM最先提出來的時候,只是針對關(guān)系數(shù)據(jù)庫的實(shí)體對象映射。因?yàn)殛P(guān)系數(shù)據(jù)庫里面存放的數(shù)據(jù)一定是一個規(guī)范的表(Tbale)。但是在NoSQL中,我們可以存放任何的對象進(jìn)去,可以是一個字符串,也可以是一個復(fù)雜對象。所以,我們根本無法預(yù)測從集合中取出的文檔到底是阿貓阿狗還是哈里波特。如果你非要把不確定性的對象抽象成一個確定的類(class),那你的思路已經(jīng)進(jìn)入了一個死胡同。遇到這種情況,就應(yīng)該換一個思路來想。假設(shè)我從文檔中取出的對象一定是固定的,就像從數(shù)據(jù)庫表(Table)中取得的一樣,那么我就可以把文檔(Document)映射成一個類,這樣就解決了映射的問題。為了讓這個假設(shè)是成立的,我們需要讓文檔中的數(shù)據(jù)一定是標(biāo)準(zhǔn)化的,那這些數(shù)據(jù)是從哪里來的呢?一定是我們自己插入的嘛。這樣一想,就好辦多了。我們可以限制文檔插入對象的類型,這樣就可以保證獲取到的文檔也一定是標(biāo)準(zhǔn)化的、可以實(shí)例化的。NoSQL題外話前面講過,所有的這一切都是假設(shè)我們從數(shù)據(jù)庫中取出的數(shù)據(jù)一定是規(guī)則統(tǒng)一的。理想很豐滿,現(xiàn)實(shí)很骨感。我們無法保證數(shù)據(jù)庫里面的數(shù)據(jù)就一定是規(guī)范的,假如某一天,有個程序員不小心向數(shù)據(jù)庫的用戶集合中寫了一條其他的文檔,那么意味著db.Collection.GetAll〈T_CustomerInfo_TB〉()的時候就會報錯,錯誤原因是有一條非“用戶”類型的數(shù)據(jù)無法進(jìn)行轉(zhuǎn)換。這樣的錯誤一定是致命的,因?yàn)橐坏┯羞@樣的錯誤,那么就會導(dǎo)致整個網(wǎng)站崩潰。這樣的崩潰一定是災(zāi)難性的。如何能讓MongoDB中保存的數(shù)據(jù)一定是我們所期望的數(shù)據(jù)呢?從代碼層面解決不了的話,我們可以考慮其他的辦法來實(shí)現(xiàn)“曲線救國”。我們的目的是保證從數(shù)據(jù)庫中取出的數(shù)據(jù)一定是規(guī)范的,那么如何保證呢?簡單的辦法就是確保寫入的時候就一定是規(guī)范的。劉朋:按照劇本來說,這個時候我應(yīng)該出場了。我的臺詞是“那如何保證寫入的數(shù)據(jù)是規(guī)范的呢?”MOL:大家都是寫劇本的,套路太深……如果由程序員手動去向數(shù)據(jù)庫寫數(shù)據(jù),那么無論如何也不能保證數(shù)據(jù)的規(guī)范性,即使是MOL這樣的“大?!保ㄗ缘靡鉂M狀)也不能保證。既然人做不了,那么何不把這個事情交給機(jī)器去做?也就是說,機(jī)器寫入的數(shù)據(jù)一定是規(guī)范的,那么讀出來的數(shù)據(jù)也一定是規(guī)范的。這樣一來,我們的目的就簡單了,就是保證所有人都不能手動向數(shù)據(jù)庫寫入數(shù)據(jù)。我們可以通過企業(yè)文件和規(guī)章制度來告訴大家,不能向數(shù)據(jù)庫寫入數(shù)據(jù)。這樣的做法體現(xiàn)的是以人為本的理念。但所有的制度都是人寫的,因此不可避免地,還會有人“一不小心”地向數(shù)據(jù)庫寫入數(shù)據(jù)。所以,只有規(guī)章制度還是不夠的。最重要的是可以將數(shù)據(jù)庫設(shè)置為不對外公開,這樣就沒有人可以登錄數(shù)據(jù)庫了,自然就杜絕了有人寫入的可能性。所有的這一切都是MOL在向大家傳達(dá)一種思想,這種思想就是用抽象思維去架構(gòu)程序,用面向?qū)ο蟮乃季S來編程。其實(shí)有一個第三方的插件NORM,是可以實(shí)現(xiàn)將MongoDB進(jìn)行實(shí)例化的。它的實(shí)現(xiàn)思想和前面講的方法基本一致,所以在這里MOL不再講NORM是怎么一回事。接下來的一天,我們不會講新的內(nèi)容,大家把MongoDB的架構(gòu)搭起來,并且發(fā)布一個版本到測試服務(wù)器上。鵬輝:我們寫的代碼直接發(fā)布到測試機(jī)上?要不要這么自信啊。MOL:只要架構(gòu)沒有問題,那么你出錯的機(jī)會就大大降低了?!跋嘈抛约骸边h(yuǎn)比相信代碼來得更簡單有效。眾領(lǐng)命,開始搭建自己的架構(gòu)。關(guān)于日志愉快的一天過去了,大家紛紛把自己寫的程序傳到了測試服務(wù)器上,靜靜地等著奇跡的發(fā)生。MOL就看不慣這種一潭死水的樣子,“操”起鼠標(biāo),啪啪啪,就連接到測試服務(wù)器上開始“操練”起來。不一會,三個人的頁面都出現(xiàn)了不同程序的錯誤,有的錯誤是數(shù)組越界,有的錯誤是索引越限……各種林林總總的錯誤,讓大家目瞪口呆。這個時候,MOL的成就感油然而生。MOL:每個人的程序都報錯了,大家自行檢查一下自己的代碼。眾領(lǐng)命,各自打開自己的程序調(diào)試起來。不一會,情景劇再次上演。劉朋:剛才的操作是什么樣的?我的本地代碼重現(xiàn)不出來啊。鵬輝:我的本地連開發(fā)庫是沒有問題的,為啥放到測試機(jī)上就不行了呢?MOL:如果想要重現(xiàn)剛才的問題,那么你們需要在特定的環(huán)境下(測試機(jī)環(huán)境)進(jìn)行調(diào)試代碼。當(dāng)然,這樣有很多問題,第一是不方便,第二是不安全。如果這些問題要發(fā)生在生產(chǎn)服務(wù)器上,你們還要把代碼放到生產(chǎn)服務(wù)器上進(jìn)行調(diào)試嗎?還有一種辦法,就是記錄所有的異常信息。小結(jié)本章中我們分享的是NoSQL中比較其名的MongoDB數(shù)據(jù)庫的使用,以及如何用面向?qū)ο蟮乃枷雽?NET操作MongoDB進(jìn)行抽象架構(gòu),使操作MongoDB就像操作SQLServer一樣方便,最后還簡單分享了一下如何用log4net來記錄日志。第5章 越俎代庖搞搞測試在敏捷開發(fā)的流程中,比較理想的開發(fā)狀態(tài)是一個項(xiàng)目中有很多個Sprit(沖刺),任何一個沖刺完成以后,都是可以發(fā)布版本的。劉朋:聽起來似乎很難理解,項(xiàng)目都沒做完,還能發(fā)布?MOL:這也就是敏捷開發(fā)的一種體現(xiàn)。在每個Sprit完成的時候,新加入的功能一定是可用的,不太完美的地方是還有一些功能未實(shí)現(xiàn),但是已有的功能一定可以滿足一部分用戶的需求。所以我們完全可以在每一個Sprit完成的時候發(fā)版,甚至有些公司會提倡“每日發(fā)版”的理念,也就是說每天都要求程序員開發(fā)出一個可用的版本。如此頻繁地發(fā)布版本,如何保證用戶使用的產(chǎn)品一定是一個符合要求的,并且Bug極少的版本?其實(shí)答案很簡單,只有兩個字“測試”。簡單說測試任何一個程序員都不可能保證自己寫的代碼一定是正確的,即使是編碼“牛人”也不例外。這幾乎是大家所公認(rèn)的一個準(zhǔn)則。所以必須借助外力來保證自己的代碼質(zhì)量,因此有很多互聯(lián)網(wǎng)公司或軟件公司會有專人用來做測試。他們每天的工作就是“吹毛求疵”,當(dāng)然,官方說法是保證項(xiàng)目質(zhì)量。在一個項(xiàng)目中,測試工作也會占用大量的時間,下面來看一個圖,這個圖描述了測試工作在一個項(xiàng)目中的地位,如圖5-1所示。圖5-1 項(xiàng)目流程圖5-1中描述的是一個瀑布開發(fā)項(xiàng)目中測試所處的位置。把這個流程進(jìn)行循環(huán)迭代,就變成了敏捷開發(fā)中的某個Sprit??梢钥吹剑谀硞€Sprit中,測試其實(shí)會占用將近一半的開發(fā)周期。只有經(jīng)過大量的測試,才能保證開發(fā)人員編寫的代碼盡量不出問題。測試工作對于開發(fā)人員來說,就算是外力保證。除了借助外力,還可以通過借助其他的工具或插件來提高代碼質(zhì)量,比較常見的就是“單元測試”。沖沖:我見過很多公司都沒有測試人員呀。MOL:這種公司主要分兩類,第一類公司是比較“?!钡墓荆麄兊拈_發(fā)人員可以兼任測試人員,甚至還有自己的測試工具;第二類公司就是我們經(jīng)常說的“作死型”公司。如果一個項(xiàng)目沒有經(jīng)過測試就上線了,那么這個項(xiàng)目的領(lǐng)導(dǎo)人一定會眼睜睜地看著這個項(xiàng)目一步步地走向消亡。這是多么恐怖啊!沖沖:既然測試這么重要,測試人員的工作內(nèi)容是啥呀?我覺得他們每天除了挑毛病,也不干其他事。MOL:他們的工作內(nèi)容就是保證你寫的代碼不出問題,表面上看他們是在挑毛病,而實(shí)質(zhì)上他們就是在挑毛病,因?yàn)檫@本身就是他們的工作嘛。鵬輝:那像我們這樣沒有測試人員的公司,我們的測試工作應(yīng)該如何開展呢?MOL:這個問題算是切中要害了?;谖覀兊膶?shí)際情況,我們需要做的測試主要有兩方面,第一是單元測試,第二是冒煙測試。冒煙測試冒煙測試這個專業(yè)術(shù)語,聽起來不太像是軟件行業(yè)的術(shù)語。其實(shí)冒煙測試這個詞語是來自硬件行業(yè)的。MOL在小的時候,經(jīng)常會把家里的一些家用電器拆開看看,所以家里的半導(dǎo)體收音機(jī)、鐘表、隨身聽之類的小電器都會被拆得“體無完膚”,甚至連MOL父母結(jié)婚時買的一臺17寸的電視機(jī)也被拆了個七零八落,導(dǎo)致MOL家有很長一段時間都不能看電視。為了這些事,MOL也沒少挨揍。當(dāng)然,這不是重點(diǎn)。被拆的這些家用電器,基本上都有自己的工作線路,像電視機(jī)這樣高檔的家用電器,還有集成電路板。MOL對這些電路比較感興趣,經(jīng)常會給這些電路板通電,觀察它們的反應(yīng)。有一次在給電視機(jī)的電路板通電的時候,正式終結(jié)了電視機(jī)的壽命。還記得那是一個晴空萬里的金秋十月,MOL高高興興地給電路板通電,只見一股黑煙從電路板上的一個電容中冒了出來,然后就沒有然后了。所謂“冒煙”,就是這樣來的。當(dāng)然冒煙不是因?yàn)镸OL拆了電視機(jī)而發(fā)明的,而是因?yàn)槊盁熢谠缙诘挠布y試中是非常常見的。比如一個硬件做好了,給硬件通電,看看硬件會不會冒煙,如果硬件沒有冒煙,那說明硬件已經(jīng)通過了最基本的冒煙測試。測試的內(nèi)容其實(shí)就是按照業(yè)務(wù)流程運(yùn)行程序。如果程序在運(yùn)行過程中沒有出現(xiàn)錯誤、異常,就說明冒煙測試已經(jīng)通過了?;氐轿覀兊臅x商卡項(xiàng)目。我們進(jìn)行冒煙測試的方法就是,根據(jù)需求,從最開始的登錄注冊,到后面的查找商品、下單、退款等一系列操作,都自己運(yùn)行一遍,保證沒有紅頁面(錯誤頁面)和黃頁面(警告頁面)。這樣的測試其實(shí)就是保證正流程可以順利進(jìn)行,而不會出現(xiàn)阻斷性的Bug。因?yàn)槊盁煖y試的覆蓋面不寬,所以一般都是開發(fā)人員順手就完成了。真正測試人員的測試方法,其實(shí)是黑盒測試。黑盒測試程序員寫的程序,對于測試人員來說就是一個黑盒子,盒子里面是怎么工作的,測試人員是不清楚的,而且也沒必要清楚。測試人員只需要知道送一個輸入到程序中,程序的輸出與預(yù)期是一致的這就可以了,如圖5-2所示。例如,手機(jī)是一個黑盒子,那么對手機(jī)的輸入(如發(fā)短信)一定會有一個結(jié)果(有人收到短信),你會關(guān)心短信是怎么變成電信號的,然后又通過無線電波傳送出去的嗎?當(dāng)然不會,你只需要知道手機(jī)可以發(fā)短信就可以了。如果哪一天發(fā)現(xiàn)手機(jī)不能發(fā)短信了,說明手機(jī)已經(jīng)產(chǎn)生Bug了。這就是黑盒測試的思路,確定的輸入一定會造成確定的輸出。如果輸出和預(yù)期不一致,那說明黑盒測試不通過。劉朋:說了這么多,都是在說一些概念性的東西,那么具體到我們的項(xiàng)目中,應(yīng)該如何去做呢?MOL:前面說過,冒煙測試是保證程序的正流程可以順利進(jìn)行。而黑盒測試就是針對需求中所有的場景,進(jìn)行冒煙測試,相當(dāng)于是大寫的冒煙測試。在晉商卡項(xiàng)目中有很多個功能模塊,下面抽最簡單的登錄模塊來舉例說明,登錄頁面如圖5-3所示。圖5-2 黑盒圖解圖5-3 登錄頁面劉朋:這個登錄頁面如此簡單,甚至沒有記住密碼之類的功能,這有啥可測試的呀。MOL:大家來想一下,這個登錄功能做完以后,你們是如何測試的?劉朋:這還不簡單,我輸入一個用戶名和登錄密碼,如果數(shù)據(jù)庫中有匹配的信息,那么就登錄成功,否則登錄不成功并給出提示。MOL:這就是所謂的冒煙測試。而冒煙測試明顯是覆蓋不到所有場景的。例如,不輸入賬號和登錄密碼就直接登錄,程序應(yīng)該有什么反應(yīng)?比如有些黑客在輸入賬號的時候,是這樣輸入的:;deletefromT_CustomerInfo_TB;這種情況下,數(shù)據(jù)庫中的數(shù)據(jù)是否會丟失等其他一些可能發(fā)生的場景。這些場景都需要測試人員通過寫大量的測試用例來覆蓋。劉朋:這樣呀,也就是說測試人員一定要對業(yè)務(wù)非常清楚,并且需要考慮可能發(fā)生的任何情況,是這樣吧。MOL:對的,不僅如此,測試人員還會提出產(chǎn)品測試方面的問題,比如登錄頁面背景不符合需求……劉朋:看來測試工作也不是件輕松的事啊。因此我們一定要在開發(fā)的時候保證高質(zhì)量的代碼,這樣就可以減少測試的工作量了。MOL:其實(shí)測試工作并不僅限于測試人員,對于開發(fā)人員來說,必須對代碼進(jìn)行單元測試(UnitTest)來保證代碼質(zhì)量。單元測試MOL在很多公司都見過這樣的現(xiàn)象:程序員在寫完代碼以后,自己隨便“跑”一下正流程,發(fā)現(xiàn)沒有問題,就直接提交給測試。接下來的測試過程就比較戲劇性了。由于程序員的冒煙測試是不可能覆蓋所有場景的,所以測試同事在測試的過程中就會發(fā)現(xiàn)很多問題。測試回退版本以后,開發(fā)人員開始修改Bug,修改完以后再提交給測試,這樣反復(fù)幾輪以后,功能模塊總算是可以正常運(yùn)行了。據(jù)官方調(diào)查數(shù)據(jù)表明,開發(fā)的時候發(fā)現(xiàn)Bug并修復(fù),這個成本遠(yuǎn)小于測試人員發(fā)現(xiàn)Bug并反饋給開發(fā)人員帶來的成本。因?yàn)橐坏┨峤粶y試,這個項(xiàng)目就變成了兩個部門(和測試部門)反饋Bug的時候,開發(fā)人員已經(jīng)忘記了相應(yīng)的業(yè)務(wù)邏輯,于是他們再去看需求,再去溝通,這樣就造成了時間成本和人力成本的浪費(fèi)。為了減少這種測試回退的現(xiàn)象,開發(fā)人員一般在提交代碼以前,需要對自己的代碼進(jìn)行“單元測試”,但是為什么國內(nèi)很多程序員都不愿意寫單元測試,甚至有些抵觸呢?分析一下,其實(shí)很簡單。許多程序員不知道什么是單元測試,更談不上寫單元測試了;有些知道單元測試的程序員懶得寫,因?yàn)檫@會占用大量的編碼時間;很多項(xiàng)目都不會要求單元測試,所以大家對這個也就不太重視;有些業(yè)務(wù)邏輯比較簡單,程序員就非常相信自己不會出錯;過度相信測試人員,把查找Bug的任務(wù)統(tǒng)統(tǒng)交給測試人員去做;項(xiàng)目進(jìn)度緊張,沒有時間寫單元測試(這個理由似乎是最合理的)。白盒測試前面我們講過黑盒測試,現(xiàn)在再來回顧一下。黑盒測試是這樣的一種測試:一個功能模塊對于測試人員來說,就是一個黑盒子,盒子里面是怎么運(yùn)行的,并不是測試人員關(guān)心的問題,測試人員只需要關(guān)心確定的輸入進(jìn)入黑盒,從黑盒輸出的結(jié)果一定是符合預(yù)期的。黑盒測試一定是最貼近用戶使用的,因?yàn)橛脩粢膊魂P(guān)心黑盒的運(yùn)行狀態(tài),只關(guān)心輸出。那么這種測試就會有遺漏。舉個簡單的例子,有下面幾行代碼://判斷用戶所處的國家,并給出輸出Switch(CountryType){Case"中國":Return"我是中國人";Case"日本":Return"私は日本人です";Case"英國":Return"I’mEnglish";Default:Return"不要問我從哪里來";}業(yè)務(wù)場景:這個項(xiàng)目是針對中國用戶來開發(fā)的。開發(fā)人員對這段代碼一定是信心滿滿,因?yàn)殚_發(fā)人員不僅處理了需求描述的中國用戶,甚至還處理了自己能夠想到的日本人和英國人。黑盒測試人員一定只會輸入“中國”來進(jìn)行測試,因?yàn)樾枨罄镏幻枋隽酥袊?,所以程序里的日本分支和英國分支就不會被測試到。這樣就造成了黑盒測試的覆蓋率不全。其實(shí)這樣的現(xiàn)象在黑盒測試中是很常見的,因?yàn)闇y試人員不可能去問開發(fā)人員這個功能模塊里的具體代碼。而白盒測試就不一樣了。白盒測試人員一定會提前和開發(fā)人員進(jìn)行溝通,了解模塊內(nèi)部的流程,這樣寫出來的測試用例就一定會覆蓋所有的程序分支。白盒測試人員一定會分別輸入“中國”“日本”“英國”,甚至可能還會輸入“火星人”這樣連程序員都無法預(yù)知的文字。當(dāng)然,白盒測試人員和程序員的溝通成本一定會很大,因?yàn)殚_發(fā)人員要把自己寫的代碼詳細(xì)地講給測試人員,而且測試人員一定要能明確理解開發(fā)人員的意思。很多開發(fā)人員都覺得溝通成本比自己的開發(fā)時間多出很多,所以在大多數(shù)公司中,白盒測試一般由開發(fā)人員自己來完成。其實(shí)單元測試和白盒測試在很大一部分的用例上是重合的,所以只要認(rèn)真完成單元測試,然后稍微做一些加工,那么白盒測試將是手到擒來。壓力測試大家來想一下,我們的晉商卡項(xiàng)目要面對的是整個山西省的老鄉(xiāng),大約有3500萬人,按1%的比例來算,晉商卡的實(shí)際用戶大約有35萬人左右,這個用戶群體是非常龐大的。我們來設(shè)想這樣一種場景。雙十二(12月12日)的時候晉商卡搞活動,活動內(nèi)容是交城駿棗1塊錢1斤,那么,在12月12日凌晨的時候,必然會有大量的用戶進(jìn)行下單操作。假設(shè)按5%的比例來算,將會有近2萬人在凌晨0:00時同時下單,那么我們的服務(wù)器是否能夠抗得住這么大的并發(fā)?這就是壓力測試的范疇。所謂壓力測試,就是制造某種特殊場景來對服務(wù)器產(chǎn)生壓力,觀察程序在壓力環(huán)境下的運(yùn)行狀態(tài)。當(dāng)然,壓力測試并不完全只與代碼有關(guān),還需要服務(wù)器集群、負(fù)載均衡、CDN等硬件支持。壓力測試暴露出問題以后,程序員需要思考的是應(yīng)該如何改良現(xiàn)有的編碼,讓程序可以承受更大的壓力。比如使用緩存、減少數(shù)據(jù)庫交互次數(shù)、用存儲過程替換頻繁的寫操作等。關(guān)于緩存的設(shè)計,在后面的章節(jié)中的會詳細(xì)講述。其他測試除了前面提到的冒煙測試、單元測試、黑盒測試、白盒測試和壓力測試之外,還有很多測試也發(fā)揮了很重要的作用。例如:讓項(xiàng)目在服務(wù)器上不間斷地運(yùn)行很長一段時間(如兩周),的日志,這是疲勞測試。在請求的URL中加入XSS擊,這是安全測試。此外,還有其他的一些測試方法,但在我們的晉商卡項(xiàng)目中并不會出現(xiàn),大家可自行研究一下,這里就不細(xì)講了。小結(jié)試是開發(fā)人員一定要做的一項(xiàng)工作,因?yàn)閱卧獪y試可以在很大程序上保證項(xiàng)目的正常運(yùn)能將這些測試方法應(yīng)用到實(shí)際的項(xiàng)目中。最后還是要強(qiáng)調(diào),測試的意義不是保證項(xiàng)目沒有Bug,而是讓我們對項(xiàng)目更有信心!第3篇 高精尖技術(shù)第6章 神奇的緩存第7章 程序員眼中的前端第8章 人生中的第一次高并發(fā)第9章 微信公眾號第6章 神奇的緩存經(jīng)過前面幾章的學(xué)習(xí),我們的晉商卡很順利地上線了。大家都其樂融融地坐在辦公室喝茶聊天。有些讀者該埋怨MOL了,沒有認(rèn)真地講一個頁面,這樣就把我們糊弄過去了?MOL在這里要聲明一下,本書的目的不是教大家如何做一個網(wǎng)站,我們的目的是以晉商卡這個項(xiàng)目為引子,給大家分享一些項(xiàng)目進(jìn)展的時候遇到的問題以及解決問題的方法。所以我們不會去仔細(xì)地講一個控制器怎么寫,相信大家作為忠實(shí)的摩絲,這些細(xì)枝末節(jié)的事情是可以自己搞定的。劉朋:(白眼)懶就直說唄,還找個這么冠冕堂皇的借口。MOL:(汗)又被你看出來了。鵬輝:不過,話說咱們自己寫MVC代碼的時候,基本上也沒遇到什么麻煩,我覺得應(yīng)該歸功于這個優(yōu)秀的代碼結(jié)構(gòu)。沖沖:是的,我的體會最深了。我們沒有架構(gòu)的時候,可能會把大量的業(yè)務(wù)代碼都寫到控制器里,而且會在項(xiàng)目中出現(xiàn)大量重復(fù)的代碼,容易出錯不說,還不容易維護(hù)。MOL:那還不喊出我們的口號?眾:懶人無敵!網(wǎng)站崩潰了大家正在其樂融融地享受這插科打諢的時間,客戶不合時宜地打來電話。客戶:最近兩天咱們的項(xiàng)目運(yùn)行一直比較正常,但是今天上午10應(yīng)了。趕緊看看是什么原因吧。5分鐘內(nèi)解決問題。收到用戶的報障以后,MOL立刻遠(yuǎn)程連接到生產(chǎn)服務(wù)器,在任務(wù)管理器中,MOL看到CPU使用已經(jīng)達(dá)到100%,那不卡才怪。于是二話不說,放大招——直接重啟。果然,在重啟服務(wù)器以后,網(wǎng)站馬上又恢復(fù)了正常。這個時候我們要馬上分析原因,并在第一時間給出解決方案。于是,MOL命令大家分析服務(wù)器上的日志。鵬輝:上午10沖沖:上午10點(diǎn)的時候,登錄用戶非常多,大約是平常的10倍左右。劉朋:難道是用戶太多,服務(wù)器“伺候”不過來了?MOL:“伺候”這個詞用得非常貼切。因?yàn)橛脩粼谡埱蠓?wù)器的時候,服務(wù)器其實(shí)就是在伺候用戶,查詢數(shù)據(jù),處理數(shù)據(jù),最后返回給用戶。根據(jù)我們檢查日志的結(jié)果發(fā)現(xiàn),上午10點(diǎn)的時候,有大量的用戶在進(jìn)行查詢操作,因?yàn)榭蛻粼?0點(diǎn)鐘的時候有一大批新品上架,大量用戶在好奇心的驅(qū)使下在查看這些新品。這樣大量的操作,導(dǎo)致程序頻繁地訪問數(shù)據(jù)庫,接下來就導(dǎo)致對數(shù)據(jù)庫的查詢操作占用了大量的CPU,所以服務(wù)器操作系統(tǒng)就無法正常運(yùn)行。問題的原因找到了,由此也可以肯定,重啟服務(wù)器絕對不是一個長久之計。因?yàn)槲覀儫o法預(yù)知客戶會在什么時間點(diǎn)再上架新的商品。有可能是明年,當(dāng)然也有可能是下一分鐘。為了避免類似的情況再次出現(xiàn),必須有一個對策來處理這種突發(fā)場景。從架構(gòu)層面來看,處理類似問題基本上就是兩個方向,硬件方向和軟件方向。硬件方向的解決方法其實(shí)是最簡單的。服務(wù)器不是“抗不住”這么多數(shù)據(jù)庫查詢嗎,那么可以多加幾臺服務(wù)器,直到可以滿足這種查詢?yōu)橹埂_@樣的解決辦法簡單、粗暴,但是有投入更多的錢來滿足用戶的需求。話分兩頭,我們不僅要看到從硬件角度解決問題的便捷,也要看到這種解決方式的局限性。例如現(xiàn)在是1萬個用戶同時查詢,因此導(dǎo)致服務(wù)器崩潰,增加21萬個用戶同時查詢了。第二天,有2萬個用戶同時查詢了,企業(yè)客戶就需要再增加2臺服務(wù)器,才能滿足這樣的要求。隨著企業(yè)客戶的業(yè)務(wù)發(fā)展,用戶量也會每天增加,總有一該換個角度來思考問題。如果我們減少對數(shù)據(jù)庫的查詢,那是不是也可以解決CPU占用過高的問題呢?劉朋:用戶要查看新品信息,能不讓用戶看嗎?還是說只讓一部分用戶看,另一部分用戶就干著急?這樣也不合適啊。MOL:我們的目的是通過減少對數(shù)據(jù)庫的查詢來減少CPU占用,但前提一定是要保證用戶可以正常使用網(wǎng)站。我們都知道,所有網(wǎng)站上展示的動態(tài)數(shù)據(jù),一定是來源于數(shù)據(jù)庫的,當(dāng)大量用戶在訪問頁面的時候,就會對數(shù)據(jù)庫造成壓力。那么是否可以把對數(shù)據(jù)庫的查詢結(jié)果放到一個地方(如硬盤),這樣當(dāng)有下一個用戶再訪問頁面的時候,就不需要再去查詢數(shù)據(jù)庫了,直接從硬盤上讀取就可以了。這樣的想法一定是對的,而且也是MOL所推薦的,因?yàn)檫@就是緩存的思想。緩存是什么還記得計算機(jī)界的一句名言嗎:計算機(jī)科學(xué)領(lǐng)域的任何問題,都可以通過增加一個間接的中間層來解決。Anyproblemincomputersciencecanbesolvedbyantherlayerofindirection.緩存就是新增加了一個中間層,通過這個中間層,可以最少次數(shù)地訪問數(shù)據(jù)庫來獲取數(shù)據(jù)。話說,在我們國家剛解放的時候,大家的生產(chǎn)物資普遍都比較缺乏,作為農(nóng)民,有一件趁手

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論