怎樣管理你的對象

有一天晚上我腦海中忽然冒出來一個問題:「怎樣管理咱們代碼中的對象」。java

小弈是剛工做時的我,他說:經過 new 來建立一個對象而後直接使用就行了啊。算法

public class HelloWorld {
	public void hello() {
		System.out.println("hello world!");
	}
}
HelloWorld helloWorld = new HelloWorld();
helloWorld.hello();

大家看,我有一個 HelloWorld 類,我用 new 就能直接建立一個對象,而後就能使用這個對象中全部的方法了,多簡單啊。ide

二弈是工做兩年的我,他一臉鄙視的對小弈說,你別成天 HelloWorld 好很差,還有啊,除了 new 你就不會其餘的了,能不能有點追求啊?函數

小弈對二弈說那你說除了 new 還有什麼辦法啊?工具

二弈說能夠經過 Class 的 newInstance 或者 Constructor 的 newInstance 來建立對象實例啊。this

不過你得記住,Class 的 newInstance 只能對那些擁有可見的(Accessible)無參構造函數的類,才能進行對象的實例化,而 Constructor 就沒有這些限制。.net

大弈是工做三年的我,他說,雖然大家的方法均可以用來建立對象,但都仍是手動建立的,太原始了,生產力過低。code

工欲善其事,必先利其器,咱們也得找個高效的生產力工具。IOC 容器大家瞭解吧?對象

之前咱們在一個對象中若是要調用另一個對象的方法時,都是經過 new 或者反射來手動建立該對象,可是每次都這樣作太累了,而且類之間的耦合也很高。繼承

經過 IOC 容器,咱們能夠把全部的對象交給容器來管理,在使用以前只須要定義一下對象,而後再使用到該對象時,IOC 容器就會幫咱們把該對象初始化好,這樣是否是更方便呢?

大弈說完,舉了一個例子:

@Bean
public class RegisterService {
	public void register() {
		// do register
	}
}
@Bean
public class LoginService {
	public void login() {
		// do login
	}
}
@Bean
public class HelloWorld {
	@Autowired
	private RegisterService registerService;
	@Autowired
	private LoginService loginService;
	public void hello() {
		// 註冊
		registerService.register();
		// ...
		// 登陸
		loginService.login();
	}
}

IOC 容器經過一種叫 Bean 的註解,在系統啓動時掃描全部經過 Bean 標註的類,對這些類進行實例化,而後將全部的對象都保存在容器中。再掃描全部經過 Autowired 標註的屬性或者方法,從容器中找到與之匹配(經過名稱或者類型等)的對象將具體的對象賦值給這些屬性。這樣咱們就能夠直接將這些對象拿來使用了,做爲一個伸手黨是否是很幸福啊。

老弈是工做五年的我,他聽了大弈的話後,提出了一個問題,對於新的項目可使用這種 IOC 的容器,但是對於那些遺留的老項目來講,要使用 IOC 來改造是不太符合實情的。

我舉個例子,在一個遺留的老項目中,有一個核心的接口 Handler:

public interface Handler<REQ, RES> {
    RES handle(REQ request);
}

Handler 接口有不少的實現類,咱們須要對不一樣的請求來調用不一樣的 Handler 實現類進行處理,若是用 IOC 容器來管理這些實現類,顯然不太合適,由於咱們處理以前是不知道該用哪一個 Handler 實現類的。

大弈想了想,若是 Handler 接口只有幾個固定的實現類,而且在使用時只會使用一個來進行處理,那麼卻是能夠在啓動前經過配置的方式來肯定具體使用哪一種 Handler ,好比能夠經過 @Conditional 根據某些條件來肯定加載具體的對象,可是這種要在使用時才能肯定 Handler 對象的類型確實比較棘手。

老弈看你們都不說話了,就繼續說了下去。

爲了要在調用方法時使用不一樣的 Handler 來處理不一樣的而請求,須要肯定兩種類,一種是請求類,一種是處理類,而且要讓請求類和處理類一一對應起來。

假設咱們的請求類是一個 Packet 類,每個具體的請求類都繼承自這個基類。

那麼想要肯定每個具體的 Packet 是什麼類型的,能夠有不少種方法,能夠爲每一個 Packet 取一個惟一的名字,例如:

public abstract class Packet {
    public abstract String name();
}

也能夠爲每個 Packet 指定一個標誌,例如:

public abstract class Packet {
    public abstract int symbol();
}

可是無論哪一種方式,每個 Packet 的實現類都須要實現抽象類中的方法,來「標誌」本身是哪一種 Packet。

咱們以第二種方式舉例,假設咱們有兩個具體的 Packet:

public class RegisterPacket extends Packet {
	// 註冊所須要的其餘參數
	int symbol() {
		return 1;
	}
}
public class LoginPacket extends Packet {
	// 登陸所須要的其餘參數
	int symbol() {
		return 2;
	}
}

這樣當咱們接收到 request 對象時,經過調用 request.symbol() 就知道這個 request 是哪一種類型的 Packet 了,這時只要找到具體的 Handler 實現類來處理就能夠了。

那請求類已經能夠肯定了,怎樣肯定 Handler 處理類呢?咱們是否也能夠在 Handler 接口中定義一個 symbol 方法呢,像這樣:

public interface Handler<REQ, RES> {
    int symbol();
    RES handle(REQ request);
}

這樣的話,只要在全部的實現類中實現 symbol 方法來標註該 Handler 是用來處理何種 request 的便可。

public RegisterHandler implements Handler<RegisterPacket, RES> {
    int symbol(){
    	return 1;
    }
    RES handle(RegisterPacket request){
    	// 具體的處理方法
    }
}
public LoginHandler implements Handler<LoginPacket, RES> {
    int symbol(){
    	return 2;
    }
    RES handle(LoginPacket request){
    	// 具體的處理方法
    }
}

最後把全部的 Handler 實現類都實例化後保存在一個 HandlerProvider 中,要使用時再到 HandlerProvider 中來獲取便可:

public interface HandlerProvider {
    Handler getHandler(int symbol);
}

那怎樣獲取到全部的 Handler 的實現類呢,有兩種方法。

一種是經過 ServiceLoader.load(Handler.class) 的方式來獲取,不過這種經過 spi 的方式須要在項目的 resources/META-INF/services/ 目錄下建立一個 xxx.Handler 的文件,並在文件中將全部 Handler 的實現類的徹底類限定符列出來。

另外一種比較簡單的方式是經過掃描的方式,獲取到全部 Handler 的實現類。

到如今爲止,咱們的實現還算能夠,可是有一個問題,那就是在 Handler 接口中咱們增長了一個方法,這樣作就對原來的代碼進行了侵入。

爲了讓原來的代碼保持不變,咱們能夠定義一個註解來標註在全部的 Handler 實現類上,好比這樣:

@Symbol(1)
public RegisterHandler implements Handler<RegisterPacket, RES> {
    RES handle(RegisterPacket request){
    	// 具體的處理方法
    }
}
@Symbol(2)
public LoginHandler implements Handler<LoginPacket, RES> {
    RES handle(LoginPacket request){
    	// 具體的處理方法
    }
}

這樣就將 Handler 的實現和標註進行了解耦了,也能夠經過掃描 @Symbol 註解來獲取到全部的 Handler 實現類,不過這樣作的缺點就是假如我忘記對某個 Handler 實現類添加 @Symbol 註解,到時候就獲取不到該 Handler 了。

你們聽完老弈的話以後,都陷入了沉思,我靠,還能夠這麼玩,真有趣。

這時候如今的我,也就是逅弈,說了一句,若是我有一個接口,他只有幾個固定的實現類,我不想搞那一套那麼重的實現方式,可是我也須要動態的獲取實現類來對請求進行處理,那我該怎麼辦呢?

好比我有一個序列化的接口,以下所示:

public interface Serializer {
    byte[] serialize(Packet packet);
}

而後只有五種具體的序列化的實現類,以下所示:

public class JdkSerializer implements Serializer {
	@Override
    public byte[] serialize(Packet packet) {
    	// 具體的序列化操做
    }
}
public class FastJsonSerializer implements Serializer {
	@Override
    public byte[] serialize(Packet packet) {
    	// 具體的序列化操做
    }
}
public class HessianSerializer implements Serializer {
	@Override
    public byte[] serialize(Packet packet) {
    	// 具體的序列化操做
    }
}
public class KryoSerializer implements Serializer {
	@Override
    public byte[] serialize(Packet packet) {
    	// 具體的序列化操做
    }
}
public class ProtoStuffSerializer implements Serializer {
	@Override
    public byte[] serialize(Packet packet) {
    	// 具體的序列化操做
    }
}

那麼咱們該怎麼肯定使用哪一種序列化方式對參數 packet 進行序列化呢?

使用老弈剛剛說的那一套也確實可以實現,不過太麻煩了,又得對 Packet 定義 symbol,又得對 Hander 實現類進行標註,還得掃描全部的實現類。

我只有五個實現類,不須要搞那麼麻煩的。

其實很簡單,只須要定義一個枚舉類,表示序列化的算法,而後對 Packet 增長一個 algorithm 方法用來表示,使用何種序列化算法,以下所示:

public enum SerializeAlgorithm {
    JDK((byte) 1),
    FAST_JSON((byte) 2),
    HESSIAN((byte) 3),
    KRYO((byte) 4),
    PROTO_STUFF((byte) 5);
    private byte type;
    SerializeAlgorithm(byte type) {
        this.type = type;
    }
}
public abstract class Packet implements Serializable {
	public abstract byte algorithm();
}

而後定義一個 SerializerChooser 根據不一樣的算法選擇不一樣的 Serializer 實現類便可:

public interface SerializerChooser {
    Serializer choose(byte algorithm);
}

由於根據算法是能夠知道對應的序列化接口的,因此就沒有必要去掃描了,直接把幾種序列化的實現類枚舉出來便可,對象的實例可使用單例模式,以下所示:

public class DefaultSerializerChooser implements SerializerChooser {
    private DefaultSerializerChooser() {

    }
    public static SerializerChooser getInstance() {
        return Singleton.get(DefaultSerializerChooser.class);
    }
    @Override
    public Serializer choose(byte algorithm) {
        SerializeAlgorithm serializeAlgorithm = SerializeAlgorithm.getEnum(algorithm);
        switch (serializeAlgorithm) {
            case JDK: {
                return Singleton.get(JdkSerializer.class);
            }
            case FAST_JSON: {
                return Singleton.get(FastJsonSerializer.class);
            }
            case HESSIAN: {
                return Singleton.get(HessianSerializer.class);
            }
            case KRYO: {
                return Singleton.get(KryoSerializer.class);
            }
            case PROTO_STUFF: {
                return Singleton.get(ProtoStuffSerializer.class);
            }
            default: {
                return null;
            }
        }
    }
}

我說完後,你們又一次陷入了沉思,我知道你們都在思考,他們會在每一次思考中得到進步和成長,正如我在思考後獲得成長同樣。

小鳥總有一天會成長爲老鳥,我還走在成長的路上。

相關文章
相關標籤/搜索