设计模式之适配器模式

适配器模式是常用的模式之一,其主要意图就是做接口兼容:使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。比如甲乙两个接口,客户端想让乙接口做出甲接口的行为或者说让乙接口拥有甲接口同样的能力,那么乙接口就必须以某种手段适应甲接口制定的规则。这个手段就是适配器模式体现。
本文就通过一个简单的demo来逐一说明适配器模式的强大之处。

作为app的常用功能,显示和加载图片是在常见的功能,且比较成熟的网络框架诸如GlideImageLoader,Fresco,Picsso,universalimageloader等等作为android码农来说并不陌生。在app开发中通常根据不同的选择来使用上述图片框架中的一个,比如你使用了Glide,那么你的代码中肯定有如下类似的代码:

String url = "http:xxx.xxx.xxx.png";
ImageView imageView = (ImageView) findViewById(R.id.imageView);
Glide.with(context)
    .load(url)
    .into(imageView);

这么用完全可以让app发挥正常显示图片的功能,没有任何问题。而且
不考虑后期可能会替换图片加载框架的情况下你完全不用担心这么写有什么不妥。

可能你也许会说就算是替换图片框架也没什么,大不了把App中关于Glide的代码全删除或者注释掉然后换用新的框架相关的代码就行了,当然如果代码很少的话也能很快完成替换工作,但是通常一个app可能会有几十个页面或者更多,如果这么替换的话完全就是体力活儿。甚至频繁替换图片加载框架的话,来来回回删除代码等工作也会让人抓狂。

有没有更好的处理问题的方法呢?当然有,要不然这篇博客是干啥吃的。面向对象编程的设计理念之一就是要抽象,抽象是解耦的一大利器。所以解决上面替换图片库的思路就是重新抽象一层,将上面硬编码的使用方式完全变成使用接口的方式,为此进行了如下设计:

public interface IImageLoader {
    void displayImage(ImageView imageView, String url);
}

客户端可以实现IImageLoader,来简单设计一个这几的图片加载框架,那么上述使用Glide的代码将会替换成如下形式:

//父类引用指向具体的子类对象:通过工厂类来创建具体的实现
IImagerLoader mImageLoader=ImageLoaderFactory.createImageLoader();
//显示图片
mImageLoader.displayImage(imageView,url)

上面假设是客户端通过IImagerLoader实现了简单的图片加载库(姑且命名为CaiBridImageLoader),并且上面的代码在几十甚至上百个页面中都使用,那么反过来问题又来了,如果我不想使用CaiBridImageLoader,而想使用更为优秀的glide,Picsso,等等图片库怎么办?把项目中关于IImagerLoader的代码全删了,然后在硬编码成具体的图片框架的相关代码?那显然是不优雅的解决方法。

此时,适配器模式就发挥了巨大的作用,问题的核心就是解决IImageLoader接口和其他图片库的接口不兼容问题。注意上面通过父类引用的定义方式来定义mImageLoader变量,且通过工厂模式来创建一个图片加载框架,直接使用mImageLoader.displayImage的方式来完成图片的加载,至于mImageLoader具体是什么,大可不必关心。也就是说工厂类返回的图片框架对象可能就是CaiBridImageLoader,GlideImageLoader,PisscoImageLoader等。

如上所述,如果想让Glide拥有IImagerLoader一样的能力,那么就让Glide等图片库去适配IImageLoader吧。

public class GlideAdapter implements IImageLoader {

    @Override
    public void displayImage(ImageView imageView, String url) {
        Glide.with(imageView.getContext())
                .load(url)
                .into(imageView);

    }
}

如果想用Picasso图片库,那么代码可能使创建一个PicassoAdapter:

public class PicassoAdapter implements IImageLoader {

    @Override
    public void displayImage(ImageView imageView, String url) {
      Picasso.with(imageView.getContext())
                .load(url).into(imageView);

    }
}

甚至替换成UniversalImageLoader也很简单,提供一个UniversalImageLoaderAdapter:

public class UniversallImageAdapter implements IImageLoader {

    @Override
    public void displayImage(ImageView imageView, String url) {
      DisplayImageOptions displayImageOptions = new DisplayImageOptions.Builder()
                .cacheOnDisk(false)
                .cacheInMemory(true)
                .build();
        ImageLoader.getInstance().displayImage(url,imageView,displayImageOptions);


    }
}

创建上面的各种Adapter之后,只需要修改ImageLoaderFactory工厂类来返回具体指定的图片库即可,完全不用最原始的方法:删除替换代码来完成功能:
工厂方法可能的代码如下:

public IImageLoader createImageLoader() {
       
        if (imageLoaderType == ImageLoaderType.PICASSO) {
            return new PicassoAdapter();
        } else if (imageLoaderType == ImageLoaderType.GLIDE) {
           return new GlideAdapter();
        } else if (imageLoaderType == ImageLoaderType.UNIVERSAL)   {           return new UniversallImageAdapter()
        }else{
          return CaiBridImageLoader()
        }
       
    }

这样几个图片加载库能很好的适配IImageLoader接口,解决了接口兼容性问题,我们的下面两行代码就安居乐业了,再也不用担心被删除:

IImagerLoader mImageLoader=ImageLoaderFactory.createImageLoader();
//显示图片
mImageLoader.displayImage(imageView,url)

想要替换成不同的图片框架,只需要修改createImageLoader方法的返回类型,如果想继续添加别的图片库,只需要使之于IImageLoader适配即可。

当然,因为本例子只是简单的用来讲解适配器模式,并不具有实用性,如果读者想看具有实用性的图片框架适配的代码,可以参考RxGalleryFinal这个github的库(提供了类似微信图片选择的功能)。研究其imageloader包下的代码即可:

这里写图片描述

其实适配器模式无所不在,ListView的Adapter,RecyclerView的Adapter,都是常见的体现。另外比如一些第三方播放器,如IjkMeidaPlayer除了提供了自己的播放器IjkMediaPlayer之外,对android原生的播放器也做了支持,其体现就是提供了AndroidMediaPlayer这个类。
AndroidMediaPlayer和IjkMediaPlayer都是抽象类AbstractMediaPlayer的子类,而AbstractMediaPlayer实现了Ijk提供的IMediaPlayer接口,也等同于AndroidMediaPlayer也实现了IMediaPlayer这个接口,这样android系统的播放器和Ijk自己的播放器因为接口兼容性问题而本来无法工作的两个系统完全的可以一起工作,这不正是适配器模式的强大之处的应用的体现么。具体的代码可以去github上看看Ijk的相关源码,此处不再赘述。

到此为止适配器模式简单说明完毕,如有不当之处,欢迎批评指正

你可能感兴趣的:(设计模式之适配器模式)