设计模式实践 策略模式

195 阅读3分钟

什么是策略模式?什么时候用?

  • 提到策略模式,一般常见于算法,例如排序算法,多种策略提供排序。
  • Android中的动画的差值器,每种差值器让动画的执行变化不同,实现加、减速度等效果。

怎么实现策略模式?

  • 先定义一个外部调用的接口,每种策略作为实现。
  • 建立外部管理类,提供设置和获取策略实现的方法,调用时使用策略接口提供的Api调用即可。

策略模式实现ImageLoader

  • 平时Android开发中,总会使用一些图片加载框架,例如Glide、Fresco或者是老牌的Android-Universal-Image-Loader,而我们一般不会直接使用,而是会封装一层,方便以后切换别的框架时,不需要改动外部Api。
  1. 策略接口
public interface ImageLoaderStrategy {
    /**
     * 初始化,在application的onCreate中初始化,该方法存在意义是为了有些框架需要在Application中初始化
     */
    void init(Context context);

	 /** 
     * 加载图片
     * @param option 加载可选项
     * @param targetView 目标ImageView
     */
    void load(Context context, LoadOption option, ImageView targetView);

    /**
     * 清除内存缓存
     */
    void clearMemoryCache(Context context);
    
    /**
     * 清除磁盘缓存
     */
    void clearDiskCache();

    /**
     * 暂停请求,一般在ListView或RecyclerView滚动时调用,停止加载快速滚动的图片数据
     */
    void pause(Context context);

    /**
     * 恢复请求,在ListView或RecyclerView滚动停止时调用
     */
    void resume(Context context);
}
  1. 加载参数,不是策略模式的必须,只是该例子需要
public class ImageOption {
	private String url;

	public ImageOption(Builder builder) {
		this.url = builder.url;
	}

	public static class Builder {
		private String url;
		//...这里可以加其他参数,现在只提供一个url参数演示
		
		public Builder setUrl(String url){
			this.url = url;
		}
		
		public ImageOption build() {
			return new ImageOption(this);
		}
	}
}
  1. 统一的外部门面类
public class ImageLoader {
    private ILoaderStrategy mLoader;
    private Context mContext;

    public ImageLoader with(Context context) {
        mContext = context;
        return this;
    }

    public Context getContext() {
        return mContext;
    }

    public void setLoader(ILoaderStrategy loader) {
        mLoader = loader;
    }

    public ILoaderStrategy getLoader() {
        return mLoader;
    }
}
  1. 具体实现
  • Glide实现
public class GlideLoader implements ILoaderStrategy {
    private Context mApplicationContext;

    @Override
    public void init(Context context) {
        mApplicationContext = context.getApplicationContext();
        //做一些初始化操作...
    }

    @Override
    public void load(@NotNull Context context, @NotNull LoadOption option, @NotNull ImageView targetView) {
        //1、根据option中的资源类型决定加载的资源类型
        //2、变换、裁剪操作
        //3、加载
        Glide.with(context).load(xxx).into(targetView);
    }

    @Override
    public void clearMemoryCache(int level) {
        Context context = ImageLoader.getContext();
        // clear Glide cache
        if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
            Glide.get(context).clearMemory();
        }
        // trim memory
        Glide.get(context).trimMemory(level);
    }

    @Override
    public void clearDiskCache() {
        Glide.get(ImageLoader.getContext()).clearDiskCache();
    }

    @Override
    public void pause(Context context) {
        Glide.with(context).pauseRequests();
    }

    @Override
    public void resume(Context context) {
        Glide.with(context).resumeRequests();
    }
}
  • Fresco实现,同样建立一个ILoaderStrategy接口的实现类
public class FrescoLoader implements ILoaderStrategy {
    private Context mApplicationContext;

    @Override
    public void init(Context context) {
        mApplicationContext = context.getApplicationContext();
        //做一些初始化操作...
    }

    @Override
    public void load(@NotNull Context context, @NotNull LoadOption option, @NotNull ImageView targetView) {
        //1、根据option中的资源类型决定加载的资源类型
        //2、变换、裁剪操作
        //3、加载
    }

    @Override
    public void clearMemoryCache(int level) {
        Fresco.getImagePipeline().clearMemoryCaches();
    }

    @Override
    public void clearDiskCache() {
        Fresco.getImagePipeline().clearDiskCaches();
    }

    @Override
    public void pause(Context context) {
        Fresco.getImagePipeline().pause();
    }

    @Override
    public void resume(Context context) {
        Fresco.getImagePipeline().resume();
    }
}
  1. 初始化,在Application中初始化,设置策略对象
ImageLoader.with(this).setLoader(new GlideLoader());
  1. 调用
ImageLoader.getLoader().load(context, new LoadOption.Builder().setUrl("www.xxxx.jpg").build(), holder.vImage);

总结

  • 切换不同的图片加载器实现,在初始化时设置不同的Loader实例即可。
  • 拓展方便。如果需要使用另外的加载框架,新建一个实现类做对应的操作即可,对外API是不变的,外部使用都不需要改动。
  • 高内聚,低耦合。对外都是依赖封装的ImageLoader,外部不需要关注内部实现是谁,怎么处理。