1. 程式人生 > >Java[Android]設計模式建立型設計模式之Builder模式+單例模式+原型模式

Java[Android]設計模式建立型設計模式之Builder模式+單例模式+原型模式

1. 寫在前面

寫部落格純屬為自己記一個筆記方便自己以後檢視,希望對路過的朋友有幫助;若有偏頗請朋友及時指正。

本文主要介紹在建立型設計模式中的Builder模式、單單例模式和原型模式。

2. Builder模式

Builder模式是把一個複雜物件的構造過程與表示分離,使使用者利用同樣的呼叫過程可以創建出符合條件的不同物件,在實際使用過程中我們一般採用鏈式呼叫的方式使程式碼簡潔可讀性很強。

2.1 UML類圖

從UML圖可以看出Builder模式中有以下幾種模式:

Builder角色:為建立一個Product物件的各個屬性指定抽象介面(包括最終生成物件的介面定義)。

實際Builder(ConcreteBuilder):實現Builder的介面以構造和裝配該產品的各個部件;在實際程式碼中通常會在其中定義一個內部類(ProductParams)來表示Product產品的各個部件(屬性part1,part2),然後在最後的build方法中利用這個內部類構造出Product物件。

Director 構造一個使用Builder介面的物件,有時候在實際開發中可能會省略或者由具有其它功能的類來代替。

Product 表示被構造的複雜物件。ConcreteBuilder建立該產品的內部表示並定義它的建立過程。

2.2 程式碼示例

Builder類:

public interface Builder {
	public Builder makePart1(String part1);
	public Builder makePart2(String part2);
	public Product build();
}

ConcreteBuilder:

public class ConcreteBuilder implements Builder {
	private ProductParams params;
	
	public ConcreteBuilder() {
		this.params = new ProductParams();
	}
	
	@Override
	public Builder makePart1(String part1) {
		this.params.part1 = part1;
		return this;
	}

	@Override
	public Builder makePart2(String part2) {
		this.params.part2 = part2;
		return this;
	}

	@Override
	public Product build() {
		Product product = new Product();
		product.setPart1(params.part1);
		product.setPart2(params.part2);
		return product;
	}
	
	class ProductParams {
		public  String part1;
        public String part2;
	}
}

Product類:

public class Product {
	private String part1;
	private String part2;
	
	public Product() {
	}
	
	public Product(String part1, String part2) {
		this.part1 = part1;
		this.part2 = part2;
	}

	public String getPart1() {
		return part1;
	}

	public void setPart1(String part1) {
		this.part1 = part1;
	}

	public String getPart2() {
		return part2;
	}

	public void setPart2(String part2) {
		this.part2 = part2;
	}

	@Override
	public String toString() {
		return "Product [part1=" + part1 + ", part2=" + part2 + "]";
	}
} 

Director類:

public class Director {
    public Product generateProduct(ConcreteBuilder builder) {  
    	builder.makePart1("part1"); 
    	builder.makePart2("part2"); 
        return builder.build();
    }  
}

Client類:

public class Client {
	public static void main(String[] args) {
		Product product = new Director().generateProduct(new ConcreteBuilder());
		System.out.println(product);
	}
}

2.3 在原始碼中的應用體現:

在Android原始碼中最常見的Builder的運用就是AlertDialog.Builer,Notication。有興趣的朋友可以去Android原始碼中檢視,有了上面的結構相信很容易看出Builder的使用。

3. 單例模式

關於單例模式就運用得很普遍了,定義-一個類的例項在系統中有且只有一個例項;一般通過私有構造方法對外提供獲取唯一例項的介面的方式實現。相信大多數朋友都見過或者寫過,這裡我們著重說一下單例的幾種見的寫法。

3.1 餓漢式

將類的例項宣告為static並且直接賦物件,也就是在類載入的時候就已經初始化了單例的例項。由其實現方式可以看出物件是在類載入的時候就已經初始化故而可能會影響類載入的速度;但是程式在獲取的時候相對就要快一些(物件已經存在直接返回);並且由於在類載入的時候就已經初始化所以這種方式是執行緒安全的。其寫法如下:

public class Singleton {
	private static Singleton mInstanceSingleton = new Singleton();
	
	private Singleton() {
	}
	
	public static Singleton getInstance() {
		return mInstanceSingleton;
	}
}

3.2 懶漢式(執行緒不安全)

這種方式與餓漢式宣告方式類似,只是物件的初始化不是在類載入的時候進行而是在系統第一次呼叫介面獲取單例物件的時候初始化,這樣原則上節約了記憶體(若不需要則不會去初始化)和此類的載入速度;這種方式由於在第一次呼叫的時候會初始化物件會稍微慢點(基本上忽略不計),但有一點此方式由於物件的生成在對外介面中可能會造成物件不一至,所以此種寫法線上程安全的角度來說是不安全的,我們在運用的時候要特別注意。其寫法如下:
public class Singleton2 {
	private static Singleton2 mInstanceSingleton = null;
	
	private Singleton2() {
	}
	
	public static Singleton2 getInstance() {
		if(mInstanceSingleton == null) {
			mInstanceSingleton = new Singleton2();
		}
		return mInstanceSingleton;
	}
}

3.3 懶漢式(執行緒安全)

故明思義,這種方式的出現是為了解決懶漢式執行緒不安全的的問題出現的,主要是通過synchronized關鍵字來實現執行緒同步的,故而效能上會差一點,但相比解決執行緒安全的問題是可以忽略不計的。其寫法如下:

	public static synchronized Singleton3 getInstance() {
		if(mInstanceSingleton == null) {
			mInstanceSingleton = new Singleton3();
		}
		return mInstanceSingleton;
	}

或者

	public static Singleton3 getInstance2() {
		synchronized (Singleton3.class) {
		if(mInstanceSingleton == null) {
				mInstanceSingleton = new Singleton3();
			}
		}
		return mInstanceSingleton;
	}

3.4 雙重檢查式(Double-checked locking)

這種方式是對懶漢式(執行緒安全)的進化個人覺得,進行兩次Null判斷,第一次避免了不必要的同步操作,第二次是為Null的時候才建立例項物件。其避免了多餘的執行緒同步操作當然這種方式也是執行緒安全的,其寫法如下:

public class Singleton4 {
	private static Singleton4 mInstanceSingleton = null;
	
	private Singleton4() {
	}
	
	public static synchronized Singleton4 getInstance() {
		if(mInstanceSingleton == null) {
			mInstanceSingleton = new Singleton4();
		}
		return mInstanceSingleton;
	}
	
	public static Singleton4 getInstance2() {
		if(mInstanceSingleton == null) {
			synchronized (Singleton4.class) {
				if(mInstanceSingleton == null){
					mInstanceSingleton = new Singleton4();
				}
			}
		}
		return mInstanceSingleton;
	}
}

3.5 靜態內部類

我們都知道載入一個類時其內部類不會同時被載入,當且僅當某個靜態成員(靜態成員變數,構造方法,靜態方法)被呼叫時才去載入。這種方式我們以一個靜態內部類的方式儲存單例物件,如前所說當類載入時這個靜態內部類不會被載入,當系統呼叫getInstance方法時才會去載入這個內部類進而初始化單例物件,這樣既能保證物件的唯一性而且也是執行緒安全的,其寫法如下:
public class Singleton5 {
	private Singleton5() {
	}
	public static Singleton5 getInstance() {
        return SingletonHolder.mInstance;
    }
	private static class SingletonHolder {
        private static final Singleton5 mInstance = new Singleton5();
    }
}

3.6 容器式

幫明思義,這種方式是通過容器(Map,List等)來管理單例物件,使用時根據key獲取對應的例項,這種方式可以管理多種型別的單例,並且在使用時可以通過統一的介面進行獲取操作,降低了使用者的使用成本,遮蔽了內部的實現細節,降低了耦合度,感覺上跟執行緒池差不多,其寫法如下:

public class Singleton6 {
	public static Map<String, Object> maps = new HashMap<String, Object>();
    public static void registerService(String key,Object instance){
        if(!maps.containsKey(key)){
        	maps.put(key, instance);
        }
    }
    public static Object getService(String key){
        return maps.get(key);
    }
}
除此之外還有列舉寫法等在實際開發過程中用得比較少的方式這就不一一羅列了,以上說了比較常見的幾種單例模式的寫法當然各有各的好處和缺點,我們在使用的時候需要根據實際情況來衡量具體使用哪種方式,就我自己的經歷來看用得比較多的為懶漢式執行緒安逸,其次是靜態內部類,然後是餓漢式。

4. 原型模式

原型模式就是利用拷貝原型物件建立新的例項,在建立過程中不會修改原型物件的屬性,即在記憶體中重新建立了一個新的物件並用新的引用指向這個物件。它也是為了解決複雜物件的建立而出現的。

4.1 UML類圖


從UML類圖中可以看出其實原型模式相對比較簡單,存在以下幾種角色:

抽象原型(Prototype)角色:這是一個抽象角色,通常由一個介面或抽象類實現。此角色給出所有的具體原型類所需的介面,在。面向物件程式設計中通常以介面或者抽象類的形式存在。

抽象產品(IProduct) 角色:面向物件的寫法,抽象產品和屬性和動作。

具體原型產品(PrototypeProduct)角色:被複制的物件。此角色需要實現抽象的原型角色所要求的介面。

原型管理器(Prototype Manager)角色:建立具體原型類的物件,並記錄或者使用每一個被建立的物件。

客戶(Client)角色:客戶端類向原型管理器提出建立物件的請求。

4.2 程式碼示例

Prototype:

public abstract class Prototype {
	public abstract Prototype clone();
}

IProduct:

public abstract class IProduct extends Prototype {
	public int number;
	public String name;
	
	public int getNumber() {
		return number;
	}
	
	public void setNumber(int number) {
		this.number = number;
	}
	
	public String getName() {
		return name;
	}
	
	public void setName(String name) {
		this.name = name;
	}
}

ProtypeProduct1:

public class ProtypeProduct1 extends IProduct{
	@Override
	public Prototype clone() {
		ProtypeProduct1 protypeProduct1 = new ProtypeProduct1();
		protypeProduct1.setNumber(this.getNumber());
		return protypeProduct1;
	}
}

ProtypeProduct2

public class ProtypeProduct2 extends IProduct{
	@Override
	public Prototype clone() {
		ProtypeProduct2 protypeProduct2 = new ProtypeProduct2();
		protypeProduct2.setNumber(this.getNumber());
		return protypeProduct2;
	}
}

PrototypeManager:

public class PrototypeManager {
	public void managerProtoType(IProduct product) {
		int number = product.getNumber();
		while(number > 0) {
			System.out.println("oldProduct:" + product.hashCode());
			IProduct iProduct= (IProduct) product.clone();
			iProduct.setNumber(number >= 1000 ? 1000 : number);
            System.out.println("newProduct" + iProduct.hashCode() + " number:" + iProduct.getNumber());
            number -= 1000;
		}
	}
}

Client:

public class Client {
	public static void main(String[] args) {
		IProduct product = new ProtypeProduct1();
		product.setNumber(4500);
		product.setName("隨機");
		PrototypeManager mPrototypeManager = new PrototypeManager();
		mPrototypeManager.managerProtoType(product);
	}
}

5. 最後

內容相對簡單,只當自己給自己做個筆記。