從面向對象(OO)設計原則上理解Spring的IOC

相信大多數人在剛接觸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文件中手動修改。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章