1. 程式人生 > 其它 >分析程式耦合與解耦思路

分析程式耦合與解耦思路

技術標籤:Springjava設計模式spring程式語言

程式間耦合是指程式之間的依賴關係。我們開發的程式如果耦合度太高就會導致獨立性太差,所以軟體開發應該遵循高內聚,低耦合的設計標準。依賴一般分為類之間的依賴和方法之間的依賴。那麼如何降低程式間的依賴呢?在開發中,應該做到程式在編譯的時候不依賴,在執行的時候才依賴,我們可以通過兩個步驟來分析實現。

  • 第一:使用反射來建立物件,避免使用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

此刻,已實現單例模式,並且成功完成解耦。