共計 4316 個字符,預計需要花費 11 分鐘才能閱讀完成。
推薦學習:
這可能是全網 Java 學習路線最完整,最詳細的版本了,沒有之一
1、聊聊解耦?
耦合:代碼之間的關聯關系稱為耦合,具有強關聯關系的稱為強耦合。
解耦:解除代碼之間的關聯關系,使每個業務代碼職責單一,目的明確,通常我們在業務上稱為解耦。
2、代碼示例
我們以傳統的 EJB 開發模式為例子,先不以框架展示,大家可以看看一些改代碼難受的場景。
業務來了:我需要把一段數據保存到 mysql 數據庫中,按照分層邏輯實現(controller,service,dao)
Dao 接口層:
publicinterfaceUserDao{
/**
* 保存的接口方法
*/
voidsave();
}
Dao 的 mysql 實現:
publicclassUserDaoMysqlImplimplementsUserDao{
@Override
publicvoidsave() {
System.out.println(“ 保存 mysql 數據庫成功!”);
}
}
Service 接口層:
publicinterfaceUserService{
/**
* 業務接口保存方法
*/
voidsave();
}
Service 的實現:
publicclassUserServiceImplimplementsUserService{
// 業務層調用數據 dao 層,這里不解釋了
privateUserDao userDao =newUserDaoMysqlImpl();
@Override
publicvoidsave() {
userDao.save();
}
}
Controller 視圖層:
publicclassUserController{
privateUserService userService =newUserServiceImpl();
publicvoidsave(){
userService.save();
}
}
很明顯,我們已經實現了業務功能:保存一段數據進 mysql 數據庫
這個時候,你的產品經理說,客戶 mysql 壞了,剛裝了個 oracle,你再改改吧?
然后你這個時候也就加個 oracle,實際上不費時,需求我們再補充下
現在需求:保存一段數據,可以保存在 mysql,也可以保存在 oracle
上面已經有 mysql 代碼了,我們可以知道,我需要增加個 dao 的實現,稱為 UserDaoOracleImpl
上代碼先:
publicclassUserDaoOracleImplimplementsUserDao{
@Override
publicvoidsave() {
System.out.println(“ 保存 oracle 數據庫成功!”);
}
}
ok 我們還要改一個地方,就是 UserServiceImpl,之前父類接口指向的子類引用要改成 oracle
publicclassUserServiceImplimplementsUserService{
// 業務層調用數據 dao 層,這里不解釋了
// private UserDao userDao = new UserDaoMysqlImpl();
privateUserDao userDao =newUserDaoOracleImpl();
@Override
publicvoidsave() {
userDao.save();
}
}
我們發現,在目前的需求形式上,dao 的擴展我們是一定會需要改的,因為滿足多態的特性,但是我們增加一個 dao 層的某個業務,連 service 業務層代碼也要動,你想想,如果業務層代碼達到了上千,即便你有了注釋,改了某一層,另一層也要跟著改動,是不是很難受?
所以我們目前要解決:(如果每次 dao 進行擴展都去 service 修改源碼來切換到新的 dao,這樣做法耦合度太高,每次都需要去修改 UserServiceImpl 的代碼)
這個場景,我們很經典的稱為耦合!!!!我們可以自己給耦合多一個定義
耦合: 代碼之間的關聯關系稱為耦合,更具體的說,在當前主流的職責劃分層次(controller,service,dao)明確的前提下進行編碼,某一層的改動,會導致另一個層跟著變動,可以稱為耦合。
3、工廠模式可以解耦
在不了解工廠模式的前提下,就默認把這個作為生產對象的地方;
我們新建一個工廠,稱為 BeanFactory
/**
* 定義一個 bean 工廠,專門生產普通對象
*/
publicclassBeanFactory{
publicstaticUserDaogetBean() {
returnnewUserDaoMysqlImpl();
}
}
然后我們要對耦合的那一層修改,目前看是把 service 解耦了吧?我所有創建對象的操作,都在一個具體的工廠類里;
publicclassUserServiceImplimplementsUserService{
// 業務層調用數據 dao 層,這里不解釋了
// private UserDao userDao = new UserDaoMysqlImpl();
// private UserDao userDao = new UserDaoOracleImpl();
privateUserDao userDao = BeanFactory.getBean();
@Override
publicvoidsave() {
userDao.save();
}
}
很明顯,我已經進行了 service 和 dao 解耦;但是!!!!!!
該死的需求是:我要改成 oracle,sqlserver,……
那我們繼續:我順便把之前的 getBean 換成了 mysql 的
publicclassUserServiceImplimplementsUserService{
// 業務層調用數據 dao 層,這里不解釋了
// private UserDao userDao = new UserDaoMysqlImpl();
// private UserDao userDao = new UserDaoOracleImpl();
// private UserDao userDao = BeanFactory.getMysqlBean();
privateUserDao userDao = BeanFactory.getOracleBean();
@Override
publicvoidsave() {
userDao.save();
}
}
很明顯我現在已經把 dao 和 service 的耦合,轉移到了工廠和 service 上了,這就是解耦的一步;但是大家還是疑惑,我感覺我代碼增多了?或者更麻煩了?我們繼續看
我們每次增加新的業務,擴展,都要修改工廠,所以這個我們可以不可以不在代碼里直接做這個事情?——– 引出配置文件
我們在 resources 下定義一個 applicationContext.properties
userDao=com.chenxin.gmall.user.demo.dao.UserDaoMysqlImpl
userService=com.chenxin.gmall.user.demo.service.UserServiceImpl
如果我們需要換成 oracle,我們只改這個配置文件,改成 UserDaoOracleImpl,不需要去動代碼
那我們剛剛的 BeanFactory 就又可以通過讀取配置文件的方式,用反射來創建對象,反射創建對象是根據對象的全類名做的,不是直接 new,看看效果
/**
* 定義一個 bean 工廠,專門生產普通對象
*/
publicclassBeanFactory{
privatestaticProperties properties =newProperties();
//1. 加載配置文件
static{
InputStream resourceAsStream = BeanFactory.class.getResourceAsStream(“/applicationContext.properties”);
try{
properties.load(resourceAsStream);
resourceAsStream.close();
}catch(IOException e) {
e.printStackTrace();
}
}
//publicstaticUserDaogetMysqlBean() {
// return new UserDaoMysqlImpl();
// }
//
// public static UserDao getOracleBean() {
// return new UserDaoOracleImpl();
// }
publicstaticUserDaogetBean(String key){
// 2. 使用 key 獲得 value
String className = properties.getProperty(key);
// 3. 使用 value 利用反射技術創建對象
try{
return(UserDao) Class.forName(className).newInstance();
}catch(Exception e){
e.printStackTrace();
returnnull;
}
}
}
好了,我們來需求,我們要保存到 oracle,sqlserver 數據庫,看看你是不是只需要多一個 dao 的 oracle 實現,然后去改配置文件就 ok?多一個 sqlserver 后,改下 applicationContext.properties
算了我直接寫個代碼吧:
/**
* 新增了 sqlserver 的支持,多態的表現
*/
publicclassUserDaoSqlServerImplimplementsUserDao{
@Override
publicvoidsave() {
System.out.println(“ 保存 SqlServer 數據庫成功!”);
}
}
改下 applicationContext.properties
userDao=com.chenxin.gmall.user.demo.dao.UserDaoSqlServerImpl
userService=com.chenxin.gmall.user.demo.service.UserServiceImpl
你可以試一下,是不是這么干的!
現在看來,是解耦了不少吧。但是會有人發現嗎,這個 getBean 返回值,是 UserDao,如果你有很多,是不是我們要寫很多很多的 Dao 的 getBean?別急,后面一步一步帶你走向 spring 的思路
最后一句話,解耦不代表代碼一定少,更多時候是你用更多的代碼來解決人力成本,所以新手一定要記得,解耦的原則,是減少開發中出現的問題,增加開發效率,不代表代碼一定會減少下去,希望不要有這樣的誤區!
感謝閱讀,三連是最大的支持!
丸趣 TV 網 – 提供最優質的資源集合!