Android 設計模式之Builder模式

在平常開發過程當中時常須要用到設計模式,可是設計模式有23種,如何將這些設計模式瞭然於胸而且能在實際開發過程當中應用得駕輕就熟呢?和我一塊兒跟着《Android源碼設計模式解析與實戰》一書邊學邊應用吧!

設計模式系列文章


今天咱們要講的是Builder模式(建造者模式)

定義

將一個複雜對象的構建和它的表示分離,使得一樣的構建過程能夠建立不一樣的表示設計模式

使用場景

  • 當初始化一個對象特別複雜時,如參數多,且不少參數都具備默認值時
  • 相同的方法,不一樣的執行順序,產生不一樣的事件結果時
  • 多個部件或零件,均可以裝配到一個對象中,可是產生的運行效果又不相同時
  • 產品類很是複雜,或者產品類中的調用順序不一樣產生了不一樣的做用,這個時候使用建造者模式很是合適

使用例子

  • AlertDialog
  • universal-image-loader

實現

實現的要點

  • 簡言之,就是把須要經過set方法來設置的多個屬性封裝在一個配置類裏面
  • 每一個屬性都應該有默認值
  • 具體的set方法放在配置類的內部類Builder類中,而且每一個set方法都返回自身,以便進行鏈式調用

實現方式

下面以咱們的圖片加載框架ImageLoader爲例來看看Builder模式的好處

未採用Builder模式的ImageLoader
public class ImageLoader {
    //圖片加載配置
    private int loadingImageId;
    private int loadingFailImageId;

    // 圖片緩存,依賴接口
    ImageCache mImageCache = new MemoryCache();

    // 線程池,線程數量爲CPU的數量
    ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    //省略單例模式實現
    
    /**
     * 設置圖片緩存
     * @param cache
     */
    public void setImageCache(ImageCache cache) {
        mImageCache = cache;
    }

    /**
     * 設置圖片加載中顯示的圖片
     * @param resId
     */
    public Builder setLoadingPlaceholder(int resId) {
        loadingImageId = resId;
    }

    /**
     * 設置加載失敗顯示的圖片
     * @param resId
     */
    public Builder setLoadingFailPlaceholder(int resId) {
        loadingFailImageId = resId;
    }
    
    /**
     * 顯示圖片
     * @param imageUrl
     * @param imageView
     */
    public void displayImage(String imageUrl, ImageView imageView) {
        Bitmap bitmap = mImageCache.get(imageUrl);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
            return;
        }
        // 圖片沒有緩存,提交到線程池下載
        submitLoadRequest(imageUrl, imageView);
    }

    /**
     * 下載圖片
     * @param imageUrl
     * @param imageView
     */
    private void submitLoadRequest(final String imageUrl, final ImageView imageView) {
        imageView.setImageResource(loadingImageId);
        imageView.setTag(imageUrl);
        mExecutorService.submit(new Runnable() {
            @Override
            public void run() {
                Bitmap bitmap = downloadImage(imageUrl);
                if (bitmap == null) {
                    imageView.setImageResource(loadingFailImageId);
                    return;
                }
                if (imageUrl.equals(imageView.getTag())) {
                    imageView.setImageBitmap(bitmap);
                }
                mImageCache.put(imageUrl, bitmap);
            }
        });
    }

    /**
     * 下載圖片
     * @param imageUrl
     * @return
     */
    private Bitmap downloadImage(String imageUrl) {
        Bitmap bitmap = null;
        //省略下載部分代碼
        return bitmap;
    }
}
複製代碼

從上面的代碼中咱們能夠看出,每當須要增長一個設置選項的時候,就須要修改ImageLoader的代碼,違背了開閉原則,並且ImageLoader中的代碼會愈來愈多,不利於維護緩存

下面咱們來看看如何用Builder模式來改造ImageLoader
  • 首先是把ImageLoader的設置都放在單獨的配置類裏,每一個set方法都返回this,從而達到鏈式調用的目的
public class ImageLoaderConfig {
    // 圖片緩存,依賴接口
    public ImageCache mImageCache = new MemoryCache();

    //加載圖片時的loading和加載失敗的圖片配置對象
    public DisplayConfig displayConfig = new DisplayConfig();

    //線程數量,默認爲CPU數量+1;
    public int threadCount = Runtime.getRuntime().availableProcessors() + 1;

    private ImageLoaderConfig() {
    }


    /**
     * 配置類的Builder
     */
    public static class Builder {
        // 圖片緩存,依賴接口
        ImageCache mImageCache = new MemoryCache();

        //加載圖片時的loading和加載失敗的圖片配置對象
        DisplayConfig displayConfig = new DisplayConfig();

        //線程數量,默認爲CPU數量+1;
        int threadCount = Runtime.getRuntime().availableProcessors() + 1;

        /**
         * 設置線程數量
         * @param count
         * @return
         */
        public Builder setThreadCount(int count) {
            threadCount = Math.max(1, count);
            return this;
        }

        /**
         * 設置圖片緩存
         * @param cache
         * @return
         */
        public Builder setImageCache(ImageCache cache) {
            mImageCache = cache;
            return this;
        }

        /**
         * 設置圖片加載中顯示的圖片
         * @param resId
         * @return
         */
        public Builder setLoadingPlaceholder(int resId) {
            displayConfig.loadingImageId = resId;
            return this;
        }

        /**
         * 設置加載失敗顯示的圖片
         * @param resId
         * @return
         */
        public Builder setLoadingFailPlaceholder(int resId) {
            displayConfig.loadingFailImageId = resId;
            return this;
        }

        void applyConfig(ImageLoaderConfig config) {
            config.displayConfig = this.displayConfig;
            config.mImageCache = this.mImageCache;
            config.threadCount = this.threadCount;
        }

        /**
         * 根據已經設置好的屬性建立配置對象
         * @return
         */
        public ImageLoaderConfig create() {
            ImageLoaderConfig config = new ImageLoaderConfig();
            applyConfig(config);
            return config;
        }
    }
}
複製代碼
  • ImageLoader的修改
public class ImageLoader {
    //圖片加載配置
    ImageLoaderConfig mConfig;

    // 圖片緩存,依賴接口
    ImageCache mImageCache = new MemoryCache();

    // 線程池,線程數量爲CPU的數量
    ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    //省略單例模式實現
    
    //初始化ImageLoader
    public void init(ImageLoaderConfig config) {
        mConfig = config;
        mImageCache = mConfig.mImageCache;
    }
    
    /**
     * 顯示圖片
     * @param imageUrl
     * @param imageView
     */
    public void displayImage(String imageUrl, ImageView imageView) {
        Bitmap bitmap = mImageCache.get(imageUrl);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
            return;
        }
        // 圖片沒有緩存,提交到線程池下載
        submitLoadRequest(imageUrl, imageView);
    }

    /**
     * 下載圖片
     * @param imageUrl
     * @param imageView
     */
    private void submitLoadRequest(final String imageUrl, final ImageView imageView) {
        imageView.setImageResource(mConfig.displayConfig.loadingImageId);
        imageView.setTag(imageUrl);
        mExecutorService.submit(new Runnable() {
            @Override
            public void run() {
                Bitmap bitmap = downloadImage(imageUrl);
                if (bitmap == null) {
                    imageView.setImageResource(mConfig.displayConfig.loadingFailImageId);
                    return;
                }
                if (imageUrl.equals(imageView.getTag())) {
                    imageView.setImageBitmap(bitmap);
                }
                mImageCache.put(imageUrl, bitmap);
            }
        });
    }

    /**
     * 下載圖片
     * @param imageUrl
     * @return
     */
    private Bitmap downloadImage(String imageUrl) {
        Bitmap bitmap = null;
        //省略下載部分代碼
        return bitmap;
    }
}
複製代碼
  • 調用形式,是否是很熟悉?
ImageLoaderConfig config = new ImageLoaderConfig.Builder()
        .setImageCache(new MemoryCache())
        .setThreadCount(2)
        .setLoadingFailPlaceholder(R.drawable.loading_fail)
        .setLoadingPlaceholder(R.drawable.loading)
        .create();
ImageLoader.getInstance().init(config);
複製代碼

總結

  • 在構建的對象須要不少配置的時候能夠考慮Builder模式,能夠避免過多的set方法,同時把配置過程從目標類裏面隔離出來,代碼結構更加清晰
  • Builder模式比較經常使用的實現形式是經過鏈式調用實現,這樣更簡潔直觀

歡迎掃碼關注公衆號,我們一塊兒學習成長吧 bash

AntDream
相關文章
相關標籤/搜索