前言
android開發中,必要的瞭解一些設計模式又是非常有必要的。 Android開發的設計模式,基本設計思想源於java的設計模式
java的設計模式有N多種,據不完全統計,迄今爲止,網絡出現最頻繁的大概有23種。
設計模式的出現就是爲了高質量、易維護和複用性強的代碼
什麼是設計模式?
- 基本定義:設計模式(Design pattern)是一套被反覆使用的代碼設計經驗的總結。
- 使用設計模式的目的是爲了可重用代碼、讓代碼更容易被他人理解。
- 設計模式是是軟件工程的基石脈絡,如大廈的結構一樣。
- Design pattern的四大要素:模式名稱(Name),問題(Question),解決方案(Solution),效果(Efftive)。
- OO(面向對象)的六大原則:單一職責原則,開閉原則,里氏替換原則,依賴倒置原則,接口隔離原則,迪米特原則。
- 單一職責原則:一個類中應該是一組相關性很高的函數,數據的封裝。兩個完全不一樣的功能就不應該放在一個類中。
- 開閉原則:對修改封閉,對擴展放開。
- 里氏替換原則:抽象和繼承;所有引用基類的地方必須能透明的使用其子類的對象。
- 依賴倒置原則:抽象不應該依賴細節,細節應該依賴抽象。
- 接口隔離原則:將大接口改成多個小接口。
- 迪米特原則:也稱爲最少知識原則,一個對象應該對另一個對象有最少的瞭解。
設計模式的分類
設計模式分爲三種類型:
- 創建型模式5種:單例模式,抽象工廠模式,工廠模式,原型模式,建造者模式。
- 結構型模式7種:適配器模式,橋接模式,裝飾模式,組合模式,外觀模式,享元模式,代理模式。
- 行爲型模式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之大話設計模式》