控制反轉(Inversion of Control,英文縮寫為IoC)是架構的重要特徵,並非物件導向程式設計的專用術語。它與依賴注入(Dependency Injection,簡稱DI)和依賴查找(Dependency Lookup)並沒有關係。
使用過Spring的開發者應該都對IOC控制反轉功能有所了解,最開始學習時應該都知道使用依賴注入來實現IOC的功能,本文來介紹使用IOC控制反轉思想的幾種設計模式。
依賴注入來實作IOC
注入依賴是IOC最基本的實作方式,也是最常用的一種物件導向設計方式之一。注入依賴如何達到控制反轉效果,先以一個例子開始:
public interface UserQueue {void add(User user);void remove(User user); User get(); }public abstract class AbstractUserQueue implements UserQueue {protected LinkedList<User> queue = new LinkedList<>(); @Overridepublic void add(User user) { queue.addFirst(user); } @Overridepublic void remove(User user) { queue.remove(user); } @Overridepublic abstract User get(); }public class UserFifoQueue extends AbstractUserQueue {public User get() {return queue.getLast(); } }public class UserLifoQueue extends AbstractUserQueue {public User get() {return queue.getFirst(); } }
#UserQueue
介面定義了公共的方法,用於在一個佇列中去存放User物件。 AbstractUserQueue則是為後續的繼承類,提供了一些公用的方法實作。最後的UserFifoQueue
和 UserLifoQueue,則分別實作了FIFO 和 LIFO 佇列。
這是實作子類別多態性的一種有效方式。
透過建立一個依賴UserQueue抽象類型(也稱為DI術語中的服務)的客戶端類,可以在執行時間注入不同的實現,而無需重構使用客戶端類別的程式碼:
public class UserProcessor {private UserQueue userQueue;public UserProcessor(UserQueue userQueue) {this.userQueue = userQueue; }public void process() {// process queued users here } }
UserProcessor展示了依賴注入確實是IOC的一種方式。
我們可以透過一些硬編碼方式 如 new 操作,直接在建構函數中實例化在UserProcessor中取得對佇列的依賴關係。但這是典型的程式碼硬編程,它引入了客戶端類別與其依賴關係之間的強耦合,並大大降低了可測性。
該類別在建構函式中宣告對抽象類別 UserQueue
的相依性。也就是說,依賴關係不再透過在建構函式中使用 new 操作, 相反,透過外部注入的方式,要麼使用依賴注入框架,要麼使用factory或builders模式。
使用依賴注入,客戶端類別的依賴關係的控制,不再位於這些類別中;而是在註入器中進行,看如下程式碼:
public static void main(String[] args) { UserFifoQueue fifoQueue = new UserFifoQueue(); fifoQueue.add(new User("user1")); fifoQueue.add(new User("user2")); fifoQueue.add(new User("user3")); UserProcessor userProcessor = new UserProcessor(fifoQueue); userProcessor.process(); }
#上述方式達到了預期效果,而且對UserLifoQueue的注入也簡單明了。
直接透過觀察者模式實現IOC,也是一種常見的直覺方式。廣義上講,透過觀察者實現IOC,觀察者模式通常用於在模型視圖的上下文中,追蹤模型物件的狀態的變化。
在一個典型的實作中,一到多個觀察者綁定到可觀察對象(也稱為模式術語中的主題),例如透過呼叫addObserver方法進行綁定。一旦定義了被觀察者和觀察者之間的綁定,則被觀察者狀態的變化都會觸發呼叫觀察者的操作。看下面範例:
public interface SubjectObserver {void update(); }
值改變時,會觸發呼叫上述這個很簡單的觀察者。在真實情況下,通常會提供功能更豐富的API,如需要保存變化的實例,或者新舊值,但是這些都不需要觀察action(行為)模式,所以這裡舉例盡量簡單。
下面,給一個被觀察者類別:
public class User {private String name;private List<SubjectObserver> observers = new ArrayList<>();public User(String name) {this.name = name; }public void setName(String name) {this.name = name; notifyObservers(); }public String getName() {return name; }public void addObserver(SubjectObserver observer) { observers.add(observer); }public void deleteObserver(SubjectObserver observer) { observers.remove(observer); }private void notifyObservers(){ observers.stream().forEach(observer -> observer.update()); } }
User類別中,當透過setter方法變更其狀態事,都會觸發呼叫綁定到它的觀察者。
使用主題觀察者和主題,以下是實例給出了觀察方式:
public static void main(String[] args) { User user = new User("John"); user.addObserver(() -> System.out.println("Observable subject " + user + " has changed its state.")); user.setName("Jack"); }
每當User物件的狀態透過setter方法進行修改時,觀察者將被通知並向控制台列印出一條訊息。到目前為止,給出了觀察者模式的一個簡單用例。不過,透過這個看似簡單的用例,我們了解到在這種情況下控制是如何實現反轉的。
觀察者模式下,主題就是起到」框架層「的作用,它完全主導何時何地去觸發誰的調用。觀察者的主動權被外放,因為觀察者無法主導自己何時被調用(只要它們已經被註冊到某個主題中的話)。這意味著,實際上我們可以發現控制被反轉的」事發地「— 當觀察者綁定到主題時:
user.addObserver(() -> System.out.println("Observable subject " + user + " has changed its state."));
上述用例,简要说明了为什么观察者模式是实现IoC的一种非常简单的方式。正是以这种分散式设计软件组件的形式,使得控制得以发生反转。
模板方法模式实现的思想是在一个基类中通过几个抽象方法来定义一个通用的算法,然后让子类提供具体的实现,这样保证算法结构不变。
我们可以应用这个思想,定义一个通用的算法来处理领域实体,看例子:
public abstract class EntityProcessor {public final void processEntity() { getEntityData(); createEntity(); validateEntity(); persistEntity(); }protected abstract void getEntityData();protected abstract void createEntity();protected abstract void validateEntity();protected abstract void persistEntity(); }
processEntity()
方法是个模板方法,它定义了处理实体的算法,而抽象方法代表了算法的步骤,它们必须在子类中实现。通过多次继承 EntityProcessor 并实现不同的抽象方法,可以实现若干算法版本。
虽然这说清楚了模板方法模式背后的动机,但人们可能想知道为什么这是 IOC 的模式。
典型的继承中,子类调用基类中定义的方法。而这种模式下,相对真实的情况是:子类实现的方法(算法步骤)被基类的模板方法调用。因此,控制实际是在基类中进行的,而不是在子类中。
总结:
依赖注入:从客户端获得依赖关系的控制不再存在于这些类中。它存由底层的注入器 / DI 框架来处理。
观察者模式:当主体发生变化时,控制从观察者传递到主体。
模板方法模式:控制发生在定义模板方法的基类中,而不是实现算法步骤的子类中。
以上是IOC控制反轉實例詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!