怎样管理你的对象

有一天晚上我脑海中忽然冒出来一个问题:“怎样管理咱们代码中的对象”。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;
            }
        }
    }
}

我说完后,你们又一次陷入了沉思,我知道你们都在思考,他们会在每一次思考中得到进步和成长,正如我在思考后获得成长同样。

小鸟总有一天会成长为老鸟,我还走在成长的路上。

相关文章
相关标签/搜索