盤點 Android 你用著卻不一定知道的設計模式(上)

幕後眼光發表於2019-03-04

前言

當自己的編碼時間久了之後,會發現優秀的程式碼,往往是遵循合理的設計模式進行開發的,這些程式碼具備高內聚、低耦合的特性,能夠在隨時變化的需求中,保持穩定性、靈活性。

本文,是在 Android 程式碼中去尋找「設計模式」的影子,並不會很詳細地展開各個模式的定義與應用。文中選取的例子都是儘量簡單易懂的,主要讓大家知道平時原來自己也是用著各種設計模式,只是不知道名字而已,開始盤它!

篇幅有限且網上優秀的書籍多,所以不要想著在這一篇文章弄清楚它們。注:本人水平有限,不對的地方,還請指出修正)。

一、單例模式

記得曾經筆試時就考過寫出單例模式的實現方式:1、懶漢式(執行緒安全);2、餓漢式(DCL);3、靜態內部類;4、列舉實現(最佳實現)。

當某個物件的建立是比較耗時的,如果頻繁的建立與銷燬的話,對效能影響又大,既然沒有好的辦法優化,那就在記憶體中持有這個物件的唯一例項,減少記憶體佔用。

值得注意的是:1、單例物件建立的執行緒安全問題;2、Android 中建立單例時,如果持有 Context 容易導致記憶體洩露,儘量使用 Application Context。

例如 GlideImageLoader 圖片載入框架,正是採用單例模式,來獲取例項物件的。

     1、Glide.with(context).load(imageUrl).into(imageView);
     
     2、ImageLoader.getInstance().displayImage(imageUrl,imageView);
複製程式碼

二、Builder 模式

構建者模式把一個物件的建立與表示分離開了,也就是說構建的過程不同,會生產出不同的物件出來。同時 Builder 類,把具體產品的建立細節隱藏了,使得我們不用關注產品具體是怎麼實現的。例如:我們無需關注呼叫方法的順序,因為 Builder 類已經封裝好了呼叫的順序了。還有這種鏈式呼叫寫起來真的很爽!

最常見的就是我們 Android 裡面的對話方塊的建立過程了,我們通過 AlertDialog.Builder() 構建的過程中,有沒有設定按鈕、標題、提示等,其實建立出來的對話方塊風格是不一樣的。

    1、new AlertDialog.Builder()
            .setTitle("title")
            .setMessage("message")
            .setPositiveButton("ok", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                    // do something
                }
            }).create();
            
    2、new StringBuilder().append("A").append("B").append("C").toString();
複製程式碼

這裡介紹一個 Android Studio 的外掛 —— Builder Generator,這個外掛可以省去手寫 Builder 的煩惱。

三、工廠模式

有以下幾種:

  1. 簡單工廠:含靜態方法,也叫靜態工廠,多用 if...else 來做分支,去建立各個例項,方法內部如果建立的物件多的,會略顯臃腫
  2. 工廠模式:含抽象工廠、具體工廠、抽象產品、具體產品之分,符合“開閉原則”
  3. 抽象工廠模式:與工廠模式的區別,工廠模式建立單一產品,抽象工廠能建立多種產品,符合“開閉原則”

工廠類封裝好類的例項化過程,隱藏了物件例項化的具體引數,只需傳入要建立類的唯一標識,工廠類就能建立出指定的類。換句話說:我只告訴工廠我要什麼,工廠只負責生產,我負責使用,具體工廠怎麼生產,我就不管啦~

    public class ConcreteFactory extends Factory {
        public <T extends Product> T createProduct(Class<T> c){
            Product product=null;
            try {
                product = (Product)Class.forName(c.getName()).newInstance();
            } catch (Exception e) {
                //異常處理
            }
            return (T)product;
        }
    }
    
    
    Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/bitmap.png");
複製程式碼

Retrofit 可新增 Gson 轉換(這裡體現了 Builder 模式 和工廠模式

    Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl(API_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
複製程式碼

四、策略模式

將演算法單獨封裝起來,使之替換時,互不影響。

之前看過一篇文章說怎麼消除專案程式碼中的 if...else ,運用策略模式,將每個 if...else 裡面的方法各自封裝,來解決因為大量的 if...else 導致的類臃腫

例如:android 中的設定動畫的插值器,替換不同插值器,各不影響,而且效果不同

    Animation animation = new AlphaAnimation(1,0);
    animation.setInterpolator(new AccelerateDecelerateInterpolator());
    imageView.setAnimation(animation);
    animation.start();
複製程式碼

五、模版模式

在抽象類中,定義模版(抽象)方法,並在子類做具體的實現。其實就是我們經常用的 BaseActivityBaseFragment 那套東西,show code~

BaseActivity 裡面保證模版方法,按照順序執行,同時子類必須實現父類定義的抽象方法,提高了複用性以及擴充套件性。

    public abstract class BaseActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            // 1、獲取佈局id
            setContentView(getLayoutId());
            // 2、初始化 view
            initView();
            // 3、初始化資料
            initData();
        }
        
        public abstract int getLayoutId();
    
        public abstract void initView();
    
        public abstract void initData();
    }
複製程式碼

六、介面卡模式

介面卡模式,可以解決介面不相容的問題,使得本不相容的介面一起工作。

舉例:港版 iPhone 的充電器是三孔插頭,可是現在房間只有二孔插頭,所以我得網上買個三孔轉二孔的轉換器(相當於介面卡),這樣我的三孔充電器就能在二孔插座使用了。

我們常用 ListView 使用的 Adapter ,用的就是介面卡模式,Google 開發工程師,設計程式碼的時候,考慮到 ListView 每個 ItemView 有不同 UI為了應對這種可變性,BaseAdapter 提供 getView() 方法,以保證最後輸出的統一為 View

    public class MyAdapter extends BaseAdapter {
        @Override
        public int getCount() {
            return 0;
        }
    
        @Override
        public Object getItem(int position) {
            return null;
        }
    
        @Override
        public long getItemId(int position) {
            return 0;
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            return null;
        }
    }
複製程式碼

七、觀察者模式

觀察者模式,多以一對多的形式依賴存在。多個觀察者同時監聽著被監聽的物件時,當被監聽的物件發生狀態變化時候,會通知所有觀察者更新。

如:郵件的訂閱功能,訂閱某個模組,當這個模組有新的內容更新,會給所有訂閱者傳送郵件。

Android 中,觀察者模式使用的是比較頻繁的,例如:EventBusRxJava等。最熟悉的就是 AdapternotifyDataSetChanged() 方法了,大家可以點進去看原始碼,當資料發生改變的時候,通知 itemView 重新佈局。

   /**
   * 觀察者集合
   */
   private final DataSetObservable mDataSetObservable = new DataSetObservable();
   
   //此處,省略很多程式碼...
   
    /**
     * Notifies the attached observers that the underlying data has been changed
     * and any View reflecting the data set should refresh itself.
     */
    public void notifyDataSetChanged() {
        mDataSetObservable.notifyChanged();
    }
複製程式碼

推薦閱讀

更多技術分享,請關注微信公眾號——碼農茅草屋:

碼農茅草屋

相關文章