1. 程式人生 > >Android原始碼中的靜態工廠方法

Android原始碼中的靜態工廠方法

我們知道工廠模式有三兄弟,通常我們說的工廠模式指的是工廠方法模式,它的應用頻率最高。本篇部落格分享的簡單工廠模式是工廠方法模式的“小弟”,確切的來講它不屬於設計模式,而是一種方法。此外,工廠方法模式還有一位“大哥”——抽象工廠模式。

今天我們來分享一下簡單工廠模式的一些情況,以及它在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自動呼叫自己的無參建構函式,導致資料丟失。

相關推薦

Android原始碼靜態工廠方法

我們知道工廠模式有三兄弟,通常我們說的工廠模式指的是工廠方法模式,它的應用頻率最高。本篇部落格分享的簡單工廠模式是工廠方法模式的“小弟”,確切的來講它不屬於設計模式,而是一種方法。此外,工廠方法模式還有一位“大哥”——抽象工廠模式。 今天我們來分享一下簡單工廠

Android之大話設計模式--簡單工廠模式(靜態工廠方法模式)

簡單工廠模式解釋: 簡單工廠模式(Simple Factory Pattern)屬於類的創新型模式,又叫靜態工廠方法模式(Static Factory Method Pattern),是通過專門定義一個類來負責建立其他類的例項,被建立的例項通常都具有相同的父親。 UML圖:

【代碼優化】考慮使用靜態工廠方法取代構造器

ava tracking 什麽事 依據 mod true data -m span 靜態工廠方法與設計模式中的工廠方法模式不同,和設計模式中的工廠方法模式不直接相應。 使用靜態工廠方法比構造器的優勢: 第一、靜態工廠方法是有名稱的,而構造器是通過

EffectiveJava讀書筆記——考慮用靜態工廠方法代替構造器(一)

無法 父類 應該 樹結構 對象 如何 log 筆記 工廠類 參考網址:http://blog.csdn.net/mingyunduoshou/article/details/6149758 http:[email protect

靜態工廠方法+服務提供者框架模板

tro class return sse strong ava hash [] nes Provider.java public interface Provider { Service newService(); } Service.java public in

實例/靜態工廠方法得到bean

create spa tor bean .config return col pan creat <bean id="a" class="com.yundaex.wms.config.TestBeanChild" /> <bean id="b

靜態工廠方法代替構造器、遇到多個構造器參數時要考慮用構建器

泛型 不用 推斷 frame public 多參數 eof ram api 一、用靜態工廠方法代替構造器 類通過共有的構造方法可以提供很大的優點:1、構造方法可一有不同的名字,我們可以通過名字區分構造什麽樣子的對象,而構造器名字相同,當參數列表的數目相同 順序不同時 很大的

JavaScript工廠方法、構造函數與class

格式 ret 存在 mon pillar bug 多種方法 希望 journal JavaScript中的工廠方法、構造函數與class 本文轉載自:眾成翻譯 譯者:謝於中 鏈接:http://www.zcfy.cc/article/1129 原文:https://med

Effective Java 第三版——1. 考慮使用靜態工廠方法替代構造方法

plain 額外 body image 單獨 oba car 翻譯 一個 Tips 《Effective Java, Third Edition》一書英文版已經出版,這本書的第二版想必很多人都讀過,號稱Java四大名著之一,不過第二版2009年出版,到現在已經將近8年的時

【Effective Java讀書筆記】創建和銷毀對象(一):考慮使用靜態工廠方法代替構造器

返回對象 boolean 簡化 將不 其他 種類型 bigint color pre 類可以提供一個靜態方法,返回類的一個靜態實例,如Boolean包裝類的一個獲取實例的靜態方法 1 public static Boolean valueOf(boolean b) { 2

Spring(十三):使用工廠方法來配置Bean的兩種方式(靜態工廠方法&實例工廠方法

color 示例 簡單的 rgs icc tostring pac ng- clas 通過調用靜態工廠方法創建Bean 1)調用靜態工廠方法創建Bean是將對象創建的過程封裝到靜態方法中。當客戶端需要對象時,只需要簡單地調用靜態方法,而不需要關心創建對象的具體細節。 2

一、考慮使用靜態工廠方法替代構造函數

fault 變化 擁有 def enum 可選 不能 鼓勵 ice 1、何為靜態工廠方法   靜態工廠方法就是一個返回類實例的靜態方法。比如Boolean的valueof方法: 1 public static final Boolean TRUE = new Boolea

Java物件學習之建立物件——使用靜態工廠方法代替構造器缺點

最近學習Android知識,發現Java好多的基礎知識運用的真的很一般,所以決定在重新梳理一下,現在Kotlin也來了,在Android之路上,留給我看Java的時間不多了。       靜態我們就來簡單介紹一下,使用靜態工廠方法建立物件相較於構造器建立的物件的優缺

Java物件學習之建立物件——使用靜態工廠方法代替構造器優點(四)

最近學習Android知識,發現Java好多的基礎知識運用的真的很一般,所以決定在重新梳理一下,現在Kotlin也來了,在Android之路上,留給我看Java的時間不多了。       靜態我們就來簡單介紹一下,使用靜態工廠方法建立物件相較於構造器建立的物件的優缺

Java物件學習之建立物件——使用靜態工廠方法代替構造器優點(二)

最近學習Android知識,發現Java好多的基礎知識運用的真的很一般,所以決定在重新梳理一下,現在Kotlin也來了,在Android之路上,留給我看Java的時間不多了。       靜態我們就來簡單介紹一下,使用靜態工廠方法建立物件相較於構造器建立的物件的優缺

Java物件學習之建立物件——使用靜態工廠方法代替構造器優點(一)

      最近學習Android知識,發現Java好多的基礎知識運用的真的很一般,所以決定在重新梳理一下,現在Kotlin也來了,在Android之路上,留給我看Java的時間不多了。       靜態我們就來簡單介紹一下,使用靜態工

Android原始碼引用@hide類出現引用異常的問題error: cannot find symbol

    自己開發的APP在Android中使用一些系統隱藏的類         編譯的時候報錯,出現如下異常 error: cannot find symbol import android.net.EthernetM

Effective Java 第三版讀書筆記——條款1.考慮使用靜態工廠方法替代構造器

lean jdb 底層 public lasso 基本 win inter nds 獲取一個類的實例的傳統方法是使用公開的構造器,除此之外,一個類還可以提供公開的靜態工廠方法(static factory method)來返回它的實例。例如 Boolean 類中的 valu

Java中用靜態工廠方法代替構造器的優缺點

Effective Java學習筆記,靜態工廠方法的善用。 一般情況下,對於類而言,我們獲取一個類的例項時,最常用的方法是提供一個公有的構造器。 但是還有一種方法,類可以提供一個公有的靜態工廠方法,它只是個返回類的例項的靜態方法而已。但是靜態工廠方法卻又許多妙用之處。

Effective Java 第三版閱讀筆記——條款1.考慮使用靜態工廠方法替代構造器

獲取一個類的例項的傳統方法是使用公開的構造器,除此之外,一個類還可以提供公開的靜態工廠方法(static factory method)來返回它的例項。例如 Boolean 類中的 valueOf 方法,這個方法將基本型別 boolean 轉換為一個 Boolean 物件的引用: pub