相信很多開發者在面試的時候,都會被問到過什麼是Spring的控制反轉?控制反轉的原理到底是什麼?是誰控制了誰?誰又進行了控制反轉等問題?下面我們就來一一解答一下這些問題吧?
IoC是什麼?
很多初學者将Spring IoC看做了一種技術手段,說IoC是Spring實作控制反轉的一種手段,其實不然,IoC本不是一種技術手段,而是一種在程式代碼設計中的設計思想。這種程式設計思想可以通過Java實作、也可以通過Python實作。
試想,在我們開始接觸面向對象程式設計的時候,需要建立一個對象使用的時候,需要通過new這個關鍵字來建立,然後使用。
但有了IoC思想的支援,在開發這種就意味着開發效率的提升,為什麼這麼說呢?因為IoC為我們提供的程式設計思路就是通過中間容器的方式去管理在程式設計過程中我們需要的去使用的這些對象,如圖所示。
那麼我們到底如何去了解IoC這種程式設計思想呢?
控制?什麼是控制?
上面提到了傳統建立對象的方式是通過在某個對象使用空間中通過new的方式去建立另一個對象,由開發人員去主動的管理這個對象,這就是所謂的控制。而在IoC思想中,我們将這些對象的管理交給了一個叫容器的東西,由容器去來控制這些對象。
反轉?什麼是反轉?
根據上面的描述,在傳統的開發中,是由開發者自己來控制對象的建立與銷毀的,這個是開發者主動的操作過程。而反轉的意思,就是說開發者不需要主動去控制對象,而是由容器來幫助開發者完成對于對象的依賴控制。
是以說反轉的意思,就是對于對象的管理,由開發者反轉成了容器。意思就是說開發者需要依賴容器去對對象進行管理和建立。
通過上面的了解,IoC,控制反轉的意思就是将對象的控制權進行了轉移,從開發者本身轉移到了對象容器控制。
那麼既然依賴關系發生了變化,總得遵循一定的原則吧?這裡遵循的開發原則就是依賴倒置原則,那麼什麼是依賴倒置原則?
六大基礎設計原則
單一職責原則
一個類隻負責一項單一的職責,不存在超過一個因素而導緻類的狀态發生變化。
裡氏轉換原則
在能使用基礎類的地方,都可以用其實作子類來進行替換,這種替換不會引起任何的程式問題。
接口隔離原則
用戶端操作不應依賴其不需要的接口,而類關系的建立應該在最小接口上進行依賴。
迪米特法則
對于一個對象對其他對象的了解越少越好。
開閉原則
軟體設計對于擴充操作應該是開放的,也就是子產品的擴充是開放的,而對于修改是關閉的,也就是說某個已有子產品的行為是不能被修改的。
依賴倒置原則
對于高層次的子產品,不應該依賴于低層次的子產品
例如一個公司的組織架構,如下圖所示。董事會是整個組織架構的最高子產品組成,而其他的子產品都是董事會的子子產品,同理,營運部又是市場部和銷售部的高層子產品,以此類推。
抽象操作不應該是依賴于具體的實作去完成的,而是具體的實作是依賴于抽象去進行的擴充。例如車,這個概念是一個抽象上的概念,基于車這個概念就出現大車、小車等。而再具體的某個專業領域中就出現了消防車、救護車、警車等。
為什麼要使用依賴倒置?
假設一個場景,一個人從北四環外去東二環上班,他有三種選擇方式。自行車、公共交車和自己開車。
如果沒有依賴倒置的話我們實作這個操作應該如下的操作。
public class Person {
private Bike bike;
private Bus bus;
private Car car;
public Person(){
bus = new Bus();
}
public void goWork(){
System.out.println(bus.go("公共汽車"));
}
}
如果有條件的話就可以開車上班這個時候代碼就需要改成如下
public class Person {
private Bike bike;
private Bus bus;
private Car car;
public Person(){
//bus = new Bus();
car = new Car();
}
public void goWork(){
// System.out.println(bus.go("公共汽車"));
System.out.println(car.go("開車"));
}
}
顯然,這種操作如果這個人的交通選擇方式越來越多的時候,需要加入的新對象也就會越來越多。也就是說每次修改需求,都需要對Person類進行大的修改,來适配新的交通工具。相當于每次都是對Person類的重構。
那麼如何使用上面提到的這些原則來完成對于人上班的操作優化呢?
依賴倒置原則的使用
定義一個上班的動作接口
這個接口主要就是對員工上班的操作動作進行了定義。
public interface GoWork {
/**
* 上班的方式
* @param method
* @return
*/
String goWork(String method);
}
繼承接口實作一個Bike類
public class Bike implements GoWork {
@Override
public String goWork(String method) {
return method;
}
}
實作人員類
public class Person {
private GoWork goWork;
public Person(){
goWork = new Bike();
}
public void go(){
System.out.println(goWork.goWork("自行車"));
}
}
從這改造上來看,在人員更換交通工具的時候隻需要修改goWork = new Bike();代碼即可。
GoWork接口抽象了人員操作的一個上班的行為,而具體到自行車、公共汽車、小汽車等操作上來看,每個操作就不一樣了,可以有自己具體的實作方式。但是其共同的特性就是可以将人員送到公司上班的地方。
當然如果繼續改造的話,我們可以将人員類進行如下的修改。
public class Person {
private GoWork goWork;
public Person(GoWork goWork){
goWork = goWork;
}
public void go(){
System.out.println(goWork.goWork("自行車"));
}
}
這樣我們就實作了對于上面的迪米特法則的實作,讓人員類與車輛類之間的依賴了解降到了最低。
而這個時候,我們可以繼承GoWork接口來實作任何交通工作方式的擴充,也就是說開閉原則,對于擴充是開放的,但是對于修改是不支援的,因為修改之後會讓其他的繼承類也出現修改。
而對于擴充其他的功能來講,可以通過繼承其他的接口來實作如下所示。假設該人員的小汽車是奧迪那麼它在具備了上班這個功能的基礎上,還有奧迪的配置,但是這個配置是自行車和公共汽車沒有的那麼就可以通過如下的這種方式來實作。
定義奧迪規則。
public interface Aodi {
/**
* 奧迪加速
*/
public void speed();
}
多繼承
public class Car implements GoWork,Aodi {
@Override
public void speed() {
}
@Override
public String goWork(String method) {
return null;
}
}
通過上述的功能,就完成了上面提到的所有原則的使用。
總結
上面我們介紹了關于IoC程式設計思想的相關内容。通過一個人員上班的小例子來展示了程式設計思想在具體操作中使用。程式設計思想的加入可以極大的提升開發的速度,使得代碼邏輯更加合理,對于代碼優化提升有着很大的幫助。