[進階] Android設計模式 一

前言

android開發中,必要的瞭解一些設計模式又是非常有必要的。 Android開發的設計模式,基本設計思想源於java的設計模式

java的設計模式有N多種,據不完全統計,迄今爲止,網絡出現最頻繁的大概有23種。

設計模式的出現就是爲了高質量、易維護和複用性強的代碼

什麼是設計模式?

  • 基本定義:設計模式(Design pattern)是一套被反覆使用的代碼設計經驗的總結。
    • 使用設計模式的目的是爲了可重用代碼、讓代碼更容易被他人理解。
    • 設計模式是是軟件工程的基石脈絡,如大廈的結構一樣。
  • Design pattern的四大要素:模式名稱(Name),問題(Question),解決方案(Solution),效果(Efftive)。
  • OO(面向對象)的六大原則:單一職責原則,開閉原則,里氏替換原則,依賴倒置原則,接口隔離原則,迪米特原則。
    • 單一職責原則:一個類中應該是一組相關性很高的函數,數據的封裝。兩個完全不一樣的功能就不應該放在一個類中。
    • 開閉原則:對修改封閉,對擴展放開。
    • 里氏替換原則:抽象和繼承;所有引用基類的地方必須能透明的使用其子類的對象。
    • 依賴倒置原則:抽象不應該依賴細節,細節應該依賴抽象。
    • 接口隔離原則:將大接口改成多個小接口。
    • 迪米特原則:也稱爲最少知識原則,一個對象應該對另一個對象有最少的瞭解。

設計模式的分類

設計模式分爲三種類型:

  1. 創建型模式5種:單例模式,抽象工廠模式,工廠模式,原型模式,建造者模式。
  2. 結構型模式7種:適配器模式,橋接模式,裝飾模式,組合模式,外觀模式,享元模式,代理模式。
  3. 行爲型模式11種:觀察者模式,中介者模式,訪問者模式,解釋器模式,迭代器模式,備忘錄模式,責任鏈模式,狀態模式,策略模式,命令模式,模板模式。

創建型模式5種

1. 單例模式(Singleton Pattern)– Android常用模式

簡介

保證一個類僅有一個實例,全局只有一個訪問點。

對單例的實現可以分爲兩大類——懶漢式和餓漢式,他們的區別在於:

懶漢式:指全局的單例實例在第一次被使用時構建

餓漢式:指全局的單例實例在類裝載時構建。

從它們的區別也能看出來,日常我們使用的較多的應該是懶漢式的單例,畢竟按需加載才能做到資源的最大化利用

摘錄自這一篇聊一聊Java的單例

核心Code

實現方法詳解看七種方式實現Singleton模式

/**
 * 單例模式,使用靜態內部類,線程安全(推薦)
 */
public static class Singleton5 {
    private final static class SingletonHolder {
        private static final Singleton5 INSTANCE = new Singleton5();
    }

    public static Singleton5 getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

它利用了ClassLoader來保證了同步,同時又能讓開發者控制類加載的時機。從內部看是一個餓漢式的單例,但是從外部看來,又的確是懶漢式的實現。


/**
 * 靜態內部類,使用枚舉方式,線程安全(推薦)
 */
public enum Singleton6 {
    INSTANCE;
    public void whateverMethod() {

    }
}

這是極簡的寫法,利用了創建枚舉實例的過程是線程安全的。所以這種寫法也沒有同步的問題。但是enum比較耗資源,需要權衡。

/**
 * 靜態內部類,使用雙重校驗鎖,線程安全(推薦)
 */
public static class Singleton {
    private volatile static Singleton instance = null;

    private Singleton() {

    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

這是一種雙檢鎖實例的方式,只在第一次實例化的時候進行加鎖,並在在加鎖前後都會對是否實例化了進行判定.

性能方面優於單檢鎖的形式.爲了避免編譯器對new Singleton()進行優化,instance變量加上volatile修飾,去除編譯器優化的干擾. 

已有示例

Android中的系統級服務都是通過容器的單例模式實現方式,以單例形式存在,減少了資源消耗。

2. 工廠模式(Factory Pattern)

簡介

定義一個用於創建對象的接口,讓子類決定將哪一個類實例化。

核心Code

麥當勞的點餐,可以點可樂,漢堡這可以使用Builder模式,也可以點套餐,這個可以認爲是工廠模式

public class FactoryDemo {
    public static void start() {
        Order order = OrderFactory.createBigMacCombo();
        System.out.println(order.makeOrder());
    }
}

------------------------Factory

public class OrderFactory {
    //創建一份巨無霸套餐
    public static Order createBigMacCombo() {
        return new Order.OrderBuilder()
                .addBurger(new BigMac())
                .addBeverage(new Coke())
                .build();
    }
}

--------------------------Order

public class Order {
    private IBurgers mBurger;
    private IBeverages mBeverages;

    private Order(OrderBuilder builder){
        mBurger = builder. mBurger;
        mBeverages = builder. mBeverages;
    }


    public String makeOrder(){
        StringBuilder sb = new StringBuilder();
        if ( mBurger!= null) {
            sb.append( mBurger.makeBurger()).append( " ");
        }
        if ( mBeverages!= null) {
            sb.append( mBeverages.makeDrinking()).append( " ");
        }
        return sb.toString();
    }

    public static class OrderBuilder{
        private IBurgers mBurger;
        private IBeverages mBeverages;
        public OrderBuilder(){

        }
        public OrderBuilder addBurger(IBurgers burgers){
            this. mBurger = burgers;
            return this;
        }
        public OrderBuilder addBeverage(IBeverages beverages){
            this. mBeverages = beverages;
            return this;
        }

        public Order build(){
            return new Order( this);
        }
    }
}

---------------------------------Product

public class BigMac implements IBurgers {

    @Override
    public String makeBurger() {
        return "巨無霸";
    }

}

public class Coke implements IBeverages {

    @Override
    public String makeDrinking() {
        return "可樂";
    }

}

public interface IBeverages {
    String makeDrinking();
}

public interface IBurgers {
    String makeBurger();
}

已有示例

Android中,BitmapFactory用於從不同的數據源來解析、創建Bitmap對象

3. 抽象工廠模式(Abstract Factory Pattern)

簡介

提供一個創建一系列相關或相互依賴對象的接口,而無需指定它們具體的類。

一個用於創建對象的接口,讓子類決定實例化哪一個類,工廠方法使一個類的實例化延遲到其子類。

使用場景:一個對象族或者一組沒有任何關係的對象都有相同的約束,都可以使用抽象工廠模式

  • 當調用者需要一個產品時,直接傳遞一個參數給工廠,讓工廠生產不同的產品。
  • 這些產品實現了同樣的接口。
  • 調用者無需瞭解細節,只需要提要求(傳參數)給工廠即可。

角色介紹:

  • AbstractProduct: 抽象產品類
  • ConcreteProductA : 產品的具體實現A
  • ConcreteProductB : 產品的具體實現B
  • AbstractFactory : 抽象工廠
  • ConcreteFactory : 具體工廠實現

但是抽象工廠模式有個最大的缺點:產品族擴展非常困難,嚴重違反了開閉原則

核心Code

用戶只需要告訴Factory要的產品,不需要關心產品是如何生產的。不過每增加一樣產品,BaseAppFactory都要增加一個方法,然後所有的實現類都要修改

public class FactoryDemo {
    public static void start() {
        BaseAppFactory factory = new MacAppFactory();
        BaseTextEditor textEditor = factory.createTextEditor();
        textEditor.edit();
        textEditor.save();

        BaseImageEditor imageEditor = factory.createImageEditor();
        imageEditor.edit();
        imageEditor.save();
    }
}

--------------------Factory


public class MacAppFactory extends BaseAppFactory {
    @Override
    public BaseTextEditor createTextEditor() {
        return new MacTextEditor();
    }

    @Override
    public BaseImageEditor createImageEditor() {
        return new MacImageEditor();
    }
}

public abstract class BaseAppFactory {
    public abstract BaseTextEditor createTextEditor();

    public abstract BaseImageEditor createImageEditor();
}

-----------------Products

public class MacImageEditor extends BaseEditor {
    @Override
    public void edit() {
        System.out.println("圖片處理編輯器,edit -- Mac版");
    }

    @Override
    public void save() {
        System.out.println("圖片處理編輯器,save -- Mac版");
    }
}

public class MacTextEditor extends BaseEditor {
    @Override
    public void edit() {
        System.out.println("文本編輯器,edit -- Mac版");
    }

    @Override
    public void save() {
        System.out.println("文本編輯器,edit -- Mac版");
    }
}

public abstract class BaseEditor {
    public abstract void edit();

    public abstract void save();
}

已有示例

Android底層對MediaPlayer的創建。

MediaPlayerFactory是Android底層爲了創建不同的MediaPlayer所定義的一個類。

4. 原型模式(Prototype Pattern)– Android常用模式

簡介

用原型實例指定創建對象的種類,並且通過拷貝這個原型來創建新的對象。基本可以理解成實現了clone方法

使用場景:在系統中要創建大量的對象,這些對象之間具有幾乎完全相同的功能,只是在細節上有一點兒差別

  • 比如我們需要一張
  • 的幾種不同格式:ARGB_8888、RGB_565、ARGB_4444、ALAPHA_8等。

    那我們就可以先創建一個ARGB_8888的Bitmap作爲原型,在它的基礎上,通過調用Bitmap.copy(Config)來創建出其它幾種格式的Bitmap。

  • 另外一個例子就是Java中所有對象都有的一個名字叫clone的方法,已經原型模式的代名詞了。

核心Code

核心Code

注意像ListView這類的需要深度copy

public class Person implements Cloneable {
    private String name;
    private int age;
    private double height;
    private double weight;

    public Person() {

    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

    public double getWeight() {
        return weight;
    }

    public void setWeight(double weight) {
        this.weight = weight;
    }

    private ArrayList<String> hobbies = new ArrayList<String>();

    public ArrayList<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(ArrayList<String> hobbies) {
        this.hobbies = hobbies;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", height=" + height +
                ", weight=" + weight +
                '}';
    }

    @Override
    public Object clone() {
        Person person = null;
        try {
            person = (Person) super.clone();
            person.name = this.name;
            person.weight = this.weight;
            person.height = this.height;
            person.age = this.age;

            person.hobbies = (ArrayList<String>) this.hobbies.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return person;
    }
}

已有示例

Android中的Bundle類,該類實現了Cloneable接口

public Object clone() {
    return new Bundle(this);
} 
public Bundle(Bundle b) {
    super(b);

    mHasFds = b.mHasFds;
    mFdsKnown = b.mFdsKnown;
}

Intent類,該類也實現了Cloneable接口

@Override
public Object clone() {
    return new Intent(this);
}
public Intent(Intent o) {
    this.mAction = o.mAction;
    this.mData = o.mData;
    this.mType = o.mType;
    ......
}

使用的時候可以直接拷貝現有的Intent,再修改不同的地方,便可以直接使用。

Uri uri = Uri.parse("smsto:10086");    
Intent shareIntent = new Intent(Intent.ACTION_SENDTO, uri);    
shareIntent.putExtra("sms_body", "hello");    

Intent intent = (Intent)shareIntent.clone() ;
startActivity(intent);

開源庫OkHttp中,也應用了原型模式。它就在OkHttpClient這個類中,它實現了Cloneable接口

@Override 
public OkHttpClient clone() {
    return new OkHttpClient(this);
}
private OkHttpClient(OkHttpClient okHttpClient) {
    this.routeDatabase = okHttpClient.routeDatabase;
    this.dispatcher = okHttpClient.dispatcher;
    this.proxy = okHttpClient.proxy;
    ......
}

5. 建造者模式(Builder Pattern)– Android常用模式

簡介

將一個複雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。

核心Code

———–優化前

  • 參數的構造函數的最後面的兩個參數含義不清,可讀性不怎麼好,可能需要點開查看源碼
  • 當有很多參數時,編寫這個構造函數就會顯得異常麻煩

    Person p1=new Person();
    Person p2=new Person(“張三”);
    Person p3=new Person(“李四”,18);
    Person p4=new Person(“王五”,21,180);
    Person p5=new Person(“趙六”,17,170,65.4);

    public class Person {
    private String name;
    private int age;
    private double height;
    private double weight;

    public Person() {
    }
    
    public Person(String name) {
        this.name = name;
    }
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public Person(String name, int age, double height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }
    
    public Person(String name, int age, double height, double weight) {
        this.name = name;
        this.age = age;
        this.height = height;
        this.weight = weight;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        this.age = age;
    }
    
    public double getHeight() {
        return height;
    }
    
    public void setHeight(double height) {
        this.height = height;
    }
    
    public double getWeight() {
        return weight;
    }
    
    public void setWeight(double weight) {
        this.weight = weight;
    }
    

    }

———–優化後

如果換一個角度,試試Builder模式,可讀性可是嗖嗖的~

我們給Person增加一個靜態內部類Builder類,並修改Person類的構造函數。

創建過程一下子就變得非常清晰了。對應的值是什麼屬性一目瞭然,可讀性大大增強。

Person.Builder builder=new Person.Builder();
Person person=builder
    .name("張三")
    .age(18)
    .height(178.5)
    .weight(67.4)
    .build();

public class PersonNew {
    private String name;
    private int age;
    private double height;
    private double weight;

    private PersonNew(Builder builder) {
        this.name = builder.name;
        this.age = builder.age;
        this.height = builder.height;
        this.weight = builder.weight;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

    public double getWeight() {
        return weight;
    }

    public void setWeight(double weight) {
        this.weight = weight;
    }

    public static class Builder {
        private String name;
        private int age;
        private double height;
        private double weight;

        public Builder name(String name) {
            this.name = name;
            return this;
        }

        public Builder age(int age) {
            this.age = age;
            return this;
        }

        public Builder height(double height) {
            this.height = height;
            return this;
        }

        public Builder weight(double weight) {
            this.weight = weight;
            return this;
        }

        public PersonNew build() {
            return new PersonNew(this);
        }
    }
}

已有示例

在Android中, Builder模式也是被大量的運用。

比如常見的對話框的創建AlertDialog.Builder,ImageLoader的初始配置。

AlertDialog.Builder builder=new AlertDialog.Builder(this);
AlertDialog dialog=builder.setTitle("標題")
        .setIcon(android.R.drawable.ic_dialog_alert)
        .setView(R.layout.myview)
        .setPositiveButton(R.string.positive, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {

            }
        })
        .setNegativeButton(R.string.negative, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {

            }
        })
        .create();
dialog.show();

java中有兩個常見的類也是Builder模式:StringBuilder和StringBuffer

還有比較著名框架中:Gson中的GsonBuilder

GsonBuilder builder=new GsonBuilder();
Gson gson=builder.setPrettyPrinting()
        .disableHtmlEscaping()
        .generateNonExecutableJson()
        .serializeNulls()
        .create();

EventBus中也有一個Builder

EventBus(EventBusBuilder builder) {...}

OkHttp中的

Request.Builder builder=new Request.Builder();
Request request=builder.addHeader("","")
    .url("")
    .post(body)
    .build();

private Response(Builder builder) {...}

各大框架中大量的運用了Builder模式。

小結:

  • 定義一個靜態內部類Builder,內部的成員變量和外部類一樣
  • Builder類通過一系列的方法用於成員變量的賦值,並返回當前對象本身(this)
  • Builder類提供一個build方法或者create方法用於創建對應的外部類,該方法內部調用了外部類的一個私有構造函數,該構造函數的參數就是內部類Builder
  • 外部類提供一個私有構造函數供內部類調用,在該構造函數中完成成員變量的賦值,取值爲Builder對象中對應的值

設計模式專題

Github Code: https://github.com/vivianking6855/android-advanced/tree/master/DesignPattern

Reference

Android開發中常見的設計模式

Android設計模式之23種設計模式一覽

《Android深入透析》之常用設計模式經驗談

《android之大話設計模式》

設計模式中英文對照

Android 設計模式

發佈了153 篇原創文章 · 獲贊 20 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章