1.需求分析
- 公司的项目需要对接多套酒店系统,x5,xms,绿云。。。加起来差不多有6-7个。按照最常规的思维,比如我现在需要获取入住客人的信息,可能就需要进行大量的if else判断
-
int hotelType = 0; if (hotelType == 0) { //x5 request } else if (hotelType == 1) { //xms request } else if (hotelType == 2) { //绿云request }
随着系统类型和系统类方法的增加,后续的代码会变得非常难以维护,耦合性太高,代码太冗余
2.设想一下理想状态
- 每套酒店系统的请求都是独立的,互不影响
- 增加新的酒店系统不会影响到现有的代码
- 酒店系统中增添新的请求方法不会影响现有的代码
- 请求方法的返回值可以自定义
根据以上的需求,我们先写几句伪代码来实现
Pms.getService(GuestService.class).getGuestInfo().setCallback(new ICallback<String, String>() {
@Override
public void onSuccess(String result) {
System.out.println(result);
}
@Override
public void onFailure(String error) {
System.out.println(error);
}
});
我们先分析一下上面的这一段代码
- Pms是一个统一管理的类,里面有一个getService方法,方法的参数是一个class。阅读过Retrofit源码的可能会比较熟悉这一步,其实内部的实现就是通过动态代理获取到相应的对象。
- 按照面向对象的思维,这个class应该是一个interface class,interface中会定义好一大堆和“客人”相关的方法,其中有一个就叫getGuestInfo
- 而且getGuestInfo方法应该是有一个返回值的,因为这样才能继续调用后面的setCallback(接口中其他的方法也应该是这个返回值)
- 数据的回调是通过ICallback<R, E>来实现,ICallback中定义了两个泛型"R","E"。其中R表示response,即正确请求的返回值,E表示Error,即请求错误的返回值。因为涉及到多套不同的系统,请求方式和返回值都可能存在不同,所以这里用泛型来定义
3.具体代码实现
- 我们先来看看Pms.getService(Class clz)的实现方式
- 首先定义一个叫GuestService的接口,并继承了BaseService(这个接口只是为了做到统一管理,并没有任何代码)。我们之前提到了要想继续调用setCallback,getGuestInfo就需要有一个返回值,这里我们定义为AbsCallback,关于这个类我们后续再讲
public interface GuestService extends BaseService {
AbsCallback getGuestInfo();
}
Pms这个管理类比较简单,我们只是说一下getService这个方法,这里同样用到了泛型,因为系统是不知道client想要获取的Service类型。而真正的获取service对象的操作又放到了ServiceContainer中
public class Pms {
/**
* 这才是真正获取service对象的地方
*/
private static ServiceContainer serviceContainer = new ServiceContainer();
/**
* 系统类型
*/
public static int PMS_TYPE;
//各种不同系统的标识
public static final int PMS_TRANS = 0;
public static final int PMS_XMS = 1;
public static final int PMS_X5 = 2;
.........
//标识是否已经初始化
private static boolean isInitialized = false;
//限定系统类型的取值范围
@IntDef({PMS_TRANS, PMS_XMS, PMS_X5, PMS_X51, PMS_DEMO, PMS_LUYUN, PMS_LUYUN1})
@Retention(RetentionPolicy.SOURCE)
public @interface PmsType {
}
/**
* init parameter, this function should be called first, otherwise the following calls can be wrong
*
* @param PMS_TYPE
*/
public static void init(@PmsType int PMS_TYPE) {
Pms.PMS_TYPE = PMS_TYPE;
isInitialized = true;
}
public static <T> T getService(Class<T> clz) {
if (!isInitialized)
throw new RuntimeException("pms request should be initialized first");
return serviceContainer.getService(clz);
}
}
我们再来看一下ServiceContainer的代码,这里主要是用到了动态代理,不熟悉的同学可以去了解一下这方面的知识
这里我们定义了一个容器来装载Service,防止对象的重复创建。并规定传入的class只能是Interface,然后我们再通过这个class来获取到Interface的真实对象。这里我自定义了个InvocationHandler,并在它的回调函数中继续我们的逻辑操作。
public class ServiceContainer {
private Map<Class, BaseService> serviceMap = new LinkedHashMap<>();
public synchronized <T> T getService(Class<T> clz) {
if (!clz.isInterface())
throw new IllegalArgumentException("must be a interface");
Object service = serviceMap.get(clz);
if (service == null) {
service = Proxy.newProxyInstance(clz.getClassLoader(), new Class[]{clz}, new MyInvocationHandler());
if (service instanceof BaseService)
serviceMap.put(clz, (BaseService) service);
}
return (T) service;
}
}
MyInvocationHandler的代码
需要说明的是:
- 通过动态代理获取到的对象每次调用它本身的函数都会回调到invoke,比如我们上面调用的Pms.getService(GuestService.class).getGuestInfo()。在invoke函数中我们可以拿到getGuestInfo方法的所有信息以及它所属类的信息。
- 这里我们使用一个Holder来装载回调函数中所有的信息,并标识这个方法的返回值是否是AbsCallback,如果hasCallback为true就表示有回调。
- 拿到方法的所有信息后我们去ServiceImpContainer执行真正的调用操作
public class MyInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
//标识这个方法的返回值是否在AbsCallback或者AbsCallback的子类
boolean hasCallback = method.getReturnType().isAssignableFrom(AbsCallback.class);
Holder holder = HolderFactory.produce(proxy, method, args, hasCallback);
return ServiceImpContainer.getInstance().invoke(holder);
}
}
- invoke回调的响应是因为service接口调用了响应的方法,但是我们都知道接口没有一个实现类是毫无意义的。所以每个service接口都应该有一个对应的实现类,这里我们定义GuestInfoService的实现类是GuestInfoImp
- 我们先将getGuestInfo的返回值用null来替代,因为真正的逻辑还没有执行,不清楚返回值到底是啥。
public class GuestServiceImp extends ServicePresenter implements GuestService {
@Override
public AbsCallback getGuestInfo() {
PmsRequestManager requestManager = new PmsRequestManager();
requestManager.setPmsRequest(getPmsRequest());
requestManager.getGuestInfo(getHolder());
return null;
}
}
ServiceImpContainer的代码
- 这里同样使用到一个容器,以Service为key,value是对应这个Service的实现类
- 在类初始化的时候我们先在容器中添加我们所有的Service,比如我这里包含了Guest,Room,Minibar三个模块
- 这个类最重要的就是invoke函数了,具体请看注释,写的都比较详细
public class ServiceImpContainer {
private Map<Class<?>, Class<?>> serviceContainer = new HashMap<>();
private static ServiceImpContainer mInstance;
private ServiceImpContainer() {
serviceContainer.put(GuestService.class, GuestServiceImp.class);
serviceContainer.put(RoomService.class, RoomServiceImp.class);
serviceContainer.put(MinibarService.class, MinibarServiceImp.class);
}
public static ServiceImpContainer getInstance() {
if (mInstance == null)
mInstance = new ServiceImpContainer();
return mInstance;
}
/**
*通过调用该方法,实际上我们调用了Service实现类的方
*/
public Object invoke(Holder holder) {
//通过method获取到所属的接口,再获取对应的接口实现类
Class<?> aClass = serviceContainer.get(holder.getMethod().getDeclaringClass());
if (aClass == null)
return null;
try {
//获取到接口实现类的对象
Object instance = aClass.newInstance();
//判断该方法的返回值是否为AbsCallback
if (holder.isHasCallback()) {
//为true的情况我们就new一个AbsCallback,并将holder传进去。由于我们之前在实现类中直接返回了null,这里可以进行覆盖操作,实际调用的时候返回值依然是AbsCallback
AbsCallback callback = new AbsCallback(holder);
//这里同样是一个map容器,用来存储callback,因为holder通过上一句代码实际上已经传给了接口的实现类,在必要的时候我们需要通过holder来拿相应的callback
CallbackContainer.put(holder, callback);
//通过反射调用这个方法
holder.getMethod().invoke(instance, holder.getArgs());
return callback;
} else {
//如果没有回调就直接反射调用该方法
return holder.getMethod().invoke(instance, holder.getArgs());
}
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
AbsCallback的代码
- 我们的Service实现类是继承了ServicePresenter,而ServicePresenter又继承了AbsCallback,说到底Service接口的实现类是AbsCallback的一个子类
- 在ServiceImpContainer的invoke方法中我们new了一个AbsCallback,并传入了holder,但是我们的实现类如果直接去访问父类的holder肯定是null,因为AbsCallback在创建的时候并没有指定子类,这里不涉及到多态。所以我们用一个ThreadLocal来保存Holder
- 到这里为止,实际上我们已经成功地调用了Service接口对应地实现类,如果getGuestInfo方法不涉及到回调,那么是已经完工了
public class AbsCallback {
private ICallback callback;
private static ThreadLocal<Holder> threadLocal = new ThreadLocal<>();
protected AbsCallback() {
}
protected static Holder getHolder() {
return threadLocal.get();
}
public AbsCallback(Holder holder) {
threadLocal.set(holder);
}
public <R, E> void setCallback(ICallback<R, E> callback) {
this.callback = callback;
}
public <R, E> ICallback<R, E> getCallback() {
return callback;
}
}
3.回调的实现
在需求分析中我们已经提到了会有多套系统,为了避免if else的出现,我们将每个系统的具体请求隔离开来,并使用策略模式进行访问
每个系统的Request都实现了IPmsRequest接口,外部进行访问的时候直接调用PmsRequestManger相应的方法进行访问
PmsRequestManger.java
package com.jack.pms.pms1.request.strategy;
import android.support.annotation.NonNull;
import com.jack.pms.pms1.bean.Holder;
public class PmsRequestManager {
private IPmsRequest pmsRequest;
public void setPmsRequest(@NonNull IPmsRequest pmsRequest) {
this.pmsRequest = pmsRequest;
}
public void getGuestInfo(Holder holder) {
pmsRequest.getGuestInfo(holder);
}
public void getMinibarItemList(Holder holder) {
pmsRequest.getMinibarItemList(holder);
}
public void recordMinibarItems(Holder holder) {
pmsRequest.recordMinibarItems(holder);
}
public void getRoomAccountList(Holder holder) {
pmsRequest.getRoomAccountList(holder);
}
public void getRoomList(Holder holder) {
pmsRequest.getRoomList(holder);
}
}
再贴出一个具体的请求类,拿PmsX5Request.java来举例说明,其它几个请求类的实现都是大相径庭,只需要替换具体的实现就行
package com.jack.pms.pms1.request;
import com.jack.pms.pms1.bean.Holder;
import com.jack.pms.pms1.callback.ICallback;
import com.jack.pms.pms1.container.CallbackContainer;
import com.jack.pms.pms1.request.strategy.IPmsRequest;
public class PmsX5Request implements IPmsRequest {
@Override
public void getGuestInfo(final Holder holder) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
//通过holder获取到AbsCallback,再拿到ICallback
ICallback<String, String> callback = CallbackContainer.get(holder).getCallback();
if (callback == null) {
return;
}
if (Math.random() > 0.3) {
callback.onSuccess("success");
} else {
callback.onFailure("failure");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
@Override
public void getMinibarItemList(Holder holder) {
}
@Override
public void recordMinibarItems(Holder holder) {
}
@Override
public void getRoomAccountList(Holder holder) {
}
@Override
public void getRoomList(final Holder holder) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
ICallback<String, String> callback = CallbackContainer.get(holder).getCallback();
if (callback == null) {
return;
}
if (Math.random() > 0.3) {
callback.onSuccess("success");
} else {
callback.onFailure("failure");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
4.总结
随着开发经验的增长,我们不应该满足于业务的实现,空闲的时候多多考虑一下如何将设计模式更好的运用到代码中。虽然可能会造成类数量的急剧增加,原本简单的几个类就能实现的业务现在要几十个类,但慢慢地会发现这是值得的,随着业务的复杂性越来越大,我们就更需要写出一些高类聚,低耦合的代码,对于信心的提升也是非常大的。当然了,我本身的水平也不咋的,欢迎大家指正。
后面写的有点懒了,具体的请看项目源码地址