【Android-业务】
前言
甲方:做个简单的APP端OA吧乙方:好的
项目快交付了......
甲方:没网也要能用
乙方:......
一、分析
简化一下思路:有网-->用户点击-->APP处理-->访问数据库-->返回结果
所以我们能“秀操作”的地方只有“访问数据库”了。
所以解决方案就是:
当你准备离线时,从服务端的数据库拉取下来相关的数据,保存到本地数据库。
无网操作本地数据库。
到有网的地方,移动端和服务端进行数据同步。
二、业务点
1.同步代码
这个很简单,就是根据个人的权限,直接get数据库相关数据,然后给APP,保存到sqlite。
2.难点:双策略
什么叫“双策略”呢?
比如你有一个“修改个人手机号”的方法setUserPhone
,那么这个方法在有网的情况下,毫无疑问就是简单的请求http,然后服务端操作数据库,最后返回结果。
那么无网的情况呢?
还是同样的方法setUserPhone
,但是需要另一套操作,这就是“双策略”了。
造个轮子
先来简单的,因为“双策略”很像“接口+不同的实现”,所以我们先做这个(为了更理解后面的封装)。
接口类:
/**
* <p>
* 一个接口
* </p>
*
* @author mythS
* @since 2020/5/14 15:35
*/
public interface JieKou {
/**
* 用于测试两种状态的下的接口
*/
int test();
}
第一种情况的实现类:
/**
* <p>
* 接口在1情况下的实现
* </p>
*
* @author mythS
* @since 2020/5/14 15:38
*/
public class JKShiXian1 implements JieKou {
@Override
public int test() {
return 1;
}
}
第二种情况的实现类:
/**
* <p>
* 接口在2情况下的实现
* </p>
*
* @author mythS
* @since 2020/5/14 15:38
*/
public class JKShiXian2 implements JieKou {
@Override
public int test() {
return 2;
}
}
判断以及选择不同的接口实现:
/**
* <p>
* 主入口测试效果
* </p>
*
* @author mythS
* @since 2020/5/14 15:39
*/
public class Test {
JieKou jieKou;
public void test(int qingkuang) {
if (qingkuang == 1) {
//走1接口
jieKou = new JKShiXian1();
}
if (qingkuang == 2) {
//走2接口
jieKou = new JKShiXian2();
}
int n = jieKou.test();
Log.i("提示", "" + n);
}
}
使用举例:
Test test = new Test();
如果要第二种实现,就这样:
test.test(2);
如果要第一种实现,就这样:
test.test(1);
轮子升华版
首先我们需要明确要做出什么效果:
1、用户继承我的“策略封装类”,需要复写一个自己的“什么情况,用什么策略”的方法(拥有了更广泛的适用性)
2、开放一个api,能够让用户传入“实现类”获取其实例(用户可以在需要的时候,直接取需要的实例)
3、接口的调用不再是用接口本身的名字,而是用自己继承“策略封装类”后子类的实例调用(让程序和接口解耦)
PS:上面的“轮子版”代码,直接放进来用于演示,就不新建新的接口以及实现了。
下面直接上代码,代码最后跟上解释:
策略的封装类(核心):
/**
* <p>
* 策略的封装类
* </p>
*
* @author mythS
* @since 2020/5/15 13:19
*/
abstract class BaseStrategy {
/**
* 策略的逻辑
* 继承这个抽象类,需要个性化实现这个方法
*
* @param n 不同情况的标识,比如0是在线1是离线
* @param <E> 返回接口的实例(复写后一定要换成自己接口的全限定性名)
*
* @return 所需求情况下的实例
*/
public abstract <E> E cur(int n);
/**
* 获得实现类实例
*
* @param eClass 实现类全限定性名
*/
protected static <E> E getImpl(Class<?> eClass) {
String s = getClassFullName(eClass);
return (E) getImplCur(s);
}
/**
* 全限定名字符串处理
*/
private static String getClassFullName(Class<?> eClass) {
String s = eClass.toString();
if (s.split(" ").length > 1) {
s = s.split(" ")[1];
}
return s;
}
/**
* 利用反射根据类名获得其实例
*/
private static Object getImplCur(String s) {
Object o = null;
try {
o = Class.forName(s).newInstance();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return o;
}
}
这个类就是这次“升华版”的核心。总体来说这个类的设计思路为:
通过传进来一个“实现类”的全限定性名,
然后利用反射生成其实例(getImplCur方法)。实例赋值给开放api方法getImpl。
定义一个抽象方法cur,参数就是不同情况的标识,用户自己拟定,比如在线是0,离线是1,根据自己的业务拟定。
这样用户继承这个“父类”的时候,重写这个方法,根据标识,利用开放api方法getImpl,获得需要的“实现类”就能完成我们的预想了。
下面举例说明怎么使用:
第一步,上代码:
/**
* <p>
* 举例使用策略
* </p>
*
* @author mythS
* @since 2020/5/15 15:08
*/
public class TestStrategy extends BaseStrategy {
@Override
public JieKou cur(int n) {
//1也就是在线时,需要JKShiXian1的实现
if (n == 1) {
return getImpl(JKShiXian1.class);
} else {
return getImpl(JKShiXian2.class);
}
}
}
就是简单的创建一个子类,继承BaseStrategy,然后根据自己的业务复写cur方法,
我这里是假设在线时需要JKShiXian1的实现,离线时需要JKShiXian2的实现。n为1代表在线。
在具体需要使用的地方,实例化子类TestStrategy为u,然后直接u.cur(2).test()就能获得JKShiXian2的实现效果。
代码:
TestStrategy u = new TestStrategy();
Log.i("提示", "" + u.cur(1).test());
补充:
如果觉得上面两行太麻烦,还需要new对象,可以在TestStrategy写一个静态方法cur1,
然后内容和复写cur方法时里面的内容一样,然后注掉cur内容,返回个null(就是不用这个了)
然后在需要调用的地方直接TestStrategy.cur1即可。
这也是上面说的”需要实现的效果2“的开发api的好处,使用者比较自由。
总结
关于这个令人无语的业务,确实在实际开发中遇到了(人生真实处处有“惊喜”)。
当然了,实际解决肯定不是这么简单,实际需要考虑的方方面面有很多,代码很庞大,但是因为代码需要保密,只能写这样一个简单例子,帮助遇到同类问题的朋友,能有个思路。
如果觉得有疑惑、有类似业务或者我写的有问题,不妨留言,讨论一下。
当然,如果能顺手给个赞,在下感激不已。