我的新书《Android App开发入门与实战》已于2020年8月由人民邮电出版社出版,欢迎购买。点击进入详情
简介
我们知道,android每一种模块都有很多种解决方案,比如网络模块有OKHttp,Volley,Retrofit等;数据库有OrmLite,GreenDao,Room等;图片模块有Glide,Picaso等。
平时我们开发的时时候可能就会选定一种模块,比如图片我们就用Glide,然后在项目的代码里面直接调用Glide的接口完成图片处理功能。
其实粗略一看到也没什么问题,如果我们不更换这些模块的话。
但是,万一哪一天有了一个更好的解决方案呢?或者,公司开发了一个中间件,也是用来解决这个模块的问题,然后公司通知需要各个项目都接入这个模块。
这种情况下,我们通常的解决方案是: 将项目中所有用到原先模块API的地方,统统换成新引入模块的API,这样不仅工程量大,而且还容易造成改出新的问题;而且新的模块的API也需要重新了解熟悉。
解决方案
根据设计模式中的“开闭原则”,我们尽量应该做到对修改关闭。
想上述提到的解决方案,相当于是业务跟模块之间强耦合了。
那么怎么解决这种“强耦合”呢?接口。
我们在使用模块功能的时候,尽量不要直接使用模块提供的API接口,而是使用我们定义的接口提供的方法,也就是我们通常说的面向接口编程。
具体的解决方案:
- 定义一个接口,这个接口的方法就是我们项目里面说调用的;
- 所引用的模块都要实现这个接口,虽然模块本身有自己的API,但是我们现在不是直接使用模块的API,而是使用我们定义的接口。所以这个模块必须要实现我们定义的接口;
- 提供一个使用类,通常是单例模式。这个使用类就是我们项目里面所直接调用的,所以这个使用类也必须实现我们定义的接口。
- 在使用类中指定所引用的第三方模块。比如Glide或者Picaso,或者是公司机制提供的中间件模块。
这种解决方案的好处就是,无论怎么替换第三方模块,项目使用到这个模块功能的地方都不需要改动,只需要在配置里面设定好使用的第三方模块即可。
实例代码
public interface ILogProcessor {
void v(String vLog);
void d(String dLog);
void i(String iLog);
void e(String eLog);
}
private static volatile LogLoader sInstance = null;
private static ILogProcessor sILogProcessor;
private LogLoader() {
}
public static LogLoader getInstance () {
if (sInstance == null) {
synchronized(LogLoader.class){
if (sInstance == null) {
sInstance = new LogLoader();
}
}
}
return sInstance;
}
public static ILogProcessor load(ILogProcessor logProcessor) {
return sILogProcessor = logProcessor;
}
@Override
public void v(String vLog) {
sILogProcessor.v(vLog);
}
@Override
public void d(String dLog) {
sILogProcessor.d(dLog);
}
@Override
public void i(String iLog) {
sILogProcessor.i(iLog);
}
@Override
public void e(String eLog) {
sILogProcessor.e(eLog);
}
}
public class DefaultLogProcessor implements ILogProcessor {
@Override
public void v(String vLog) {
Log.v("DefaultLogProcessor", "defaultlog:" + vLog);
}
@Override
public void d(String dLog) {
Log.d("DefaultLogProcessor", "defaultlog:" + dLog);
}
@Override
public void i(String iLog) {
Log.i("DefaultLogProcessor", "defaultlog:" + iLog);
}
@Override
public void e(String eLog) {
Log.e("DefaultLogProcessor", "defaultlog:" + eLog);
}
}
public class TinyLogProcessor implements ILogProcessor {
@Override
public void v(String vLog) {
TinyLog.v("tinylog:" + vLog);
}
@Override
public void d(String dLog) {
TinyLog.d("tinylog:" + dLog);
}
@Override
public void i(String iLog) {
TinyLog.i("tinylog:" + iLog);
}
@Override
public void e(String eLog) {
TinyLog.e("tinylog:" + eLog);
}
}
public class LogLoader implements ILogProcessor{
private static volatile LogLoader sInstance = null;
private static ILogProcessor sILogProcessor;
private LogLoader() {
}
public static LogLoader getInstance () {
if (sInstance == null) {
synchronized(LogLoader.class){
if (sInstance == null) {
sInstance = new LogLoader();
}
}
}
return sInstance;
}
public static ILogProcessor load(ILogProcessor logProcessor) {
return sILogProcessor = logProcessor;
}
@Override
public void v(String vLog) {
sILogProcessor.v(vLog);
}
@Override
public void d(String dLog) {
sILogProcessor.d(dLog);
}
@Override
public void i(String iLog) {
sILogProcessor.i(iLog);
}
@Override
public void e(String eLog) {
sILogProcessor.e(eLog);
}
}
public class SwitcherFragment extends BaseFragment {
@Override
protected void initViewsAndEvents(Bundle savedInstanceState) {
}
@Override
protected int getContentViewLayoutID() {
return R.layout.fragment_solution_switcher;
}
@OnClick({R.id.btn_log1, R.id.btn_log2})
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_log1:
LogLoader.load(new TinyLogProcessor());
LogLoader.getInstance().d("this is tiny log");
break;
case R.id.btn_log2:
LogLoader.load(new TinyLogProcessor());
LogLoader.getInstance().d("this is system default log");
break;
}
}
}