圖片來源:Wikipedia
大家聽過「帕拉林匹克運動會(帕奧)」嗎?它是自 1960 ~ 70 年代開始,專為身心障礙者舉辦的國際體育賽事,於 1990 年代開始,在一般奧運閉幕後一個月內,於同一城市舉辦。帕奧的名字(Paralympic)據說是由 Paraplegia 與 Olympic 合併而來,在台灣曾經一度稱之為「殘障奧運會」,後來為了顧及身心障礙者感受,改以音譯的「帕拉林匹克運動會」稱之。
今天要聊的是十個 RD 有十個都曾製造過的程式壞味道:Data Class。這是個「明明四肢健全,卻要自廢武功」的壞習慣。可以說如果帕奧提倡的是殘而不廢,那 Data Class 就是「不殘而廢」了。
Data Class 顧名思義,就是「只有 Data 的 Class」,簡單吧!那麼,為什麼只有 Data 是一個壞味道呢?因為在物件導向的世界中,物件與物件是藉由「行為」來互動的。沒有行為,物件就不會「動」,事情就沒人做。然而你為了要完成工作,不可能沒人做事,對吧?所以常見的做法,就是在另外的類別裡面,加入一些與這些資料相關的操作,以完成任務。
這裡我們就不另外舉例子了,我們先前其實已經舉過一個「沒有 Data Class 壞味道」的例子了,程式如下。反倒是各位可以試著把 getFullName 方法往外搬到其他類別上去,讓 Student 只剩資料,看看這樣是否在可讀性上真的變差了。
@Data
public class Student {
String lastName;
String firstName;
public Student(String firstName, String lastName) {
this.lastName = lastName;
this.firstName = firstName;
}
// 搬走試試?
public String getFullName() {
return firstName + " " + lastName;
}
}
總之,當遇到 Data Class 壞味道時該怎麼解決?其實也不難,就利用 Move Method 的方法,把行為搬到物件上去就可以了。
這個壞味道,筆者認爲是在「辨別」上比較需要有技巧一點,因為得看當時的場景來決定。
Data Class 之所以為壞味道,展現在耦合度上。一個物件的行為明明可以自己完成,卻跑去請別人幫忙,如果今天資料格式有換,那幫忙的人就不得不跟著換,測試也會跟著壞,這是不太合理的事情。先前說過了,物件導向程式的物件之間溝通,應該要盡量倚賴行為,而不是資料。
然而,Data Class 一定是壞味道嗎?「VO」、「DTO」難道不是一種約定成俗的模式嗎?我就是只要取資料而已,我要取的資料就只是想要放在那邊給 View 來拿,或直接回傳給前端網頁,因此而沒有行為,這樣難道也不行嗎?
可以,當然可以。一個物件如果只有資料,沒有行為,他「不一定」會有 Data Class 的壞味道。如上所述,他還得搭配一個「放在其他地方的行為」才算數。
這時,如果我們搭配 CQRS(命令查詢職責分離)模式來看,就會比較清楚。Ajay Kumar 在 CQRS 一書中,有建議我們,命令指令與查詢指令要從權責上分開。命令指令要讓 Entity 加入,查詢則不用,可以依查詢者指定的樣貌任意組裝物件。
譬如在我們的「教務處網站系統」中,Student 代表學生,是重要、有狀態的核心物件,是 Entity,會在多種 Use Case 中被使用,這時如果一個行為是學生自己可以完成的,那就讓學生自己完成。譬如我們先前曾經舉過的 getFullName 行為,學生自己身上已經有 last name 與 first name,當我需要 full name 時,我可以把「組裝」這個行為放在 Student 身上,而不是 Use Case 上。
如果今天是一個「登入歡迎頁」需要的資料,當前端來跟後端 Query 時,我大可以依照前端指定要的項目組裝給它,而這個物件只需要儲存資料就好,因為前端要的也只是資料而已。他不是不能有行為,只是它大部分的情況下不需要行為,這是我們不能因為它只有資料就說他有壞味道。啊它就不需要做事情你硬塞個行為給它做啥呢?
所以,不是所有只存資料的物件都有 Data Class 壞味道,還是要看狀況。
這裡我要請讀者稍微思考一下,Data Class 壞味道,是不是很容易進一步造成 Feature Envy 壞味道?為什麼?
謎之聲:「你臭我也臭,大家一起臭?」
ithelp2021