关闭

Android源码中的静态工厂方法

标签: 设计模式工厂模式简单工厂模式静态工厂方法Android源码
1730人阅读 评论(1) 收藏 举报
分类:

我们知道工厂模式有三兄弟,通常我们说的工厂模式指的是工厂方法模式,它的应用频率最高。本篇博客分享的简单工厂模式是工厂方法模式的“小弟”,确切的来讲它不属于设计模式,而是一种方法。此外,工厂方法模式还有一位“大哥”——抽象工厂模式。

今天我们来分享一下简单工厂模式的一些情况,以及它在Android源码中的应用。

简单工厂模式

定义

简单工厂模式是类的创建模式,又叫做静态工厂方法(Static Factory Method)模式。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。

结构

简单工厂模式所涉及到的角色:

  • Product(抽象产品角色):产品的通用接口,定义产品的行为。
  • ConcreteProduct(具体产品角色):具体产品类,实现了Product接口。
  • Creator(工厂角色):工厂类,通过静态工厂方法factoryMethord来创建对象。

实现

抽象产品角色

abstract class Product {  
    //所有产品类的公共业务方法  
    public void methodSame() {  
        //公共方法的实现  
    }  

    //声明抽象业务方法  
    public abstract void methodDiff();  
}

具体产品角色

class ConcreteProduct extends Product {  
    //实现业务方法  
    public void methodDiff() {  
        //业务方法的实现  
    }  
}

工厂角色

class Creator {  
    //静态工厂方法  
    public static Product getProduct(String arg) {  
        Product product = null;  
        if (arg.equalsIgnoreCase("A")) {  
            product = new ConcreteProductA();  
            //初始化设置product  
        }  
        else if (arg.equalsIgnoreCase("B")) {  
            product = new ConcreteProductB();  
            //初始化设置product  
        }  
        return product;  
    }  
}

使用场景

在以下情况下可以考虑使用简单工厂模式:

  1. 工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。
  2. 客户端只知道传入工厂类的参数,对于如何创建对象并不关心。

优点

  • 工厂类包含必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的职责,而仅仅“消费”产品,简单工厂模式实现了对象创建和使用的分离。

  • 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以在一定程度减少使用者的记忆量。

缺点

  • 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。
  • 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。

Android中简单工厂模式的应用

在Android中我们了解的使用到了简单工厂方法的地方有Bitmap对象的获取、Fragment创建等。接下来我们分开看一下。

Bitmap源码分析

首先来说我们是不能通过new方法来创建Bitmap对象的,因为Bitmap类的构造函数是私有的,只能是通过JNI实例化。

接下来我们随便找个入口开始看,比如:

Bitmap bmp = BitmapFactory.decodeFile(String pathName);

我们把源码中的调用关系找出来,如下

public static Bitmap decodeFile(String pathName) {
    return decodeFile(pathName, null);
}

public static Bitmap decodeFile(String pathName, Options opts) {
    Bitmap bm = null;
    InputStream stream = null;
    try {
        stream = new FileInputStream(pathName);
        bm = decodeStream(stream, null, opts);
    } catch (Exception e) {
        /*  do nothing.
            If the exception happened on open, bm will be null.
        */
        Log.e("BitmapFactory", "Unable to decode stream: " + e);
    } finally {
        if (stream != null) {
            try {
                stream.close();
            } catch (IOException e) {
                // do nothing here
            }
        }
    }
    return bm;
}

public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
    // we don't throw in this case, thus allowing the caller to only check
    // the cache, and not force the image to be decoded.
    if (is == null) {
        return null;
    }

    Bitmap bm = null;

    Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap");
    try {
        if (is instanceof AssetManager.AssetInputStream) {
            final long asset = ((AssetManager.AssetInputStream) is).getNativeAsset();
            bm = nativeDecodeAsset(asset, outPadding, opts);
        } else {
            bm = decodeStreamInternal(is, outPadding, opts);
        }

        if (bm == null && opts != null && opts.inBitmap != null) {
            throw new IllegalArgumentException("Problem decoding into existing bitmap");
        }

        setDensityFromOptions(bm, opts);
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
    }

    return bm;
}

private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,
        Rect padding, Options opts);

/**
 * Set the newly decoded bitmap's density based on the Options.
 */
private static void setDensityFromOptions(Bitmap outputBitmap, Options opts) {
    if (outputBitmap == null || opts == null) return;

    final int density = opts.inDensity;
    if (density != 0) {
        outputBitmap.setDensity(density);
        final int targetDensity = opts.inTargetDensity;
        if (targetDensity == 0 || density == targetDensity || density == opts.inScreenDensity) {
            return;
        }

        byte[] np = outputBitmap.getNinePatchChunk();
        final boolean isNinePatch = np != null && NinePatch.isNinePatchChunk(np);
        if (opts.inScaled || isNinePatch) {
            outputBitmap.setDensity(targetDensity);
        }
    } else if (opts.inBitmap != null) {
        // bitmap was reused, ensure density is reset
        outputBitmap.setDensity(Bitmap.getDefaultDensity());
    }
}

我们来分析一下调用过程,可以看到decodeFile(String pathName)调用的是decodeFile(String pathName, Options opts),在两个参数的decodeFile方法中又去调用了decodeStream(InputStream is, Rect outPadding, Options opts)方法,然后最终调用nativeDecodeAsset或者nativeDecodeStream来构建Bitmap对象,这两个都是native方法(Android中使用Skia库来解析图像 )。再经过setDensityFromOptions方法的一些设置解码密度之类的操作,返回我们要的Bitmap对象。

/**
* Creates Bitmap objects from various sources, including files, streams, and byte-arrays.
*/

看下BitmapFactory的注释我们可以看到,这个工厂支持从不同的资源创建Bitmap对象,包括files, streams, 和byte-arrays,但是调用关系都大同小异。

Fragment创建

有时候,为了简化简单工厂模式,我们可以将抽象产品类和工厂类合并,将静态工厂方法移至抽象产品类中。Fragment的创建使用简单工厂方法没有抽象产品类,所以工厂类放到了实现产品类中。

在AndroidStudio中输入newInstance会自动补全Fragment的简单工厂方法。

public static TasksFragment newInstance() {

    Bundle args = new Bundle();

    TasksFragment fragment = new TasksFragment();
    fragment.setArguments(args);
    return fragment;
}

使用静态工厂方法,将外部传入的参数可以通过Fragment.setArgument保存在它自己身上,这样我们可以在Fragment.onCreate(…)调用的时候将这些参数取出来。

这样写有什么好处呢?

  • 避免了在创建Fragment的时候无法在类外部知道所需参数的问题。

  • Fragment推荐使用setArguments来传递参数,避免在横竖屏切换的时候Fragment自动调用自己的无参构造函数,导致数据丢失。

2
0
查看评论
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

Android源码分析之工厂方法模式

Android源码之工厂方法模式 模式的定义 定义一个用户创建对象的接口,让子类决定将哪一个类实例化。工厂方法使一个类的实例化延迟到子类。
  • bboyfeiyu
  • bboyfeiyu
  • 2014-04-21 17:41
  • 3393

Android源码分析之简单工厂模式

Android源码设计模式之简单工厂
  • bboyfeiyu
  • bboyfeiyu
  • 2014-04-17 17:13
  • 4944

Android中的工厂方法模式

写在前面的话在上一篇Android源码中的静态工厂方法中我门一起研究了工厂模式三兄弟中最简单的静态工厂方法。今天我们来一起看看三兄弟中的老二————工厂方法模式,以及它在Android源码中的体现。
  • sdkfjksf
  • sdkfjksf
  • 2016-09-29 11:39
  • 1821

java/android 设计模式学习笔记(3)---工厂方法模式

这篇来介绍一下工厂方法模式(Factory Method Pattern),在实际开发过程中我们都习惯于直接使用 new 关键字用来创建一个对象,可是有时候对象的创造需要一系列的步骤:你可能需要计算或...
  • zhao_zepeng
  • zhao_zepeng
  • 2016-05-21 20:36
  • 9017

Android设计模式(四)- 工厂方法模式

工厂方法模式也是创建型模式。 工厂模式根据抽象程度可以分为三种:简单工厂模式,工厂方法模式,抽象工厂模式。定义定义一个用于创建对象的接口,让子类决定实例化哪一个类。使用场景任何需要生成复杂对象的地方...
  • qq_25806863
  • qq_25806863
  • 2017-03-27 23:55
  • 1157

Android设计模式(四)- 工厂方法模式

工厂方法模式也是创建型模式。 工厂模式根据抽象程度可以分为三种:简单工厂模式,工厂方法模式,抽象工厂模式。定义定义一个用于创建对象的接口,让子类决定实例化哪一个类。使用场景任何需要生成复杂对象的地方...
  • qq_25806863
  • qq_25806863
  • 2017-03-27 23:55
  • 1157

设计模式之静态工厂、工厂方法和抽象工厂的联系与区别

解析: 开闭原则:对扩展开放,对修改封闭。静态工厂增加需要是修改源代码,对修改不封闭,不符合开闭原则。
  • sinat_35512245
  • sinat_35512245
  • 2017-02-06 11:31
  • 1989

Java静态工厂方法 —— 有了它,你还需要工厂模式吗

本文结合《Effective Java》第一章第一条目《考虑用静态工厂方法代替构造器》和自己的理解及实践,讲解了Java静态工厂方法的知识点,并在文末附上了自己对静态工厂方法和工厂模式的区别的理解,文...
  • hzy38324
  • hzy38324
  • 2017-05-28 10:41
  • 1807

Android 源码中的静态工厂方法

参见:点击打开链接 我们知道工厂模式有三兄弟,通常我们说的工厂模式指的是工厂方法模式,它的应用频率最高。本文分享的简单工厂模式是工厂方法模式的“小弟”,确切的来讲它不属于设计模式,而...
  • xhmj12
  • xhmj12
  • 2016-11-04 10:39
  • 196

audio_quality中的工厂方法 源码学习

audiosignal工厂 #ifndef CTSAUDIO_AUDIOSIGNALFACTORY_H #define CTSAUDIO_AUDIOSIGNALFACTORY_H #include ...
  • u010144805
  • u010144805
  • 2018-01-05 13:46
  • 43
    个人资料
    • 访问:193895次
    • 积分:2235
    • 等级:
    • 排名:第19362名
    • 原创:40篇
    • 转载:0篇
    • 译文:3篇
    • 评论:105条
    我的微信公众号
    长期为您推荐优秀博文、开源项目、视频等,进入还有好玩的等着你。扫一扫下方二维码或搜索微信号codertopia即可关注:
    我的

    QQ群:469291650


    GitHub
    我的个人博客
    博客专栏
    Relax
    最新评论