策略模式

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(……)方法。

你可能感兴趣的:(策略模式)