巧用接口解耦分离实现
来源:互联网 发布:spring源码视频 编辑:程序博客网 时间:2024/05/29 05:56
前几天爱哥在群里跟群里的各位大大讲过一个很简单但是很实用的架构小例子,就是利用接口简单地将我们的实现分离出去,以应对项目中不同的变化,这个架构的例子非常简单,只要你了解接口、一眼就能看懂,不过在此之前大家要明白一点的是很多情况下我们的架构都是依赖于某个具体的业务模型,如果这个业务模型变更了那么我们架构也有可能就不再适用,这里很多朋友可能会打爱哥脸,BullShit!简直鬼扯,架构不应该就是为了解决多变的业务逻辑才存在的吗!!是的没错,架构是应该为了解决多变的业务逻辑而存在,但是,如果我们的整个业务模型都变了呢?打个不恰当的比方,人可以穿不同的衣服来表现自己不同的个性,这是可以的,衣服就像那多变的业务逻辑,而人是应用于这套业务逻辑的模型,如果突然人变成了狗,你那些漂亮衣服还有个JB毛用啊!!!废话少说,我们先来看看这个例子,这个例子呢是一个网络请求接口的封装例子,再说这个例子前我先跟大家说说服务器返回的JSON结构,JSON的结构是下面这个样子的:
也就是说我们的基本JSON结构里有四个字段:code、data、msg、time,其中code、msg和time是每个响应JSON的基本字段,而data字段里的数据就是我们根据不同接口请求所返回的数据,这里code表示响应码,当且仅当code==200时data里才会有数据,而msg呢则表示一些额外的附加信息,如果code==200,msg不会有任何信息,否则msg会填充相应的信息说明为什么请求不成功,最后的time则表示请求响应成功的时间戳。OK,有了这么一个JSON结构,我们就可以在此结构上封装我们的网路请求框架,首先,我们根据JSON的结构新建一个响应的实体类:
* 网络请求数据响应包装类 * * @author AigeStudio{@link http://aigestudio.com/?p=100} * @since 2016-08-15 */public final class Response<T> { public T mData;// 数据实体 public String mResponseMsg;// 响应消息文本 在mResponseCode不等于200的情况下该字段才会有数据 public long mResponseTimeStamp;// 响应请求的时间戳 public int mResponseCode;// 具体的相应码 200表示请求并响应成功 该值由服务器规定}这个类很简单,基本与我们的JSON结构对应,在这个例子中,爱哥只演示两个接口的封装:登录与版本更新,其他的大家可以自行尝试练手玩,对于登录和版本更新,我们也应该根据服务器返回的JSON结构建立不同的数据实体类,假设我的登录JSON是酱紫的:
/** * 登录信息数据实体类 * * @author AigeStudio{@link http://aigestudio.com/?p=100} * @since 2016-08-15 */public class LoginInfo { public String mUser;// 用户名 public String mPassword;// 用户密码 public String mName;// 用户昵称 public String mAvatarUrl;// 用户头像地址 public String mSex;// 用户性别 public String mBirthday;// 用户生日 public long mRegisterTimeStamp;// 用户注册时的时间戳 public long mLastLoginTimeStamp;// 用户上一次登陆的时间戳 public int mAge;// 用户年龄}
假设我的登录JSON是酱紫的:
/** * 应用更新信息数据实体类 * * @author AigeStudio{@link http://aigestudio.com/?p=100} * @since 2016-08-15 */public class UpgradeInfo { public String mVersionName;// 版本名 public String mDescription;// 版本描述 public String mDownloadUrl;// 安装包下载地址 public int mVersionCode;// 版本号 public boolean isForce;// 是否强制更新}
现在我们的数据实体类定义完了,是时候来看看业务逻辑了,本文的标题是“巧用接口解耦分离实现”,这里我们的网络请求接口方法只有两个:登录和版本更新,我们先定义一个接口来封装这两个方法的声明:
/** * 网络请求方法接口 * * @author AigeStudio{@link http://aigestudio.com/?p=100} * @since 2016-08-15 */interface IApi { /** * 用户登录 * * @param user 用户账户 * @param password 用户密码 * @param c 数据请求完成后回调接口 */ void login(String user, String password, CallBack<LoginInfo> c); /** * 应用更新 * * @param code 版本号 * @param c 数据请求完成后回调接口 */ void upgrade(String code, CallBack<UpgradeInfo> c);}一个登陆方法和一个版本更新方法,对登陆方法而言,我们需要传入两个必要参数:用户名和密码,而对于版本更新而言,我们需要传入一个当前版本的版本号,用于和服务器的最新版本做对比,这些呢都是我们业务模型所决定的,跟代码逻辑无关,但是我们的架构得跟着这些业务模型变化。大家一定注意到上面的两个方法中我们都加了一个CallBack类型的参数,我们先来看看这是啥玩意:
/** * 网络请求完成后的回调接口 * * @param <T> 请求完成后返回的数据实体类 * @author AigeStudio{@link http://aigestudio.com/?p=100} * @since 2016-08-15 */public interface CallBack<T> { /** * 请求完成后回调该方法 * * @param isSuccess 请求是否成功 * @param response 如果请求成功该参数不为空否则为空 * @param error 如果请求成功该参数为空否则会将失败原因传入 */ void onFinish(boolean isSuccess, Response<T> response, String error);}
CallBack其实就是一个回调接口,大家知道我们网络请求是耗时的,我们不能在主线程里处理这些耗时操作,因此我们需要在子线程里去请求,那么我们就需要在请求完成后将结果返回给调用者,而CallBack则处理这个逻辑。
现在我们声明了接口,接下来就该实现具体的逻辑了,现在问题来了,对于网络请求来说,我们又很多成熟的框架,最基本的我们可以使用HttpURLConnection自己原汁原味地实现一个,也可以用封装的OkHttp去实现,也可以使用当下较为流行的RESTFUL框架,不管你使用哪种方式,都该是遵循我们的业务模型的,不管底层逻辑如何实现,都不应该影响我们上层的结构,这里的体现就是我们上面定义的接口和具体的实现类。这里我们有三种实现:HttpURLConnection、OkHttp和RESTFUL,我们对应不同的实现类:
/** * 网络请求方法常规实现 * * @author AigeStudio{@link http://aigestudio.com/?p=100} * @since 2016-08-15 */class ApiNor implements IApi { @Override public void login(String user, String password, CallBack<LoginInfo> c) { } @Override public void upgrade(String code, CallBack<UpgradeInfo> c) { }}
/** * 网络请求方法OkHttp实现 * * @author AigeStudio{@link http://aigestudio.com/?p=100} * @since 2016-08-15 */class ApiOkHttp implements IApi { @Override public void login(String user, String password, CallBack<LoginInfo> c) { } @Override public void upgrade(String code, CallBack<UpgradeInfo> c) { }}
/** * 网络请求方法RESTFUL实现 * * @author AigeStudio{@link http://aigestudio.com/?p=100} * @since 2016-08-15 */class ApiRest implements IApi{ @Override public void login(String user, String password, CallBack<LoginInfo> c) { } @Override public void upgrade(String code, CallBack<UpgradeInfo> c) { }}
这里很多童鞋会问爱哥为什么一个网络请求封装要三种不同的实现呢?仅仅是为了演示吗?NO NO NO,这绝不是演习,事实上我们在实际开发中很有可能遇到这样的情况,如上所说网络请求有很多封装好的框架,但是这些框架即便由很牛逼的大神所写也难免会出现一些小BUG,如何项目紧急而你又无法去解决这些BUG的时候,使用这样的接口隔离实现能让你快速在不同的网络请求框架之间切换且不影响上层结构。说了这么多,我们来看看怎么用,这里爱哥使用一个功能类来封装这三种不同的实现:
/** * 网络请求方法单例封装类 * * @author AigeStudio{@link http://aigestudio.com/?p=100} * @since 2016-08-15 */public final class Api implements IApi { private volatile static Api sInstance; private IApi mApi; private Api() { mApi = new ApiNor(); } public static Api getInstance() { if (null == sInstance) synchronized (Api.class) { sInstance = new Api(); } return sInstance; } @Override public void login(String user, String password, CallBack<LoginInfo> c) { mApi.login(user, password, c); } @Override public void upgrade(String code, CallBack<UpgradeInfo> c) { mApi.upgrade(code, c); }}
这个功能类很简单,大家可以看到它也是实现了IApi接口,本质而言也是个IApi的实现类,不同的是其不实现任何具体的逻辑而只是简单地调用上面那三个实现类对应的逻辑而已,这里我们构造的是ApiNor对象:
mApi = new ApiNor();
也就是说Api里所有的实现方法实质上都是调用的ApiNor的逻辑实现。最后我们再看看如何在上层中使用:
/** * 客户类 * * @author AigeStudio{@link http://aigestudio.com/?p=100} * @since 2016-08-15 */public class Main { public static void main(String[] args) { Api.getInstance().login("AigeStudio", "123456789", new CallBack<LoginInfo>() { @Override public void onFinish(boolean isSuccess, Response<LoginInfo> response, String error) { if (isSuccess) { int code = response.mResponseCode; if (code == 200) { String user = response.mData.mUser; String password = response.mData.mPassword; String name = response.mData.mName; String avatarUrl = response.mData.mAvatarUrl; String sex = response.mData.mSex; String birthday = response.mData.mBirthday; long registerTimeStamp = response.mData.mRegisterTimeStamp; long lastLoginTimeStamp = response.mData.mLastLoginTimeStamp; int age = response.mData.mAge; } else { System.out.print(response.mResponseMsg); } } else { System.out.print(error); } } }); }}
我们在main方法中简单地调用login方法模拟登录,这个网络请求框架我们就算完结了,假使有一天,ApiNor里的逻辑出现了BUG而我们又没时间改,那么我们可以简单地切到OkHttp的实现中:
mApi = new ApiOkHttp();
而这个改动,你不需要去更改任何上层的代码,也就是说我们Main类中的代码不需要任何改动,你只需要做相应的底层实现即可,这就是一个很简单的架构小例子,利用接口分离实现。除此之外,我们还可以在Api类中根据不同的接口方法调用不同的实现,比如对于登录我想用RESTFUL实现而对于版本更新我想用OkHttp实现等,这些底层的改动都不会影响上层的结构。
你可以把这种结构应用于很多很多地方,相信我熟悉了你会爱上它的,简单但是非常实用,特别是在引入第三方框架的时候,对第三方框架做一层封装能让你的项目结构更灵活。
原文链接:http://aigestudio.com/?p=100
- 巧用接口解耦分离实现
- 利用接口实现操作分离
- C++接口与实现分离
- C++接口与实现分离
- C++中接口与实现分离
- C++接口与实现分离(转)
- 类的接口与实现的分离
- 餐馆那些事之:接口实现分离
- c++接口与实现的分离
- 代理类:接口与实现的分离
- C++接口与实现分离(转
- C++中接口与实现分离
- 接口与实现分离技术学习总结
- C++接口与实现分离(转)
- c++导出函数接口与实现分离
- C++设计:接口与实现分离
- <C++> 类的‘’实现‘’与‘’接口‘’分离
- 日志系列之接口与实现分离
- 2016/8/18总结
- CTS Verifier
- MySQL性能优化
- v7包报错问题
- 1#HBase入门(待续)
- 巧用接口解耦分离实现
- 线程池ExecutorService的submit和execute
- Hadoop深入介绍
- DatePicker控件/日期控件,只显示年月/隐藏日
- Spring Data MongoDB 环境搭建
- 查看LINUX进程内存占用情况
- Spring三种bean注入方式
- JSP中在提交表单之前,发送ajax请求进行js的验证
- Android 依赖注入之Dagger