专栏首页androidBlog二次封装图片第三方框架——简单工厂模式的运用

二次封装图片第三方框架——简单工厂模式的运用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/details/54564442

CSDN首发地址:

前言

写在前头,这篇博客对于老司机们没什么多大的意义,老司机们就不用看了,对于新手还是有很大实用价值的。

平时在项目中,你们有没有遇到这样的情况,比如之前项目是使用开源框架ImageLoader,现在想使用Picasso或者Glide,这时候你会怎么办呢?是一行一行代码去改吗?

当然可以,如果项目使用到ImageLoader相应的方法少的话,那还可以,但是一旦项目比较大型的话,这会是多大的工作量,估计至少得改个几天,累先不说,至少毫无意义,那怎么办呢?别急,下面就来讲解。

第一种方法

这种方法大多数人都会使用,直接封装成为一个工具类,提取公共参数,以后想修改的话,直接修改工具类里面具体的实现

public class ImageLoaderUtils {


    public static void loadImageView(Context mContext, String url, ImageView mImageView) {
        Picasso.with(mContext).load(url).into(mImageView);
    }



}

这个时候我们的项目中如果不想使用Picasso,这个时候我们想使用Glide,我们只需修改 ImageLoaderUtils类中方法的具体实现即可,代码如下

public class ImageLoaderUtils {

    /**
     * 指定大小加载图片
     *
     * @param mContext   上下文
     * @param url       图片路径
     * @param mImageView 控件
     */
    public static void loadImageView(Context mContext, String url, ImageView mImageView) {
//        Picasso.with(mContext).load(url).into(mImageView);
        Glide.with(mContext).load(url).into(mImageView);
    }

}

看了上面的代码,相信大多数人都知道,如果我们不封装,我们必须查找每个类文件里面使用到Picasso的相应方法的位置,然后再替换,这会是一个很大的工作量。而我们如果进行封装,只需更改工具类里面的方法的具体实现,这样是不是很方便呢?看到这里,你是不是觉得这篇文章结束呢,还没,下面介绍另外一种方法,这种方法将更加优雅。

那就是使用简单工厂模式。


简单工厂模式

定义

简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。简单点说就是用来创建具有相同基类的对象

类UML图

角色介绍

从上面的UML图可以看到,总共有三个角色Factory,Product,ConcreteProuct,其中Factory依赖于Product

Product:产品的基类,通常有抽象类或者接口来充当,用来统一接口,不关心细节的实现

ConcreteProduct:Product的具体实现类

Factory:工厂类,用来创建对象,可以根据参数的不同返回不同的Product对象,实现逻辑封装在其内部

使用步骤

从上面的分析,我们知道,采用简单工厂模式,一般需要三个步骤

  • 抽象Product的共同特点,定义成一个接口或者抽象类
  • ConcreteProduct的具体实现
  • 实现Factory

下面我们一起来看一下怎样运用于替换图片框架上


简单工厂模式的运用——几行代码更换图片框架

首先为了加深理解,我们一起先来看一下类UML图

接着按照上面的三部曲,第一步,我们先定义一个接口IimageListener,用来统一参数

public interface IimageListener {



    void display(Context context, ImageView imageView, String url, int progressId, int errorId,
                 Object tag);

    void display(Context context, ImageView imageView, String url, int progressId, int errorId);

    void display(Context context, ImageView imageView, String url, int progressId);

    void display(Context context, ImageView imageView, String url);

    void display(Context context, ImageView imageView, Uri uri);
}

第二步,我们来写GlideRequest和PicassoRequest的具体实现

public class GlideRequest implements IimageListener {


    @Override
    public void display(Context context, ImageView imageView, String url, int progressId, int
            errorId, Object tag) {
        DrawableTypeRequest<String> load = Glide.with(context).load(url);
        if (progressId != -1) {
            load.placeholder(progressId).centerCrop();
        } else {
            load.placeholder(new ColorDrawable(Color.GRAY));
        }
        if (errorId != -1) {
            load.error(errorId);
        }else{
            load.error(R.drawable.ic_error);
        }

        load.into(imageView);
    }

    @Override
    public void display(Context context, ImageView imageView, String url, int progressId, int
            errorId) {
        display(context, imageView, url, progressId, errorId, null);
    }

    @Override
    public void display(Context context, ImageView imageView, String url, int progressId) {
        display(context, imageView, url, progressId, -1, null);
    }

    @Override
    public void display(Context context, ImageView imageView, String url) {
        display(context, imageView, url, -1, -1, null);
    }

    @Override
    public void display(Context context, ImageView imageView, Uri uri) {
        DrawableTypeRequest<Uri> load = Glide.with(context).load(uri);
        load.into(imageView);
    }

}

public class PicassoRequest implements IimageListener {

    @Override
    public void display(Context context, ImageView imageView, String url, int progressId, int
            errorId, Object tag) {
        Picasso.with(context).load(url).placeholder(progressId).error(errorId).tag(tag).into(imageView);
    }

    @Override
    public void display(Context context, ImageView imageView, String url, int progressId, int
            errorId) {
        Picasso.with(context).load(url).placeholder(progressId).error(errorId).into(imageView);
    }

    @Override
    public void display(Context context, ImageView imageView, String url, int progressId) {
       Picasso.with(context).load(url).placeholder(progressId).into(imageView);
    }

    @Override
    public void display(Context context, ImageView imageView, String url) {
        Picasso.with(context).load(url).into(imageView);
    }

    @Override
    public void display(Context context, ImageView imageView, Uri uri) {
           Picasso.with(context).load(uri).into(imageView);
    }
}

第三步,我们来写工厂类ImageRequestManager的实现,可以看到我们可以根据不同的参数返回不同的实例,进而来决定使用Picasso或者是Glide

public class ImageRequestManager {

    public static final String type_Glide="Glide";
    public static final String type_Picasso="Picasso";
    public static final String type_default =type_Glide;

   private ImageRequestManager(){

   }

    public static IimageListener getRequest(){
      return getRequest(type_default);

    }

    public static IimageListener getRequest(String type){
        switch (type){
            case type_Glide:
                return new GlideRequest();

            case type_Picasso:
                return new PicassoRequest();

            default:
                return new GlideRequest();
        }

    }
}

最后,以后我们想加载图片只需简单调用下面的方法就OK了,简单明了,再也不用怕替换框架了

ImageRequestManager.getRequest().display(mContext, imageView, imageUrl);

讨论

情景一:之前我是使用Glide框架,现在想使用Picasso框架,那要怎么办呢?

只需将 ImageRequestManager 里面的 String type_default=type_Glide 更改为String type_default=type_Picasso 就ok了。

//public static final String type_default =type_Glide;
public static final String type_default =type_Picasso;

情景二:有人会说了,平时在项目中基本只会使用一种图片加载框架,要么使用Picasso,要么使用Glide,你这样做同时使用了两种框架,无疑增加了APK的大小,那要怎么办呢?其实很简单

如果你只想使用Picasso,去掉Glide的具体实现就OK了,同理你只想使用Glide,去掉Picasso的具体实现就好了,不过建议保留空方法,以后要修改就不必更改工厂类 ImageRequestManager 里面的逻辑呢


简单工厂模式总结

  • 将对象的创建工作与对象的业务逻辑分析,降低了系统的耦合度
  • 简单工厂模式的要点在于:当你需要什么,只需要传入一个正确的参数,就可以获取你所需要的对象,而无须知道其创建细节。为了调用者的方便,我们可以给工厂类添加一个默认参数,这样调用的时候不必每次传入参数
  • 从上面知道的例子,我们知道,业务逻辑大部分在工厂类里面,如果工厂类需要创建的对象不多的话,简单工厂方法模式还是很有很大的优势的,而如果需要创建很多对象的话,那工厂里面无疑要增加很多case 语句,这样会导致工厂类的职责太重了,破坏了类的单一性。

综上所述:

简单工厂模式最大的优点在于实现对象的创建和对象的使用分离,将对象的创建交给专门的工厂类负责,但是其最大的缺点在于工厂类不够灵活,增加新的具体产品需要修改工厂类的判断逻辑代码,而且产品较多时,工厂方法代码将会非常复杂。

简单工厂模式适用情况包括:工厂类负责创建的对象比较少;客户端只知道传入工厂类的参数,对于如何创建对象不关心。


扩展

看了上面的文章,对于于Volley,OKhttp等网络框架的简单封装,你是不是也想到了什么,应该懂得怎么封装了吧。

而对于Retrofit的封装,你有没有想到了什么呢?因为Retrofit的返回对象比较特殊,是 Observable<>,更其他的网络框架不太一样,其实还是可以大概处理一下的,这里暂时就不讨论了,大家有兴趣的可以自己先去尝试,相信你会收获到很多东西的。


题外话

万丈高楼平地起,对于程序猿的我们,一定要多动手实践,我相信很多同学看到这里,都基本掌握了,但是很有很多同学不动手去实践一下,就这样,几天过去了,似懂非懂,最后忘记了,而这恰恰是分水岭。哈哈,就扯蛋这么多了。


最后给出github地址,上面的小项目是我平时没事写写玩玩的。

github地址

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Android 二次封装网络加载框架

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/de...

    用户2965908
  • 建造者模式(Builder)及其应用

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/details/...

    用户2965908
  • Java 反射机制详解

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/details/...

    用户2965908
  • MockMvc -你需要一个测试基类

    用例虽然能执行成功,但是还存在着不少问题。最为严重的,就是代码冗余度太高。两次模拟的HTTP请求,虽然请求的方式和发送内容不同,但是整个请求的组装、发送和结果验...

    Criss@陈磊
  • Java 函数式编程和 lambda 表达式

    函数式编程更多时候是一种编程的思维方式,是种方法论。函数式与命令式编程的区别主要在于:函数式编程是告诉代码你要做什么,而命令式编程则是告诉代码要怎么做。说白了,...

    芋道源码
  • 陌陌2019秋招研发编程题题解

    输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,c...

    武培轩
  • 观察者模式和Spring的结合

    这周给分了一个任务,就是对查询回来的数据进行各种各样的过滤,有七种不同的过滤条件。过滤条件是在数据库中存着的。在我们项目中有一个热发,就是定时的从数据库中把数...

    令仔很忙
  • 【leetcode算法-无重复字符的最长子串】

    滑动窗口,通过使用 HashSet 作为滑动窗口,我们可以用 O(1) 的时间来完成对字符是否在当前的子字符串中的检查。滑动窗口是数组/字符串问题中常用...

    用户5640963
  • 设计模式-备忘录模式

    备忘录角色对如何其他对象提供一个接口,也就是宽接口的话,那么备忘录角色存储的内部状态都暴露给其他对象。这种情况导致发起人的状态都没看到,是破坏封装性的,只能通过...

    breezedancer
  • Android中ContentProvider简介

    翻看Android源码可以发现,Android一般的代码架构如下:activity,service,receiver----contentProvider---...

    fanfan

扫码关注云+社区

领取腾讯云代金券