現在以網頁發布的軟件非常普遍,叫BS模式。前后端分離也是大趨勢,或者說逐漸普及開來,深受前后端程序員的喜愛,我還是習慣以程序員來泛稱所有軟件制作者。后端需要把數據傳送給前端,往往是通過DTO的序列化來實現的,而不是直接產生json或xml格式的數據。這里不說為什么要用DTO,只說探討一個問題,不同的api接口(方法)返回不同格式的數據,這些DTO要如何設計。
特別說明,以下說到的DTO,是指DTO類,而不是DTO實例。雖然O應該是指實例,但這里繼續用這個詞,不阻礙意思的表述。
方案一,需要什么造什么。
這個方案簡單,每個接口需要的數據結構不同就造不同的DTO,相同就復用。一般情況下能復用的不多,於是會出現大量的DTO,這些DTO的命名和管理成為難題。若規定一個接口獨立使用一個DTO,則寫這些DTO的工作量不小。還有一種粗糙的辦法,假設有100個DTO,把它們按相近程度划分為少數若干類DTO,假設10類吧,然后同一類的DTO屬性合並成一個DTO,這樣確實能有效減少DTO的數量,但在具體的應用時,會多出來許多很是奇怪的屬性。不可取。
方案二,照着對象造DTO
這里的對象可以簡單理解為數據表,一個數據表對應一個DTO,但是多對多的關系表不對應DTO。造出來的DTO基本上與數據表的數量一樣多(含統計功能的查詢除外),有效的控制了DTO的數量,命名也方便。一個缺點是每個表的全部字段不管是否用到都在DTO中,有些字段只在少數情況下使用的,也得到處出現。另一個缺點是在DTO之間的關系上幾乎總是造成循環引用。比如子對象往往要引用父對象,而父對象也有子對象的集合。不同接口往往只需要表現其中一個方向,但做出來的兩個DTO類之間就必然有這種相互引用的關系,在自動生成api文檔的時候,這種循環引用會是個問題,至今沒有找到好的解決辦法。
方案三,一個對象對應N個DTO
這N個DTO我作了規划:
類型 | 基類 | 范圍 |
---|---|---|
DTO1 | 只有ID和Name | |
DTO2 | DTO1 | 只有常用的屬性 |
DTO3 | DTO2 | 有全部的屬性,但不包含IsDeleted這類特殊的附加屬性 |
DTO4 | DTO3 | 包含了父對象 |
DTO5 | DTO3 | 包含了子對象 |
DTO6 | DTO4 | 包含了父對象和子對象 |
DTO4,DTO5,DTO6引用其它的DTO,作為泛型在具體使用的時候傳入,並約束為對應的DTO1本身或子類。
這樣,就可以以有限的數量的DTO,來組合表達足夠靈活的結構,同時不會有輸出太多多余的屬性的情況。
也能解決DTO之間的循環引用問題,從而能使用swagger等生成api文檔。
以學生和班級為例:
- 需要按條件查到若干個學生和每個學生對應的班級名稱,則是結構是:
List<學生3<班級1>> 學生列表;
- 需要得到若干個班級,以及每個班級的部分符合指定條件的學生
List<班級5<學生2>>
- 需要得到指定老師所教的全部班級的學生,則是
老師5<班級5<學生2>>
缺點也是有的,DTO有6個,雖然有繼承,不用重復寫,但總比只有1個DTO要多寫點代碼。另外,這些DTO如何區分命名比較好,也是一個有待進一步完善的問題。如上表,是以數字123456區分的,應該有更好的命名方案。
- 2019-03 經過項目的實際使用, 調整為以下方案, 更適應實際的應用的需要。
類型 | 基類 | 范圍 |
---|---|---|
DTO1 | ID和Name(Code) | |
DTO2 | DTO1 | 只有常用的屬性 |
DTO3 | DTO2 | 全部屬性(不包含IsDeleted這類特殊的附加屬性,下同) |
DTO4 | DTO2 | 常用屬性+父對象 |
DTO5 | DTO2 | 常用屬性+子對象 |
DTO6 | DTO4 | 常用屬性+父對象+子對象 |
DTO7 | DTO3 | 全部屬性+父對象 |
DTO8 | DTO3 | 全部屬性+子對象 |
DTO9 | DTO7 | 全部屬性+父對象+子對象 |