在Java Web應用程式中,SpringMVC的@Controller
,@Service
及@Repository
的分層架構是經常被使用的架構。在實作Service層時,通常會先定義Service的介面,然後才撰寫具體類別實作該介面。我曾經認為為什麼要這麼麻煩呢? Service直接寫成類別不就好了,寫一個介面然後再來繼承不是多此一舉很麻煩嗎? 後來查了一下才知道這樣做的原因其實就是符合SOLID原則的設計,不直接依賴類別的好處如下。
在寫單元測試的時候比較容易
例如你要測試一個功能模組,例如查詢使用者,該模組使用到UserService
,當撰寫測試用的程式碼時可能不想使用真正的UserService
的來測試,而是用一個測試版的UserServiceMock
來返回一些預先定義好的測試資料,若UserService
有定義介面,則測試時可以傳入測試用的UserServiceMock
類別。如果UserService
定義成具體的類別,就無法輕易地傳入測試用的實作了,你可能要更改到原本被測試的模組程式碼。
在擴充上比較容易
UserService
定義介面的話可以輕鬆地添加對UserService
的實作。例如原本是從資料庫讀取使用者資訊,如果今天要增加可以從xml檔讀取使用者資訊,則只要繼承UserService
介面並實作具體類別即可,原本的實作不用更動程式碼。
例如下面UserService
是具體類別,目前分別可從xml或資料庫中查詢使用者。
UserController
public class UserController {
@Autowired
private UserService userService;
public List<UserInfo> queryUser() {
return this.queryFromDatabase();
}
/** 從xml查詢 */
private List<UserInfo> queryFromXml() {
return userService.queryFromXml();
}
/** 從資料庫查詢 */
private List<UserInfo> queryFromDatabase() {
return userService.queryFromDatabase();
}
}
UserService
public class UserService {
public List<UserInfo> queryFromXml() {
// 從xml查詢
}
public List<UserInfo> queryFromDatabase() {
// 從資料庫查詢
}
}
如果多了一個可以從JSON資料查詢使用者,則必在Controller和Service添加新增的程式碼如下。
public class UserController {
@Autowired
private UserService userService;
public List<UserInfo> queryUser() {
List<UserInfo> userList = this.queryFromDatabase();
}
/** 從xml查詢 */
private List<UserInfo> queryFromXml() {
userService.queryFromXml();
}
/** 從資料庫查詢 */
private List<UserInfo> queryFromDatabase() {
userService.queryFromDatabase();
}
/** 從JSON查詢 */
private List<UserInfo> queryFromJSON() {
userService.queryFromJSON();
}
}
public class UserService {
public List<UserInfo> queryFromXml() {
// 從xml查詢
}
public List<UserInfo> queryFromDatabase() {
// 從資料庫查詢
}
// 新增從JSON查詢
public List<UserInfo> queryFromJSON() {
// 從JSON查詢
}
}
從上可以看到Controller和Serivce的程式碼都會被更動到。若還有非常多其他的Controller有用到UerService
,則每次UserService
有新的方法被加入時,所有用到UserService
的Controller也會被影響,造成Controller和Service高度耦合,使得在擴充和維護上變得困難。
若把UserService
定義成介面如下。
UserService
public interface UserService {
public List<UserInfo> query();
}
實作UserService
的類別。
UserServiceXmlImpl
public class UserServiceXmlImpl implements UserService {
@Override
public List<UserInfo> query() {
// 從xml查詢
}
}
UserServiceDatabaseImpl
public class UserServiceDatabaseImpl implements UserService {
@Override
public List<UserInfo> query() {
// 從資料庫查詢
}
}
UserServiceJSONImpl
public class UserServiceJSONImpl implements UserService {
@Override
public List<UserInfo> query() {
// 從JSON查詢
}
}
可以看到在UserController中
只有一種查詢方法,Controller只知道UserService
的存在,透過注入不同實作類別來查詢不同的資料來源,以後若要新增新的查詢來源方法,只要撰寫一個實作UserService
的類別並實作UserService.query()
方法即可,Controller只要依需求注入對應的實作(或使用@Order
來調整注入順序),使得在維護上變得容易。
UserController
public class UserController {
@Qualifier("ouserServiceDatabaseImplne")
@Autowired
private UserService userService;
public List<UserInfo> queryUser() {
return userService.query();
}
}
如果覺得文章有幫助的話還幫忙點個Google廣告,感恩。
參考:
沒有留言:
張貼留言