相信大多數人在剛接觸Spring時候,對Spring最重要的幾個概念:IOC(反轉控制),DI(依賴注入),AOP(面向切面編程)概念很模糊,甚至感到很費解。不知道設計出這些模式的原因,只知道在實際編程中用上這些模式後代碼的確變得簡潔,清晰,軟件的可維護性也得到了很大的提高。
這篇文章先介紹Spring的IOC,我們從面向對象的設計原則上理解Spring的IOC運用的目的,相信能爲大家以後的Spring學習理解增加些幫助。
首先我們要知道IOC的實現方式並不是程序員憑空想象出來的,肯定是在遇到具體的問題後,從設計上解決的一種方法。
下面我們就從設計者的角度看看,到底是遇到了什麼問題,讓設計者有了優化設計的思考:
程序猿小A正在開發一個模擬手機的應用,通過該應用可以實現將各種手機的特徵模擬到一臺手機上。該應用當然也是採用了面嚮對象的技術,小A設計了一個手機的父類(CellPhone),其他的各種手機繼承了該手機父類。
父類CellPhone代碼如下:
public abstract class CellPhone {
// 手機外觀
public abstract void display();
// 手機鈴聲
public abstract void ringtone();
// 發短信
public void sms() {
System.out.println("I can send sms!!");
}
// 打電話
public void callPhone() {
System.out.println("I can call someone!!");
}
// 連wifi
public void wifi() {
System.out.println("I can connect wifi");
}
}
子類MiCellPhone代碼如下:
public class MiCellPhone extends CellPhone{
@Override
public void display() {
System.out.println("mi display!!");
}
@Override
public void ringtone() {
System.out.println("mi ringtone!!");
}
}
子類HuaweiCellPhone代碼如下:
public class HuaweiCellPhone extends CellPhone{
@Override
public void display() {
System.out.println("huawei display!!");
}
@Override
public void ringtone() {
System.out.println("huawei ringtone!!");
}
}
mi手機,huawei手機各自繼承了cellphone類。
現在有一個模擬手機的方法simulateCellPhone,模擬各種手機的特徵,代碼如下:
public void simulateCellPhone(String cellType) {
CellPhone cellPhone = null;
if("mi".equals(cellType)){
cellPhone = new MiCellPhone();
}else if("huawei".equals(cellType)) {
cellPhone = new HuaweiCellPhone();
}else {
// cellPhone = 更多的手機類型
}
// 模擬外觀
cellPhone.display();
// 模擬鈴聲
cellPhone.ringtone();
}
可以看出來,當有一些需要實例化的實體類時,究竟實例化哪一個實體類,需要在實際運行的時候才能決定。
而且,對象間的耦合度過高,一旦需要增加模擬的手機類型如iphoneCellPhone等,就需要再次增加else if進行手機實例化。一旦某些手機的不需要模擬了,則需要將對應的實例化片段刪去。
後期維護的時候一旦有變化或者擴展,就必須要再次修改這個方法,對代碼進行檢查或者修改。通常下這種修改過的代碼將造成部分系統更難維護和更新,而且也更容易犯錯。
要知道,軟件開發中工作量佔比最大,時間花費最長的就是系統維護了,佔比甚至達到了70%
爲了減少系統維護人員(主要可能還是自己維護0.0)花費的時間,善良,負責的(主要是爲了懶。。)程序員小A必須想出一個法子,降低對象間的耦合性。
這時,程序員小A想到了面向對象設計原則之一:
依賴倒置原則:要依賴抽象,不要依賴具體類。
依據該原則實現的設計模式:工廠模式及反射機制,
進而引申出了IOC(反轉控制)的概念,簡單來說就是:
設計好的對象由自己“new”實例化調用,變成了交由Spring容器創建對象。
下面就是程序員小A用IOC來實現模擬手機功能的步驟,看看和上面的方式有什麼不同吧!
在spring的配置文件applicationContext.xml中,代碼如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- MiCellPhone的bean配置 -->
<bean name="miCellPhone" class="pojo.MiCellPhone">
</bean>
<!-- HuaweiCellPhone的bean配置 -->
<bean name="huaweiCellPhone" class="pojo.HuaweiCellPhone">
</bean>
</beans>
這時,整個模擬手機的測試類只需要寫成:
public class SimulateTest {
public static ApplicationContext context = null;
public static void main(String[] args) {
// 加載配置文件的路徑
context = new ClassPathXmlApplicationContext(new String[] { "applicationContext.xml" });
SimulateTest s = new SimulateTest();
s.simulateCellPhone("miCellPhone");
}
public void simulateCellPhone(String cellType) {
CellPhone cellPhone = (CellPhone)context.getBean(cellType);
// 模擬外觀
cellPhone.display();
// 模擬鈴聲
cellPhone.ringtone();
}
}
可以看出在模擬手機方法simulateCellPhone中,類的實例化直接通過Spring來獲取,不需要自己通過”new”來實例化變量。
通過對比不使用IOC的模擬手機方法和使用IOC的模擬手機方法可以看出,使用IOC的模擬手機方法代碼不僅簡潔,而且對於系統維護時手機種類的增加或者刪除,該方法都不需要進行改動。
至此,程序員小A終於滿意了,可以安心的繼續“懶”着了。
總結:
IOC模式就是:由程序員自己控制對象的生成過程,變成了由容器控制對象間的依靠關係。
使用IOC模式即是將類與類之間的耦合代碼從程序中移出,放到統一的XML文件(applicationContext.xml)中管理。控制權由程序代碼控制轉移到xml文件中管理。
IOC模式的優點:
極大的降低了類與類間的耦合性,實現了類與類之間的解耦,提高了系統的維護性和靈活性。
因爲IOC容器運用了反射的原理,實例化類的時候,通過xml文件臨時決定生成哪一種對象,生成對象的速度相對較慢。
IOC模式的缺點:
1.使用IOC模式看起來沒有直接在程序中實例化類來的直觀,變得複雜了些。(但其實習慣了就會覺得很方便)
2.反射的使用,使得生成對象的速度相對較慢,效率相對降低。(但對於IOC模式的優點來說,這點幾乎可以忽略)
3.xml文件不支持IDE的重構,如果修改了類名,還需到XML文件中手動修改。