SPI機制與策略模式

Google愛喝茶發表於2018-09-14

這篇文章由三個問題貫穿分別是
一、啥是SPI?
二、啥是策略模式?
三、SPI和策略模式有啥關係?

一、啥是SPI?

入職初期經常聽大佬說SPI,也不知道是幹啥的,最近終於有所領悟。
Java程式設計師應該對『面向介面』程式設計不陌生(如果陌生說明你對物件導向理解的還不夠透徹),我們要說的SPI和麵向介面程式設計是緊密關聯的。

簡單地講,如果一個介面由呼叫方來定義,而介面的實現由提供方來實現,這個就是SPI。而如果介面的定義和實現都由提供方來完成,就是我們常說的API。 這樣說可能還是比較抽象,不如來看一個例子。
src.zip/rt-jar內定義了一個介面Driver,在這個例子中src包扮演的角色是呼叫方

public interface Driver {
    Connection connect(String url, java.util.Properties info)
        throws SQLException;
        ...省略
}
複製程式碼

既然src定義了介面,那麼他肯定要用啊,具體細節忽略我們只關心下面這行程式碼

public class DriverManager { 
    ...
     ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
     ....
      try{
            while(driversIterator.hasNext()) {
                driversIterator.next();
            }
        } catch(Throwable t) {
        // Do nothing
        }
}
複製程式碼

小結:

  • 呼叫方提供了介面Driver
  • 呼叫方面向Driver介面進行了程式設計

下面看看提供方做了啥
首先介面提供方mysql-connector-java登場,我們重點關注兩個地方
1)介面的具體實現

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }
    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}
複製程式碼

2)名為java.sql.Driver的配置檔案

com.mysql.jdbc.Driver
複製程式碼

小結:

  • 實現了src中定義的介面
  • 定義了一個不知道幹啥的配置檔案

我們再回到方法呼叫方src中(注意src是需要引入mysql-connector-java這個包的),有這樣一行程式碼 ServiceLoader.load(Driver.class);
ServiceLoader讀取mysql-connector-java包中的那個配置檔案中定義的實現類的名字,通過反射獲取對應類例項~
這樣我們的介面就被指定具體的實現了~接下來我們來看策略模式

二、啥是策略模式?

在閻巨集博士的《JAVA與模式》一書中開頭是這樣描述策略(Strategy)模式的:
策略模式屬於物件的行為模式。其用意是針對一組演算法,將每一個演算法封裝到具有共同介面的獨立的類中,從而使得它們可以相互替換。策略模式使得演算法可以在不影響到客戶端的情況下發生變化。
我們還是通過一個例子來看

interface Calculator {
	int doExecute(int a, int b);
}
class AddCalculator implements Calculator{
	public int doExecute(int a, int b){
		return a+b;
	}
}
class MinusCalculator implements Calculator{
	public int doExecute(int a, int b){
		return a-b;
	}
}
public class Test {
	Calculator cal;
	public void setCal(Calculator cal){
		this.cal = cal;
	}
	public void doCalculate(int a, int b){
		cal.doExecute(a, b);
	}
	public static void main(String[] args) {
		Test t = new Test();
		t.setCal(new AddCalculator());
		t.doCalculator(1,2);
		t.setCal(new MinusCalculator());
		t.doCalculator(2,1);
	}
}
複製程式碼

在Test類的doCalculate方法中並不關心介面的具體實現,這就是一『面向介面』程式設計的例子,我們看到介面的具體實現在main方法也就是介面的呼叫處來指定實現,這個思想和IOC的思想又是類似的~
那麼看我們的最後一個問題,SPI和策略模式有啥關係

三、SPI和策略模式有啥關係?

聰明的小夥伴應該已經看出端倪
SPI模式和策略模式都是面向介面程式設計的典範,而且介面的具體實現由呼叫方來指定。策略模式的例子如上,對於第一個SPI例子來說,假如我們在開發一個java專案,使用的資料庫是oracle,那麼我們引入oracle的jar包即可,顯然該jar包中一定包含一個Driver的實現類以及執行實現類的配置檔案,這個時候我們的java程式就可以『無痕』地使用oracle提供的Driver類了
這裡再多說一句,所有的java專案都會依賴src包的~

相關文章