策略模式
1、 策略模式介绍
Android每一种模块都有很多种解决方案,例如网络模块有OKHttp、Volley、Retrofit等;数据库有OrmLite、GreenDao、Room等;图片模块有Glide、Picaso等。平时开发的时候可能就会选定一种模块,例如图片就用Glide,然后在项目的代码里面直接调用Glide的接口来完成图片的处理。
如果确定以后不会更换这些模块的话,那么初看也没什么问题。但是万一以后有了一个更好的解决方案呢?或者公司开发了一个中间件用来解决这个模块的问题,然后公司通知各个项目都需要接入这个模块。
这种情况下,我们通常的解决方案是:将项目中所有用到原先模块的API,统一换成新引入模块的API。这样不仅工程量大,还容易造成新的问题;而且新的模块的API也需要重新了解和熟悉。
2、策略模式实现
根据设计模式中的“开闭原则”,应该尽量做到对修改关闭。如果使用上述提到的解决方案,那么相当于是业务跟模块之间强耦合了。
那么怎么解决这种“强耦合”呢?答案是面向接口编程。
在使用模块功能的时候,尽量不要直接使用模块提供的API接口,而要使用自定义的接口提供的方法,也就是通常所说的面向接口编程。
具体的解决方案如下:
-
定义一个接口,这个接口的方法就是我们项目里面所调用的。
-
所有引用的模块都要实现这个接口,虽然模块本身有自己的API,但是现在不是直接使用模块的API,而是使用自定义的接口。所以这个模块必须要实现我们自定义的接口。
-
提供一个使用类,通常是单例模式。这个使用类就是我们项目里面所直接调用的,所以这个使用类也必须实现我们自定义的接口。
-
在使用类中指定所引用的第三方模块。例如Glide或者Picaso,或者是公司提供的中间件模块。
这种解决方案的好处就是,无论怎么替换第三方模块,项目使用到这个模块功能的地方都不需要改动,只需要在配置里面设定好使用的第三方模块即可。
(1)以日志模块为例,策略模式在日志模块中的应用。
首先定义一个日志模块API通用接口public interface ILogProcessor { void v(String vLog); void d(String dLog); void i(String iLog); void e(String eLog); }
接下来我们创建两个日志功能类,分别实现上面的日志接口。
第一个日志功能类使用系统自带的log实现,我们定义为DefaultLogProcessor:
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);
}
}
第二个日志功能类使用ZydLog实现,我们定义为ZydLogProcessor:
public class ZydLogProcessor implements ILogProcessor {
@Override
public void v(String vLog) {
ZydLog.v("zydlog:" + vLog);
}
@Override
public void d(String dLog) {
ZydLog.d("zydlog:" + dLog);
}
@Override
public void i(String iLog) {
ZydLog.i("zydlog:" + iLog);
}
@Override
public void e(String eLog) {
ZydLog.e("zydlog:" + eLog);
}
}
提供一个使用类LogLoader:
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;
}
//通过load选定使用哪一个日志功能类
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);
}
}
这个LogLoader直接使用了ILogProcessor接口提供的方法作为对外API接口的调用入口。
这种使用方式保持了调用的接口名称和ILogProcessor一致。不过有个缺点就是LogLoader必须要实现接口,而且接口名称不能发生变化。
也可以实现另一种LogLoader,不用实现ILogProcessor。自定义对外的API接口方法的名称,其灵活性更强。
public class LogLoader2 {
private static volatile LogLoader2 sInstance = null;
private static ILogProcessor sILogProcessor;
private LogLoader2() {
}
public static LogLoader2 getInstance() {
if (sInstance == null) {
synchronized (LogLoader2.class) {
if (sInstance == null) {
sInstance = new LogLoader2();
}
}
}
return sInstance;
}
public static ILogProcessor load(ILogProcessor logProcessor) {
return sILogProcessor = logProcessor;
}
public void useVmode(String vLog) {
sILogProcessor.v(vLog);
}
public void useDmode(String dLog) {
sILogProcessor.d(dLog);
}
public void useImode(String iLog) {
sILogProcessor.i(iLog);
}
public void useEmode(String eLog) {
sILogProcessor.e(eLog);
}
}
最后使用日志模块的策略模式:
LogLoader.load(new ZydLogProcessor());
LogLoader.getInstance().d("this is zyd log");
LogLoader.load(new DefaultLogProcessor());
LogLoader.getInstance().d("this is system default log");
通过LogLoader.load(……)可以自由切换不同的日志实现模块,在使用的地方无须做任何改动,仍然使用LogLoader.getInstance().d(……)方法。