日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Duke的咆哮语录②:我求求你们跟我学一下代码“分层”吧!

發(fā)布時間:2023/12/16 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Duke的咆哮语录②:我求求你们跟我学一下代码“分层”吧! 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

目錄

  • 序言
  • 垃圾代碼
  • 外賣系統(tǒng)和分層
    • 控制器層
    • 業(yè)務邏輯層
    • 數(shù)據(jù)訪問層
    • 通用數(shù)據(jù)模型層
  • Duke的咆哮

注:關于代碼的分層,事實上還沒有金科玉律的標準。所以本文所述的分層理念,僅僅是從筆者個人的開發(fā)生涯的理解上給出的。僅供參考。如果讀者有任何意見和想法,歡迎和我交流討論!

序言

在很久很久以前,昔々の時代,long long ago,那時候的js還沒有大放異彩,那時候的前端還沒有專門的工程師,甚至,我們的Java代碼還會直接寫在前端頁面里。而所謂的Ajax、局部刷新,在當時也是不折不扣的“高端技巧”。在那時瀏覽器跟服務器的交互,除了文檔(jsp、html等)、靜態(tài)資源(css、圖片等)外基本上就沒別的了。用戶每次的請求,或許是超鏈接跳轉(zhuǎn),或許是表單提交,都會導致頁面重刷。于是jsp的數(shù)量是非常非常多的,隨便一個簡單模塊的增刪改查就得有列表頁、新增頁、詳情頁等。所以有些年齡的Java程序員可能見過這種文件結構:

開發(fā)者在頁面上又要書寫Java邏輯訪問數(shù)據(jù)庫、加工數(shù)據(jù),又要照顧頁面本身。所以寫出來的頁面代碼,很可能會有些奇怪:

在剛上大學的時候,我甚至搞不清JS和JSP的區(qū)別。后來分別學習過兩者之后,我才知道兩者是八竿子打不到一塊的。可是,直到看到這種代碼,我才明白,原來JS和JSP也是可以水乳交融的,當年的我,果然還是太膚淺了……
……
……
……
個屁啊!

這種代碼是給生產(chǎn)項目程序員看的嗎?很容易人格分裂好嘛!

如果您看了這種代碼表示very good甚至有“極客風范”,那也許我們不在同一個頻道吧。

如大家所見,直接在jsp插入Java代碼的方式,或者在Servlet中打印HTML字符串的方式,是不折不扣的“反人類”設計。其負面作用遠不止代碼耦合無法復用、視圖和邏輯混亂不堪……所以從Java Web推出之始,所有人都在做的一件事就是——減負。

何為“減負”?軟件工程里有個詞叫“單一職責原則”,一個類只應該扮演一個角色。雖然你可以狡辯說jsp確實只扮演了“渲染視圖輸出”的角色,但事務的認知是分粒度的。過去的jsp/servlet解決方案,細粒度去看,不論是數(shù)據(jù)庫的訪問,還是事務的管理、還是數(shù)據(jù)的加工處理、還是身份驗證等,都塞在同一塊代碼里,這是不僅不夠面向?qū)ο?#xff0c;甚至還能讓人看到面向過程的影子。想要邏輯清晰、代碼可控,我們就要抽象代碼成類,并給類賦予單一職責,將與頁面視圖渲染本身無關的部分從它身上剝離掉!

沿著這個話題下去,我們可以聊一聊所謂的MVC Web框架的實現(xiàn)思路。但跟本次我要咆哮的主題無關所以暫時打住話頭。我們還是聊軟件工程相關的。

垃圾代碼

上文提到了“單一職責原則”,除了它,你可能還聽說過“開閉原則”、“依賴倒轉(zhuǎn)原則”、“高內(nèi)聚低耦合”等名詞。前人探索和思考散發(fā)出的智慧是我們應該充滿感激并辯證吸收的,而不是掛在嘴邊忘在心里實踐背離。

是時候了,上代碼:

從命名上來看,該代碼裝模作樣地依次實現(xiàn)了“Controller”層、“Service”層以及“Dao”層。

為什么我說是“裝模作樣”呢?

因為這分層完全就是沒有參透代碼“分層”的目的。

“分層”是對邏輯的一種切分,也是一種抽象。 切分可以降低代碼的耦合,抽象可以提高代碼的復用。可以說這是面向?qū)ο蟮墓こ袒浖_發(fā)的無上法寶——雖然不能說是銀彈吧。

按照業(yè)務的復雜度和項目細分的粒度,一般情況下我們會把Java web項目分為如下幾層:

  • 控制器層
  • 業(yè)務邏輯層
  • 數(shù)據(jù)訪問層
  • 通用數(shù)據(jù)模型層

到這里,一般的老師或者課本就會照本宣科,講什么“控制器層分發(fā)請求,業(yè)務邏輯層處理業(yè)務,數(shù)據(jù)訪問層和數(shù)據(jù)庫打交道”之類的話了。我相信初學web開發(fā)的同學很少有人能參透這些分工的目的和意義,反而云山霧繞不知所以。所以我打算換個角度跟你講,這樣分層的道理。

外賣系統(tǒng)和分層

我比較喜歡用類比去描述事物,這樣讀者可能看起來更有畫面感。要類比的話,把Java web項目比作訂外賣還是有些相似的。

控制器層

訂外賣的話,控制器,其實就是派單系統(tǒng)。我們通過APP訂了餐,派單系統(tǒng)要把單子遞給商家,然后商家把做好的餐送給我們。派單系統(tǒng)的主要職責是什么呢?我們可以列舉一下:

  • 接收客戶訂單;
  • 驗證客戶的訂單是否正確,是不是忘了填食品名,是不是在全聚德點了“東京烤雞”等。如果填錯了就要告訴客戶填錯了;
  • 如果客戶訂單填寫正常,就要把單子派給商戶,并連帶訂單中的各種要求一并告知商家;
  • 如果商家表示我們打烊了,或者客戶點的天鵝肉昨天被癩蛤蟆偷吃了,或者大廚的寬油熱鍋燒著眉毛做不了了,這些信息會及時告訴派單系統(tǒng)這頓飯做不了了;
  • 如果商家的菜新鮮出爐,派單系統(tǒng)要負責告訴配送員妥當將食品送到客戶手里;

這就是控制器的職責。驗證用戶輸入,分發(fā)給對應的邏輯處理器,妥善處理邏輯錯誤,及時返回數(shù)據(jù)給用戶。對于控制器來說,它關心的是:用戶傳遞了什么,誰來處理它,有沒有發(fā)生錯誤,怎么返回數(shù)據(jù)。至于具體的處理,也就是商家大廚具體怎么做菜,則不是控制器所關心的。你見過美團外賣他家接了單后自己就咣咣炒菜給你送上來的嗎?這也就是為什么說不要讓Controller層搶活,導致service層無事可做的原因。一個外賣平臺不要跟大廚搶活干好嘛!此外就是《咆哮①》講到的,控制器層應該是一切異常的終點。什么意思呢?你要是用美團點餐的時候,商戶打烊了APP不通知你而是咔一下閃退了,你難道不窩火嗎?是一個道理。

業(yè)務邏輯層

接下來我們聊業(yè)務邏輯層。特別地,我們聊Spring下的業(yè)務邏輯層。業(yè)務邏輯即用戶點外賣環(huán)節(jié)的商家。對于簡單的系統(tǒng),業(yè)務邏輯只需要一層就夠了。但更復雜的業(yè)務也許會再講業(yè)務邏輯分為復雜業(yè)務和通用業(yè)務等子層。我習慣性將復雜業(yè)務稱為“service”,通用業(yè)務稱為“biz”。這就好比是面館大廚師傅手下有若干個和面的、拉面的或煮湯的用于處理中間環(huán)節(jié)食材的助理師傅。和面的師傅和拉面的師傅就是通用業(yè)務層,而炒面師傅煮面師傅則是復雜業(yè)務層。總的來說,商家大廚的職責是:

  • 接收無誤的訂單信息,加工食材做飯,并將做好的飯妥投給派單系統(tǒng)指派的送餐師傅;
  • 大廚拿食材做飯就好,他不應該親自買菜。做硬菜的大師傅也不需要親自給蘿卜雕花,那是手下助理師傅的活;
  • 如果下一層發(fā)生問題,如面用完了(食材問題)或和面師傅傷者手了(中間環(huán)節(jié)出現(xiàn)問題),要及時報告派單系統(tǒng)這頓飯做不了了,以及做不了了的原因。而且要及時打掃現(xiàn)場,比如購置足量面粉、及時送師傅就醫(yī)等,保證不會影響下一單生意。

如果把上述描述轉(zhuǎn)換成業(yè)務層的職責,那就是業(yè)務層應該接收已經(jīng)通過控制器層校驗的數(shù)據(jù)。比如,對于經(jīng)過對稱加密的報文,其解密工作和校驗工作就應該在Controller層完成,將無誤的明文信息傳入service層,而不是在service層執(zhí)行類似的操作。

service層應該只將業(yè)務處理結果返回,而不應代替Controller層直接返回json或xml報文。 除了插入業(yè)務,我們可能希望service返回id給Controller,其余只要不涉及多狀態(tài)的業(yè)務,也就是要么調(diào)用成功要么調(diào)用失敗的業(yè)務,將這種service方法的返回值設為void也未嘗不可。有些同學會問那發(fā)生業(yè)務異常怎么辦?這就是Java異常機制的作用了。你可以定義一個ServiceException,將錯誤信息填在message字段提供給Controller層使用就可以。不要忘了我們在Controller層是一定一定要try-catch住業(yè)務代碼以避免異常逃逸到Controller層的外部去的。當然對于某些一定要返回業(yè)務處理狀態(tài)的方法,一定不要簡單地用int整數(shù)返回0123去描述接口狀態(tài)。兩個方案,要么使用String類型的返回值返回字符串常量,字面值即是返回的狀態(tài),要么使用枚舉,以枚舉項的名稱作為狀態(tài)的解釋。直接使用數(shù)字作為service返回值的,我真的想用我的阿姆斯特朗回旋加速阿姆斯特朗炮把你的腦漿炸出來。即使你說你在方法上加過了0123具體意義的詳細的注釋我也不會放過你。因為我在Controller層斷點調(diào)試時看到一個service返回了個整數(shù)的時候,我整個人就是懵逼的。還有就是,如果業(yè)務發(fā)生了異常,不論是不是被你catch到了,一定要throw新的異常,特別地應該是用自定義的運行時異常ServiceException出去。不然的話根據(jù)Spring默認的事務管理機制,沒有拋出異常事務就不會回滾,很有可能數(shù)據(jù)庫中就會混入不正確的值了!

那么還有就是service層應該專注于處理數(shù)據(jù),而不是和數(shù)據(jù)庫打交道。換句話說,不應該在service層直接操作數(shù)據(jù)庫。狹義的說,就是不要在service層寫SQL。那是數(shù)據(jù)訪問層的任務,service層不應該搶活。但是對于一些特殊業(yè)務,特別是跨越多個數(shù)據(jù)源的業(yè)務,因為dao層原則上都只與自己責任內(nèi)的數(shù)據(jù)源打交道,這時用一個通用業(yè)務層的實例去處理一些邊界狀態(tài),可能就是不可避免的了。

還有就是,service層本身也應該對自己的行為負責。對于下一級可能會發(fā)生的問題,service層也應該予以捕獲和處理。換句話說,將業(yè)務代碼中所有的異常捕獲并統(tǒng)一轉(zhuǎn)化為ServiceException是一種良好的實踐,甚至極端一些,service層的代碼只應該拋出ServiceException或此類自定義異常。Controller層關心的只是業(yè)務的完成狀態(tài),而不應該讓它直接了解到是SQL出了問題,還是文件系統(tǒng)出了問題。在Controller看來,這些都是業(yè)務發(fā)生了問題。一股腦把各類第三方庫的、Java SDK本身的亂七八糟的異常都甩手給Controller層,也是非常不妥的。

既然提到了異常,那就多嘴幾句。不論是Eclipse還是IDEA,都有一個非常SB的設計,那就是在程序員調(diào)用了聲明異常拋出的方法后,會標紅報錯,但程序員用自動修復時,默認的修復選項卻是將該異常聲明提升至本方法的聲明里,而不是用try-catch包裹并處理。那些技術弟弟程序員,看到標紅代碼自然而然地就依賴IDE的自動修復功能,把一大堆一大堆的異常聲明傳染得到處都是。比如Controller上的throws Exception,這還算好的,我還見過方法體聲明了一大堆找不到文件的、找不到方法的、編碼異常的等五花八門的弟弟代碼。Java的強制處理聲明式異常是一個弊大于利的設計,希望各位弟弟早日學會try-catch。自然不用說的是,自定義的DaoException或ServiceException,一定要繼承自RuntimeException,而不是Exception。至于弄不清楚兩者有什么區(qū)別的弟弟,哥哥只能勸你善良……

數(shù)據(jù)訪問層

終于講到了初學者可能最直觀可感的部分,數(shù)據(jù)訪問層,data access layer,其對象一般稱為DAO。層如其名,本層的關注點就是“數(shù)據(jù)”。這種數(shù)據(jù),99%的場合便是來自數(shù)據(jù)庫。所以武斷地講DAO層就是直接和數(shù)據(jù)庫進行交互的層也不算錯。在外賣系統(tǒng)中,DAO層就像是倉庫管理員,管理各類新鮮食材。大廚要什么,就告訴倉庫管理員取對應的東西來。而外賣派單系統(tǒng)只關心菜做得如何,對于菜是哪兒種的,在哪兒買的,則完全不關心——只要菜的味道是好的品質(zhì)是好的那就夠了。更極端一些,即使不要倉庫管理員,全聚德的大廚直接去便宜坊訂一只鴨子交給外賣系統(tǒng),從理論上也是沒問題的。所以對于一些分布式系統(tǒng)來說,甚至可以沒有DAO層。

從我給出的不太準確的解釋中,或多或少可以看出DAO層和Service層的區(qū)別,DAO層關心的是數(shù)據(jù)庫,而Service層關心的是數(shù)據(jù)的獲取、加工、處理。控制器層是不直接依賴數(shù)據(jù)訪問層的,數(shù)據(jù)訪問層本身也不應該指代具體的業(yè)務邏輯。直接依賴于數(shù)據(jù)訪問層的,只應該是業(yè)務邏輯層,但業(yè)務邏輯層也不僅僅只依賴數(shù)據(jù)訪問層才能獲取數(shù)據(jù)。分布式系統(tǒng)里Service層的數(shù)據(jù)可以通過web接口獲取,而不是查庫,當然有時這種獲取數(shù)據(jù)的對象也會被叫做DAO。

說起來貌似清晰直截,但實際上因為概念和實踐上的一些問題,初學者還是很容易將兩者的分工搞混的。其源頭就是SQL本身具有一定的業(yè)務描述能力,而大部分業(yè)務,不論增刪改查,都不可避免地要與數(shù)據(jù)庫進行交互。所以從潛意識里,“SQL = 業(yè)務”的認知就建立起來了。以插入為例,盡管從分層的角度,該業(yè)務的實現(xiàn)應該是位于service層的save()方法,但本質(zhì)上起業(yè)務作用的,卻是在DAO層執(zhí)行的insert into xxx ...SQL語句。所以究竟是DAO層承擔了業(yè)務功能,還是Service層在承擔業(yè)務功能呢?

看問題要看全局。

誠然,上例是SQL的調(diào)用完成了“插入業(yè)務”,但如果是更復雜的例子呢?例如分別取出銷量前三的筆記本電腦、手機、顯示器的品牌,將其去重取并集后按字典倒序列舉。這種例子,還能簡單地通過一條SQL語句來實現(xiàn)嗎?有些“SQL=業(yè)務”主義教徒可能真的會寫出這種SQL然后說“完全沒問題”,但隨著業(yè)務的描述越來越復雜,對應的純SQL語句也會趨向于人力理解不能。這時候,更好的設計應該是設計一個“獲取某分類商品前三品牌列表”的DAO層方法,在sevice層調(diào)用3次,之后用Java集合操作去去重排序,合并成一個List返回給Controller層。從這個例子中,您就應該可以感受到,DAO方法本身并不是業(yè)務,但service方法則是。

我個人的解釋是,不論是極簡業(yè)務還是復雜業(yè)務,業(yè)務的描述都是service層的代碼。 對于極簡業(yè)務來說,例如不和其他模塊有耦合的簡單增刪改查業(yè)務,確實是全部依賴DAO去實現(xiàn)的業(yè)務;但大部分業(yè)務,其實現(xiàn)邏輯還是應該來自service層,DAO層中的代碼,是被service層業(yè)務代碼所依賴的一些關鍵代碼。DAO層的代碼應是普世的,如果有可能的話,可以被盡可能多的service層代碼復用的。盡管插入操作的業(yè)務代碼只依賴一行DAO層方法的調(diào)用,但它仍然是業(yè)務代碼,DAO層代碼則是實現(xiàn)該業(yè)務所依賴的部分。

通用數(shù)據(jù)模型層

盡管上文貼出的DAO層代碼,是直接通過JDBC操作的數(shù)據(jù)庫,但實際上大部分生產(chǎn)環(huán)境項目,還是會依賴某種ORM框架去實現(xiàn)DAO層代碼。ORM框架會將關系數(shù)據(jù)庫的表間關系映射為Java的對象間關系,這也使得所謂的“通用數(shù)據(jù)模型”并不像傳統(tǒng)的pojo那么純粹。特別是建立了雙向綁定的對象,或包含用戶登錄密碼這種敏感字段的數(shù)據(jù),是不適合直接作為最終結果讓Controller返回給用戶的。前者在JSON序列化時會發(fā)生循環(huán)引用導致棧溢出,后者則可能會導致敏感數(shù)據(jù)被抓包者利用。為了解決這類問題,最好的做法就是構建DTO類。DTO全稱為“Data Transformation Object”,即數(shù)據(jù)傳輸對象。那么,DTO對象的構建時機應該是什么時候呢?是DAO層還是Service層呢?

稍加思索,你就會發(fā)現(xiàn),實際上所謂的“數(shù)據(jù)傳輸對象”本身所描述的,就是“業(yè)務”,是對數(shù)據(jù)集合進行投影操作的業(yè)務。

但生活中就是充滿各種變數(shù),所以我們也要有不少變通,不是嗎?

如果不考慮數(shù)據(jù)庫的查詢效率,DAO層直接獲取某對象映射表的所有數(shù)據(jù),組裝成對象集合提供給Service層,Service層再挑挑揀揀拼裝出業(yè)務所需的對象返回給Controller層,一切都是那么優(yōu)雅完美。

可問題就在于“查詢效率”是不可忽視的。特別是對擁有動輒數(shù)十數(shù)百字段的大庫表來說,獲取太多本不需要的數(shù)據(jù)肯定會影響效率,而且先全拉取再挑挑揀揀的邏輯在對象的數(shù)量級膨脹后,也會造成很大的內(nèi)存壓力和GC壓力。所以不可避免地,需要從SQL的構建開始限制獲取字段的數(shù)量。這一步,就我個人的水平和能力所限,沒有太好的解決方案。在這里總結幾種方法吧:

  • 讓DAO層特供返回DTO的方法。
    這是最容易想到的方案,直接在DAO中拼裝和返回需要的DTO。但這終究還是讓DAO層承擔了一部分Service層的功能,會使DTO層下潛為DAO層的依賴。
  • 在Service中添加投影方案描述。
    這種方法依然讓DAO層返回Entity類對象,但通過投影描述,使得DAO層獲取數(shù)據(jù)時只會查詢需要的字段。這也意味著實體類的其他字段會置空。這種方式的實現(xiàn)方式較為繁瑣,除非ORM框架本身提供相關支持,但Service層理論上又不能直接接觸ORM框架的API,所以也會比較蹩腳。此外,如果使用了@NotNull之類的驗證方案,也會因為置空產(chǎn)生驗證通不過的問題。
  • 引入Biz層。
    我們定義DAO處理的是純粹的數(shù)據(jù)庫交互層,那與之相對的,我們也可以引入一種“不純粹”的數(shù)據(jù)庫交互層。我將其命名為Biz層,“biz”即“business”,也是指“業(yè)務”。只不過這種業(yè)務是專供復雜業(yè)務使用的低級業(yè)務。開發(fā)者可以把一部分定位模糊的業(yè)務(特別是多數(shù)據(jù)源的或跨多表的,單純DAO不好描述的業(yè)務)下沉到該層里。這一層可依賴DAO層也可以不依賴DAO層,自身也可以直接交互數(shù)據(jù)庫,但不具備事務管理的能力,且直接被Service層依賴。Biz層的引入是對Service/DAO分層先天貧血的一種補充和妥協(xié)。盡管它可能很好用,但也破壞了分層的初心,重新耦合了業(yè)務和數(shù)據(jù)訪問,增加了復雜度。故不宜過多使用。
  • 拆庫。
    根據(jù)單一職責原則,數(shù)據(jù)表本身要描述的信息,在設計時也應該是帶有強烈的目的性的。這也意味著,如果某個表的字段越多,就越意味著該數(shù)據(jù)表可能承擔了過多職責。例如產(chǎn)品表就不應該包含該產(chǎn)品生產(chǎn)廠家的老板的姓名。與其考慮如何縮減查詢的字段,不如一開始就把表設計得足夠精煉,這樣即使查詢的時候帶上了三五個無用字段,一來不會產(chǎn)生太過分的性能浪費,二來開發(fā)者理解數(shù)據(jù)結構也會更容易,三來開發(fā)效率也會得到大大提高。合理的表結構設計還可以降低數(shù)據(jù)庫的存儲壓力,何樂而不為呢?哦對,那些外包公司給政府單位干low逼百年祖?zhèn)鞅斫Y構的同行,不好意思,這條不適合您們,要怪,就怪你們公司拉不來優(yōu)質(zhì)項目吧。哦對,這種公司的代碼質(zhì)量,貌似壓根不會有人意識到要看本文吧……

Duke的咆哮

本文我以為會在2個小時內(nèi)寫完,沒想到來來回回寫了足足10小時,整整一個周末的時間。我這個人吧,也算是個刀子嘴豆腐心的人。口口聲聲要“咆哮”,要“發(fā)泄”,到頭來還是苦口婆心講,像個小丑一樣考慮怎樣講能激起讀者的興趣,寓教于樂也得到收獲。心情愈發(fā)低落,但也希望本文能點亮些希望的種子吧。程序員都應該和垃圾代碼做斗爭,并為之奮斗一生。那么,至少,在這最后的環(huán)節(jié),讓我痛痛快快咆哮一回吧!

讓我們回過頭再來看垃圾代碼:

首先看Controller層,方法參數(shù)列表里用@RequestBody注解了一個Subscribe類,由于沒有加DTO字樣的后綴,所以默認的推測,該類應該是一個entity。如果是使用JPA或Hibernate的話,直接在Controller里使用entity作為請求參數(shù)接收對象是危險的。因為很可能有一些字段是不希望被用戶看到和提交的。但因為entity類是對表對應所有字段的映射,抓包者可以傳入意料之外的字段。開發(fā)者出于謹慎,就得對所有不應傳入的字段進行校驗,這會加大不少的開發(fā)成本吧。如果要請求的字段較多,更合理的方式是使用一個DTO對象作為參數(shù)接收器。但我們分析上下文,這個請求一共只用了Subscribe類對象的兩個屬性id和userId。在這種場景下,您真的有必要硬生生拉上來一個對象嗎?我一般的慣例是,如果請求參數(shù)少于3個,為了避免不必要的復雜度,可以使用直接列舉的參數(shù)列表或路徑參數(shù)(GET請求)或Map(POST請求且主體為JSON)去接收。對于多于3個的參數(shù),則建議構建專用的DTO去接收參數(shù),以充分利用靜態(tài)語言的優(yōu)勢。

所以,從第一行代碼開始,他就已經(jīng)輸了。

不僅輸了,而且輸了兩次。

連我的IDEA都智能地報告說:后邊這句throws Exception就是一句逗比代碼。其逗比程度高過下面不寫try-catch的那種。沒有try-catch,好歹聲明異常拋出代表該方法有可能拋出異常,你try-catch處理了所有Exception,該方法理論上就不會拋任何異常了。這時候你throws聲明該方法會拋出異常,是蠢呢還是壞呢?還好這是在Controller里,開發(fā)者不需要調(diào)用這個方法。如果是在其他場合,這種寫法就是在說,*雖然勞資什么Exception都不會拋出,但我tm就是非得惡心你上層代碼try-catch一下哦,啦啦啦。*那我簡直要打死你好么?

接下來,“Boolean flag = ...”。雖然Boolean和boolean只有一個字母之差,但前者為引用類型,后者為原始類型。對于只有正誤兩個取值的布爾來說,雖然創(chuàng)建一個引用類型不會帶來多少性能損失,但這種地方用不對,只能說明開發(fā)者的Java功底就是狗屁。

再往后,整個代碼片段最靈異的代碼出現(xiàn)了。一個用于訂閱的業(yè)務方法,返回了數(shù)字!乍一看,應該是用數(shù)字來指代成功或失敗吧。但是并沒有方法注釋告訴我們哪個數(shù)字對應什么狀態(tài)。一邊這么想著,一邊讓我們打開Service層方法的實現(xiàn)。OK,反正已經(jīng)預料到是這個屎結果了,service層是孤兒,沒爹沒媽,為啥有它?因為裝模作樣的在分層啊!

那么,來到DAO層,最最最震驚我的代碼出現(xiàn)了!這個返回值,它的含義居然是SQL語句執(zhí)行影響的行數(shù)!

行數(shù)!

行數(shù)!

行……數(shù)……

我tm沒看錯吧?

姑且不論這個嵌套著又查又更新的SQL有多SB,單單這個返回值,穿過DAO穿過Service像輸卵管中的小蝌蚪笨拙地滑入了Controller麻麻的懷抱的神奇操作,就已經(jīng)把在下唬得屁滾尿流了!

那么回過頭我們來看這段SQL:

UPDATE TM_SUBSCRIBER SET IFSUBSCRIBE = '1' WHERE ID = (select sub.ID from TM_SUBSCRIBER sub join TM_ONLINE_STUDY s on sub.STUDYID = s.ID where SUB.COMPANYID=? AND sub.DELETEFLAG='0'AND s.DELETEFLAG='0' AND s.id=? )

槽點多得我都列舉不完!

兩個表的別名取的那么有詩意sub和s貫徹譚浩強式命名指南暫且不提,您sub和SUB混用也罷,畢竟某些特定版本的特定SQL不區(qū)分別名大小寫。您兩個查詢變量一個天上一個地下被兩個刪除標識查詢限定隔開也罷,您能不能先解釋一下這段SQL的含義?

說人話:

先獲取某公司是否參加了某在線課程,然后如果參加了,就將其訂閱標識設為true……

這TM什么邏輯?!

感情某公司如果報名了該課程,你們不管三七二十一先在訂閱表里插入該公司,等該公司點擊了“訂閱”你們再將“是否訂閱”置true?

難道……

不應該是當某公司點擊了訂閱按鈕后,往訂閱表里插入一條包含課程ID和公司ID的記錄到訂閱表里么?

跪了,服了,哭了。

因為TM要接手這種SB代碼人,是我!!!!!!!!!!!!!!!

總結

以上是生活随笔為你收集整理的Duke的咆哮语录②:我求求你们跟我学一下代码“分层”吧!的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。