分析程式耦合與解耦思路
阿新 • • 發佈:2021-01-31
程式間耦合是指程式之間的依賴關係。我們開發的程式如果耦合度太高就會導致獨立性太差,所以軟體開發應該遵循高內聚,低耦合的設計標準。依賴一般分為類之間的依賴和方法之間的依賴。那麼如何降低程式間的依賴呢?在開發中,應該做到程式在編譯的時候不依賴,在執行的時候才依賴,我們可以通過兩個步驟來分析實現。
- 第一:使用反射來建立物件,避免使用new關鍵字建立;
- 第二:通過配置檔案來獲取要建立物件的全限定類名。通過以上兩步即可做到完美解耦。
1、程式耦合
案例:
我們做一個模擬儲存使用者的例子,在有解耦意識之前,我們的程式碼是這樣的:
package cn.com.gjw.dao;
//使用者持久層介面
public interface UserDao {
void saveUser();
}
package cn.com.gjw.dao.impl;
import cn.com.gjw.dao.UserDao;
//使用者持久層介面實現類
public class UserDaoImpl implements UserDao {
@Override
public void saveUser() {
System.out.println("模擬儲存使用者資訊。。。");
}
}
package cn. com.gjw.service;
//使用者業務層介面
public interface UserService {
void saveUser();
}
package cn.com.gjw.service.impl;
import cn.com.gjw.dao.UserDao;
import cn.com.gjw.dao.impl.UserDaoImpl;
import cn.com.gjw.service.UserService;
public class UserServiceImpl implements UserService {
private UserDao dao = new UserDaoImpl();
@Override
public void saveUser() {
dao.saveUser();
}
}
package cn.com.gjw.test;
import cn.com.gjw.service.UserService;
import cn.com.gjw.service.impl.UserServiceImpl;
public class Test2 {
public static void main(String[] args) {
UserService service = new UserServiceImpl();
service.saveUser();
}
}
main函式依賴UserServiceImpl類才能編譯成功,同樣,UserServiceImplement類依賴UserDaoImpl才能編譯成功,足以看出耦合度很高。
2、解耦
那麼接下來我們通過解耦來提高程式的獨立性:
首先建立一個屬性檔案(beans.properties
),以鍵值對的形式存放以下內容:
userService=cn.com.gjw.service.impl.UserServiceImpl
userDao=cn.com.gjw.dao.impl.UserDaoImpl
其次再建立一個Bean工廠,由工廠代替new關鍵字為我們建立物件:
/**
* 一個建立Bean物件的工程
*
* Bean:在計算機英語中,有可重用元件的含義
* JavaBean:用java語言編寫的可重用元件。
* javaBean > 實體類
*
* 它就是用來建立service和dao物件的。
*
* 第一個:需要一個配置檔案配置我們的service和dao
* 配置內容:唯一識別符號=全限定類名(key = value)
* 第二個:通過讀取配置檔案中配置的內容,反射建立物件
*
* 配置檔案可以是xml也可以是properties
*/
public class BeanFactory {
//定義Properties物件
private static Properties props;
//使用靜態程式碼塊為Properties賦值
static{
try {
//1.例項化物件
props = new Properties();
//2.獲取properties檔案的流物件
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("beans.properties");
props.load(in);
} catch (Exception e) {
throw new ExceptionInInitializerError("初始化properties失敗!");
}
}
/**
* 根據Bean的名稱獲取Bean物件
* @param beanName
* @return
*/
public static Object getBean(String beanName){
Object bean = null;
try {
String beanPath = props.getProperty(beanName);
System.out.println(beanPath);
bean = Class.forName(beanPath).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return bean;
}
}
此時,我們建立物件時,只需要呼叫BeanFactory類的getBean()方法即可。同時,我們對依賴的類也要做調整:
package cn.com.gjw.test;
import cn.com.gjw.factory.BeanFactory;
import cn.com.gjw.service.UserService;
public class Test2 {
public static void main(String[] args) {
//UserService service = new UserServiceImpl();
UserService service = (UserService) BeanFactory.getBean("userService");
service.saveUser();
}
}
package cn.com.gjw.service.impl;
import cn.com.gjw.dao.UserDao;
import cn.com.gjw.factory.BeanFactory;
import cn.com.gjw.service.UserService;
public class UserServiceImpl implements UserService {
//private UserDao dao = new UserDaoImpl();
private UserDao dao = (UserDao) BeanFactory.getBean("userDao");
@Override
public void saveUser() {
dao.saveUser();
}
}
至此,我們已完成程式間解耦,此時,不管程式中缺少UserServiceImpl類還是缺少UserDaoImpl類,程式都能編譯通過。
1.1 多例模式轉單例
但是我們的Bean現在是多例模式
,例如:
public static void main(String[] args) {
//UserService service = new UserServiceImpl();
for(int i = 0; i < 5; i++) {
UserService service = (UserService) BeanFactory.getBean("userService");
System.out.println(service);
}
//service.saveUser();
}
列印結果為:
從列印結果可以看出,我們迴圈了5次,建立了5次service物件,這會影響我們程式的效能,最好使用單例模式,同樣也要注意單例模式帶來的多執行緒問題,因此在使用單例模式時,我們儘量把普通變數定義到方法內部,形成區域性變數,對工廠類進行改進:
package cn.com.gjw.factory;
//bean工廠
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class BeanFactory {
private static Properties props;
//程式啟動時,將所有的物件建立好,並存入map集合中,使用的時候直接從集合中拿,不需要再建立
private static Map<String, Object> beans = new HashMap<String, Object>();
//靜態程式碼塊,只在程式啟動時執行一次
static {
try {
props = new Properties();
//通過流的方式載入屬性檔案
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("beans.properties");
props.load(in);
//從屬性物件props中獲取所有的鍵,是一個列舉型別的集合
Enumeration<Object> keys = props.keys();
//遍歷
while(keys.hasMoreElements()) {
String key = keys.nextElement().toString();
String pathName = props.getProperty(key);
try {
Object value = Class.forName(pathName).newInstance();
beans.put(key, value);
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static Object getBean(String beanName) {
return beans.get(beanName);
}
//通過bean的名稱獲取物件
/* public static Object getBean(String beanName) {
//bean的全限定類名
String beanPath = props.getProperty(beanName);
Object bean = null;
try {
bean = Class.forName(beanPath).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return bean;
}*/
}
列印結果為:
com.study.iocbase.service.UserServiceImpl@61bbe9ba
com.study.iocbase.service.UserServiceImpl@61bbe9ba
com.study.iocbase.service.UserServiceImpl@61bbe9ba
com.study.iocbase.service.UserServiceImpl@61bbe9ba
com.study.iocbase.service.UserServiceImpl@61bbe9ba
此刻,已實現單例模式,並且成功完成解耦。