不学网

 找回密码
 立即注册

只需一步,快速开始

手机号码,快捷登录

查看: 160|回复: 0

[java] Design Patterns in Android:工厂方法模式

[复制链接]
Ricardo 发表于 2018-6-13 18:01:59 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
前言今天给大家分享的是《设计模式Android篇:工厂方法模式》。  
工厂方法是创建型模式的一种,可用来在适当的场合创建对象。今天将通过Android源码和Android开发案例跟大家讲解什么是工厂方法模式。

工厂方法模式定义
  工厂方法模式(Factory method pattern)定义一个接口用于创建对象,但是让子类决定初始化哪个类。工厂方法把一个类的初始化下放到子类。

工厂方法模式UML类图

工厂方法各角色讲解
  • IFactory:工厂接口,生产IProduct实例
  • IProduct:产品接口/抽象类
  • FactoryA:生产ProductA产品
  • FactoryB:生产ProductB产品
  • ProductA:一种产品,代号为A

工厂方法模式示例代码
  1. <p>IFactory</p>



  2. <pre class="prettyprint" name="code"><code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> IFactory {
  3.     IProduct create();
  4. }</code></pre>
复制代码
  1. <p>IProduct </p>



  2. <pre class="prettyprint" name="code"><code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> IProduct {
  3.     <span class="hljs-keyword">void</span> doSomething();
  4. }</code></pre>
复制代码
  1. <p>ProductA</p>



  2. <pre class="prettyprint" name="code"><code class="hljs java has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ProductA</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">IProduct</span> {</span>

  3.     <span class="hljs-annotation">@Override</span>
  4.     <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">doSomething</span>() {
  5.         System.out.println(<span class="hljs-string">"lalala...我是IProduct的实现类A。"</span>);
  6.     }

  7. }</code></pre>
复制代码
  1. <p>FactoryA</p>



  2. <pre class="prettyprint" name="code"><code class="hljs java has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">IFactory</span> {</span>
  3.     <span class="hljs-keyword">public</span> IProduct <span class="hljs-title">create</span>() {
  4.         <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ProductA();
  5.     }

  6. }</code></pre>
复制代码

但是:上述的标准的工厂方法模式通常伴随着对象的具体类型与工厂具体类型的一一对应,客户端代码根据需要选择合适的具体类型工厂使用,这种选择可能包含复杂的逻辑。
就好比说,我有10种产品,到生产的时候,你要我高层模块去根据产品选择合适的工厂来生产吗?所以就应运而生了一种-简单工厂模式:简单工厂类有一个静态工厂方法,可以根据高层模块的输入参数而生产合适的产品返回,比如:


  1. <pre class="prettyprint" name="code"><code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> SimpleFactory {

  2.     <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span>  <T extends IProduct> T <span class="hljs-title">create</span>(Class<T> clazz) {
  3.         IProduct product = <span class="hljs-keyword">null</span>;
  4.         <span class="hljs-keyword">try</span> {
  5.             product = (IProduct) Class.forName(clazz.getName()).newInstance();
  6.         } <span class="hljs-keyword">catch</span> (InstantiationException e) {
  7.             e.printStackTrace();
  8.         } <span class="hljs-keyword">catch</span> (IllegalAccessException e) {
  9.             e.printStackTrace();
  10.         } <span class="hljs-keyword">catch</span> (ClassNotFoundException e) {
  11.             e.printStackTrace();
  12.         }

  13.         <span class="hljs-keyword">return</span> (T) product;
  14.     }
  15. }</code></pre>
复制代码


上述的简单工厂例子中使用了Class作为输入参数,这是不固定,参数也可以是其他,大家根绝场景自定义。
Android源码中的工厂方法模式暂时未在Framework常用类中找到很纯粹的工厂方法模式,此处待补充。有同学知道的请留言,谢谢!

Android开发中的工厂方法模式实践
假如:你在项目中使用了Universal Image Loader作为图片加载框架,过一段时间后,发现UIL已经不流行了,想用更加fashion的Glide来代替土老帽UIL。咋办?新手程序员,可能就想,既然这样,那就改呗,所有用到UIL的地方,统统抛掉,抛掉。项目小还好,改吧改吧就完了,项目大点,改坏了咋整?版本回退?可惜的是,很多新手程序员可能连版本控制不用。万一要是哪一天,连Glide都不更新了,或者说不fashion了,又要换其他图片加载框架,难道又改吗?人都要疯了好吗?
这个时候,工厂方法可能可以帮上忙:使用工厂类隔离图片加载的具体实现,对外只暴露一个工厂方法用来外部生产想要的加载框架实例,就可避免上述提到的尴尬。口说无凭,上代码:
图片加载接口
  1. <pre class="prettyprint" name="code"><code class="hljs java has-numbering"><span class="hljs-javadoc">/**
  2. * 图片加载接口
  3. */</span>
  4. <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">ImageLoaderInterf</span> {</span>
  5.     interface CallBack {
  6.         <span class="hljs-keyword">void</span> onSuccess(Bitmap result);
  7.         <span class="hljs-keyword">void</span> onFailure();
  8.     }

  9.     <span class="hljs-keyword">void</span> load(Context context, String imgUrl, ImageView view);
  10. }</code></pre>
复制代码

图片加载工厂类
  1. <pre class="prettyprint" name="code"><code class="hljs java has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ImgLoaderClientFactory</span> {</span>
  2.     <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> GLIDE = <span class="hljs-number">0</span>;
  3.     <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> UIL = <span class="hljs-number">1</span>;
  4.     <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> PICASSO = <span class="hljs-number">2</span>;

  5.     <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> ImageLoaderInterf <span class="hljs-title">getImageLoaderClient</span>(<span class="hljs-keyword">int</span> type) {
  6.         <span class="hljs-keyword">switch</span> (type) {
  7.             <span class="hljs-keyword">case</span> GLIDE:
  8.                 <span class="hljs-keyword">return</span> GlideClient.getInstance();

  9.             <span class="hljs-keyword">case</span> UIL:
  10.                 <span class="hljs-keyword">return</span> UilClient.getInstance();

  11.             <span class="hljs-keyword">default</span>:
  12.                 <span class="hljs-keyword">return</span> PicassoClient.getInstance();
  13.         }
  14.     }
  15. }</code></pre>
复制代码

UilClient:Universal Image Loader封装
  1. <pre class="prettyprint" name="code"><code class="hljs java has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UilClient</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ImageLoaderInterf</span> {</span>

  2.     <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> UilClient sInstance;

  3.     <span class="hljs-keyword">private</span> <span class="hljs-title">UilClient</span>() {}

  4.     <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> UilClient <span class="hljs-title">getInstance</span>() {
  5.         <span class="hljs-keyword">synchronized</span> (UilClient.class) {
  6.             <span class="hljs-keyword">if</span> (sInstance == <span class="hljs-keyword">null</span>) {
  7.                 sInstance = <span class="hljs-keyword">new</span> UilClient();
  8.             }
  9.         }
  10.         <span class="hljs-keyword">return</span> sInstance;
  11.     }

  12.     <span class="hljs-annotation">@Override</span>
  13.     <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">load</span>(Context context, String imgUrl, ImageView view) {
  14.         ImageLoader.getInstance().displayImage(imgUrl, view);
  15.     }
  16. }</code></pre>
复制代码

GlideClient:Glide的二次封装
  1. <pre class="prettyprint" name="code"><code class="hljs java has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GlideClient</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ImageLoaderInterf</span> {</span>
  2.     <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> GlideClient sInstance;

  3.     <span class="hljs-keyword">private</span> <span class="hljs-title">GlideClient</span>() {}

  4.     <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> GlideClient <span class="hljs-title">getInstance</span>() {
  5.         <span class="hljs-keyword">synchronized</span> (GlideClient.class) {
  6.             <span class="hljs-keyword">if</span> (sInstance == <span class="hljs-keyword">null</span>) {
  7.                 sInstance = <span class="hljs-keyword">new</span> GlideClient();
  8.             }
  9.         }
  10.         <span class="hljs-keyword">return</span> sInstance;
  11.     }

  12.     <span class="hljs-annotation">@Override</span>
  13.     <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">load</span>(Context context, String imgUrl, ImageView view) {
  14.         Glide.with(context).load(imgUrl).into(view);
  15.     }
  16. }</code></pre>
复制代码

PicassoClient:Picasso封装类
  1. public class PicassoClient implements ImageLoaderInterf {

  2.     private static PicassoClient sInstance;

  3.     private PicassoClient() {
  4.     }

  5.     public static PicassoClient getInstance() {
  6.         synchronized (PicassoClient.class) {
  7.             if (sInstance == null) {
  8.                 sInstance = new PicassoClient();
  9.             }
  10.         }
  11.         return sInstance;
  12.     }

  13.     @Override
  14.     public void load(Context context, String imgUrl, ImageView view) {
  15.         Picasso.with(context).load(imgUrl).into(view);
  16.     }
  17. }
复制代码

那么加载图片就变成了下面这样:
  1. <code class="hljs avrasm has-numbering">ImgLoaderClientFactory<span class="hljs-preprocessor">.getImageLoaderClient</span>(ImgLoaderClientFactory<span class="hljs-preprocessor">.UIL</span>)<span class="hljs-preprocessor">.load</span>(mContext, imgUrl, imageView)<span class="hljs-comment">;</span></code>
复制代码

要切换图片框架呢?怎么办?全局搜索替换ImgLoaderClientFactory.UIL即可,比如想切到Glide,将用到ImgLoaderClientFactory.UIL地方改成ImgLoaderClientFactory.GLIDE即可。
  1. <code class="hljs avrasm has-numbering">ImgLoaderClientFactory<span class="hljs-preprocessor">.getImageLoaderClient</span>(ImgLoaderClientFactory<span class="hljs-preprocessor">.GLIDE</span>)<span class="hljs-preprocessor">.load</span>(mContext, imgUrl, imageView)<span class="hljs-comment">;</span></code>
复制代码

总结工厂方法适用于:隔离客户端与待使用的目标产品,用工厂来生产目标产品即可。
                                                                       
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|不学网

GMT+8, 2018-8-18 10:59

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表