android针对多种请求封装统一进出口

本文介绍了一种针对多个酒店系统的API请求封装方案,通过抽象接口、动态代理等技术手段,实现了不同类型酒店系统的接入,有效降低了代码耦合度,提高了系统的可扩展性和可维护性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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.设想一下理想状态

  1. 每套酒店系统的请求都是独立的,互不影响
  2. 增加新的酒店系统不会影响到现有的代码
  3. 酒店系统中增添新的请求方法不会影响现有的代码
  4. 请求方法的返回值可以自定义

根据以上的需求,我们先写几句伪代码来实现

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.具体代码实现

  1. 我们先来看看Pms.getService(Class clz)的实现方式
  2. 首先定义一个叫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.总结

随着开发经验的增长,我们不应该满足于业务的实现,空闲的时候多多考虑一下如何将设计模式更好的运用到代码中。虽然可能会造成类数量的急剧增加,原本简单的几个类就能实现的业务现在要几十个类,但慢慢地会发现这是值得的,随着业务的复杂性越来越大,我们就更需要写出一些高类聚,低耦合的代码,对于信心的提升也是非常大的。当然了,我本身的水平也不咋的,欢迎大家指正。

后面写的有点懒了,具体的请看项目源码地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值