開發框架是什么,那些糟糕的開發框架

 2023-11-18 阅读 34 评论 0

摘要:本文章由@唐乾?出品,轉載請注明出處。?? 文章鏈接:?http://blog.csdn.net/tang9140/article/details/52526977 引言 本文將與大家一起來吐槽下公司的開發框架,站在開發者的角度詳述糟糕設計下導致的各種問題和使用痛點,并給出改進意見࿰


本文章由@唐乾?出品,轉載請注明出處。??
文章鏈接:?http://blog.csdn.net/tang9140/article/details/52526977


引言


本文將與大家一起來吐槽下公司的開發框架,站在開發者的角度詳述糟糕設計下導致的各種問題和使用痛點,并給出改進意見,最后附贈一個完整版后臺開發框架(Spring+Spring MVC+Apache Shiro+MyBatis+Bootstrap UI)


Java目前流行的開發框架,不外乎SSH、SSM,或者兩者的混搭。 集成這些框架技術本身并不難,難點在于怎么讓框架簡單易用,更好的服務于開發者,讓開發者專注于業務而不是一些無用的設計上。下面就從Dao,Service,Controller及基礎功能四個方面一一痛斥那些糟糕的設計,先從最底層dao說起

1、Dao

問題一、沒有抽象實體類

由于沒有抽象父類,每個實體中重復出現公共字段(如id,createBy,updateBy等)及相似方法(如toString()方法),不利于維護。解決方法就是提取出抽象父類BaseEntity,其中包含公共字段和方法,而其它實體繼承該類。


在對抽象類BaseEntity進行設計時,要仔細斟酌其中包含的字段和方法,避免過于簡單或復雜的設計。

一是設計過于簡單,只是簡單的包含公共字段。實際除了公共字段外,BaseEntity中還應該包含公共方法,如重寫toString()方法如下:

@Override
public String toString() {return ReflectionToStringBuilder.toString(this);
}

開發框架是什么。

這樣每個實體就不必單獨寫自己的toString方法了。


除此之外,BaseEntit中還可以定義抽象方法,如定義以下兩個抽象方法

//插入之前執行方法,子類實現
public abstract void preInsert();
//更新之前執行方法,子類實現
public abstract void preUpdate();
通過service層的配合,在執行dao的插入或更新前,執行相應實體的preInsert()或preUpdate()方法,統一規范了實體執行數據庫操作的事件方法。


二是設計過于復雜,包含一些不通用的字段或方法。如在BaseEntity包含parentId字段或getUrl()方法(用于獲取圖片訪問路徑)

當某幾個實體需要parentId字段時,我們可以通過定義新的抽象實體XxxEntity繼承自BaseEntity,然后在XxxEntity中定義parentId字段方式來實現。基本原則是通過有層次的繼承關系,來保證每個層級抽象類職責單一

對于某個實體類特有的行為,則直接在該實體上新增方法即可,比如在FileInfo實體上增加getUrl()方法。

問題二、沒有抽象Dao類

由于沒有抽象dao類,每個dao上出現相似的CRUD(增刪改查)方法聲明,代碼顯得冗余和混亂。如插入操作,不同的開發者命名就不一樣,有用insert、insertXxx、saveXxx各種名稱的。這還不算亂的,命名最亂是查詢操作,有getXxx、findXxx、queryXxx、loadXxx、selectXxx各種命名的,我就不一一列舉了,大家都懂得。


導致上面的問題有兩方面的原因:一是因為dao層設計不當;二是因為項目沒有命名規范管理。下面就說怎么從dao層設計上規避上面的問題。

解決方法跟實體抽象一樣,就是提取抽象父類BaseDao,其它dao繼承該抽象類。 例BaseDao接口代碼如下:

public interface BaseDao<T> {public T get(String id);public T get(T entity);public List<T> findList(T entity);public List<T> findAllList(T entity);public int insert(T entity);public int update(T entity);public int delete(T entity);}


由于BaseDao中包含了通用的CRUD操作,繼承該接口的dao也就不需要再進行方法聲明,保證了dao層方法命名的統一規范。


補充說明:由于上面只定義了BaseDao接口,對于mybatis框架來說,已經足夠使用,但是對于Hibernate框架來說,必須有對應的BaseDaoImpl方法實現,才能使dao層正常運轉起來,具體代碼實現可參考項目https://github.com/tylanbin/platform-ng

實現類地址: https://github.com/tylanbin/platform-ng/blob/master/src/main/java/me/lb/dao/common/impl/GenericDaoImpl.java

問題三、使用Map傳遞參數

dao層傳遞參數盡量不要使用Map集合,原因是Map隱藏了參數細節,調用者無法從方法參數上獲取到有用信息,導致代碼可讀性差。在hibernate框架的dao查詢方法上經常見到Map類型參數,應該盡量避免這種情況。可通過以下方式進行改進:

一、如果Map傳遞參數數量不超過兩個,直接使用其中的具體參數類型進行方法聲明

二、如果Map參數超過兩個,建議直接使用實體類或繼承實體的查詢類進行傳參。

2、Service

service常見問題跟dao層類似,一是沒有沒有抽象父類BaseService,導致大量相似的方法聲明出現;二是使用Map傳遞參數。解決方式也跟dao層相似,具體代碼參照推薦框架。下面說下service層另一個常見問題

Service之間大量相互調用

假設有兩個service A和B:A調用B的cud(增刪改)操作,這個是正常的,但是A要盡量少調用B的查詢方法,實在需要查詢時,直接調用B相應的dao進行查詢。

為啥要減少service之間的相互查詢調用?

一是出于查詢效率的考慮。 如UserService的get()方法中除了查詢用戶基本信息外,還會查詢相關信息,當在其它service中調用該方法時就會導致不必要的sql查詢出現。在比較關注查詢效率的場景下,各個service應該直接操作dao,實行按需查詢。

二是出于松耦合考慮。由于service查詢方法一般是為某個頁面或api服務,不具有一般性,因此,應該盡量避免service代碼相互耦合以提高系統擴展性。

3、Controller

與Dao,Serivice層一樣,Controller層應該有抽象父類BaseController,其中包含公共字段和方法。除此之外,Controller層還需要解決以下幾個問題:

表單參數如何接收?

一種不好的方式就是為每個表單定義一個EntityForm類(用來接收表單值)和FormConverter類(實現EntityForm和實體的相互轉換)。這種方式會導致出現大量的類、繁瑣的轉換代碼,并且意義也不大。這里推薦更簡單直接的方法,直接用實體類或繼承實體類的表單類進行接收,不需要做任何轉換即可與service層無縫對接。

表單參數如何驗證?

一般服務端對接收的參數都要進行驗證,如有效性、安全性驗證。一種不好的方式是在service中硬編碼進行檢驗,這樣對service層侵入性太強,修改也不方便。推薦通過Hibernate Validator注解,配合controller層手動調用驗證方法解決。

頁面傳值技巧

一般直接通過實體類或ViewObject對象向頁面傳值。這里要注意ViewObject中應該是聚合實體類(即包含實體類型的成員變量),而不是繼承實體類。對于使用mybatis框架來說,建議不要直接用sql得到Vo對象,這意味著需要在mapper.xml中定義resultMap,會導致每次復查代碼時要花時間對下resultMap中的字段映射,十分討厭。個人喜歡直接查詢出實體,再通過程序聚合得到Vo,這樣做既保證了 mapper.xml的簡潔干凈,又能滿足各種頁面傳值展示的需要。

4、基礎功能

問題一、沒有統一工具類

基礎工具類包括字符串處理、日期處理,文件處理、Http模擬請求、Excel導入導出、Json數據解析等。項目中最好由專人負責提供工具類,其它人直接使用,保證工具類的統一。

問題二、沒有統一分頁功能

由于沒有統一分頁功能,開發人員必需先寫countSql,然后再寫pageSql,十分不利于維護。推薦做法是通過數據庫分頁插件,攔截查詢語句自動化分頁(需要配合Page類實現),具體實現參照文后框架。

問題三、日志打印系列問題

日志打印有好幾個問題,列舉如下:

1、沒有通過slf4j接口打印日志

通過slf4j日志門面實現與具體的日志框架解耦,方便以后更換日志框架,比如從log4j切換到logback。如果日志打印是基于slf4j接口的,只需要更換日志jar包即可,是不是很cool。

2、日志打印級別過低問題

有的項目中通篇打的debug級別日志,這完全是亂來。對于重要信息或異常信息,一定要打error級別日志。一般項目在上線后,可能會調高日志打印級別為warn或error級別,因此重要信息一定要打error日志,以方便線上定位問題。

3、沒有日志攔截器功能

一般在請求剛進入action時,需要打印日志。不好的方式是在每個action里編寫打印日志代碼。正確的方法是通過日志攔截器,統一進行日志打印。另對于service層需要打印日志時,可通過AOP方式統一攔截處理。通過上面兩種方式,實現了日志打印和業務代碼之間的解耦,可做到隨時取消或修改日志打印。

問題四、關于異常設計

一種情況是沒有自定義異常;另一種情況是過度使用異常,甚至于在每個dao或service方法上都聲明拋出異常。異常應該在確實會發生異常的情況下才使用

對于自定義異常,建議繼承自RuntimeException(即運行時異常),而不是繼承Exception(即受檢異常)。當方法拋出受檢異常時,意味著調用方必須進行處理,或繼續往上拋出。只有在確認某個異常能被開發人員處理時,才能聲明拋出受檢異常,除此之外,都應該拋出非受檢異常,此時需要在方法DOC上說明可能拋出的異常信息。

問題五、沒有訪問權限設計

沒有統一的訪問權限設計,導致在程序中硬編碼方式進行權限管理。特別對于后臺系統,更需要一種細粒度和可配置的角色權限管理,推薦使用shiro框架進行統一認證和授權管理,當然也可選擇Spring Security,相對來說shiro簡單,易上手。

問題六、沒有代碼生成工具

對于高效開發來說,代碼生成工具必不可少。想想你正在使用mybatis框架,手動拼寫sql將是一個痛苦的過程,此時就更有必要使用一些自動化的工具。推薦使用rapid-generator(Java代碼生成器)或者IDE插件進行代碼生成。

問題七、沒有命名規范

由于沒有規范,每個開發人員各自為戰,導致命名混亂不坑。這個其實牽涉到管理上的問題,最好是制定好項目的命名規范,包括包結構、目錄結構、文件名甚至于方法和變量名等一系列規范。

結束語

搭建開發框架是每個項目的第一步,也是非常關鍵的一步,一定要慎之又慎。好的設計能夠起到事半功倍的效果,相反,不好的設計只會拖住開發者的腳步,延緩項目進度。

最后希望每個框架設計人員能夠腳踏實地,以解決開發者痛點為已任,一步步改進設計。沒有最好,只有更好,唯有不滿足現狀,方能進步

后臺開發框架項目地址:https://github.com/tangqian/jeesite

說明:該系統為精簡版的JeeSite系統,只保留用戶、角色、權限部分,去掉了CMS及流程部分

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://808629.com/179509.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 86后生记录生活 Inc. 保留所有权利。

底部版权信息