java之 ------ 幾種常見的簡單設計模式

前言:

設計模式(Design pattern)是一套被反覆使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。用於解決特定環境下、重複出現的特定問題的解決方案。使用設計模式是爲了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。 毫無疑問,設計模式於己於他人於系統都是多贏的,設計模式使代碼編制真正工程化,設計模式是軟件工程的基石,如同大廈的一塊塊磚石一樣。項目中合理的運用設計模式可以完美的解決很多問題,每種模式在現在中都有相應的原理來與之對應,每一個模式描述了一個在我們周圍不斷重複發生的問題,以及該問題的核心解決方案,這也是它能被廣泛應用的原因。(引用---點擊打開鏈接


一、單例模式(SingletonPattern

a、單例模式:是一種常見的設計模式。爲了保證一個類在內存中只能有一個對象。

思路:1、如果其他程序能夠隨意用new創建該類對象,那麼就無法控制個數。因此,不讓其他程序用new創建該類的對象。
    2、既然不讓其他程序new該類對象,那麼該類在自己內部就要創建一個對象,否則該類就永遠無法創建對象了。
    3、該類將創建的對象對外(整個系統)提供,讓其他程序獲取並使用。

步驟:1、將該類中的構造函數私有化。
    2、在本類中創建一個本類對象。
    3、定義一個方法,返回值類型是本類類型。讓其他程序通過該方法就可以獲取到該類對象。

單例模式中又有:餓漢式和懶漢式兩種

1、餓漢式:不管你需不需要,都在類內部直接先生成一個對象。因爲構造方法爲私有的,所以外面無法new新的對象,只能通過getInstance來獲得這個唯一的對象,從而實現單例模式

/*餓漢式*/
public class Single {
	private static final Single s=new Single();
	private Single(){
	}
	public static Single getInstance(){
		return s;
	}
}
2、懶漢式:與餓漢式不同之處在於,不是直接生成一個對象,而是在需要對象是纔會生成,也就是隻有當調用getInstance這個方法是纔會產生這個唯一對象。(也可以說是單例的延遲加載方式)

/*懶漢式*/
public class Single2 {
	private static Single2 s;
	private Single2(){
	}
	public static Single2 getInstance(){
		if(s==null){
			s = new Single2();
		}
		return s;
	}
}
這個類可以滿足基本要求,但是,像這樣毫無線程安全保護的類,如果我們把它放入多線程的環境下,肯定就會出現問題了(問題就是假如有多個線程同時到達if(s==null)時,這個條件都成立,那麼就會產生多個對象了,就違背了我們的本意),如何解決?我們首先會想到對getInstance方法加synchronized關鍵字。所以該爲下面這句即可。

public static synchronized Single2 getInstance();
但是,synchronized關鍵字鎖住的是這個對象,這樣的用法,在性能上會有所下降,因爲每次調用getInstance(),都要對對象上鎖,事實上,只有在第一次創建對象的時候需要加鎖,之後就不需要了,所以,這個地方需要改進:
public static synchronized Single2 getInstance(){
		if(s==null){
			synchronized (s) {  
                <span style="white-space:pre">		</span>if (s == null) {  
                   <span style="white-space:pre">			</span> s = new Single2();  
                <span style="white-space:pre">		</span>}  
            <span style="white-space:pre">		</span>}  
		}
		return s;
}
看上去寫的很好,但在自己運行時,就會發現還是有錯誤,雖然知道是線程的原因,卻沒有深究。大概知道那麼回事。看看大神的準確解答:在Java指令中創建對象和賦值操作是分開進行的,也就是說instance = new Singleton();語句是分兩步執行的。但是JVM並不保證這兩個操作的先後順序,也就是說有可能JVM會爲新的Singleton實例分配空間,然後直接賦值給instance成員,然後再去初始化這個Singleton實例。這樣就可能出錯了。

b、單例變形-----多例    (“單例+緩存”技術)

★ 緩存在單例中的使用

緩存在編程中使用很頻繁,有着非常重要的作用,它能夠幫助程序實現以空間換取時間,通常被設計成整個應用程序所共享的一個空間,現要求實現一個用緩存存放單例對象的類。
說明:該緩存中可以存放多個該類對象,每個對象以一個key值標識,key值相同時所訪問的是同一個單例對象。

import java.util.HashMap;
import java.util.Map;
public class A {
	//定義一個緩存(集合),用來存放數據的容器
	private static Map<String,A> map = new HashMap<String,A>();
	public static A getInstance(String key){
		A a = map.get(key);
		//判斷a是否存在,不存在則是null
		if(a==null){
			a = new A();//新建一個對象
			map.put(key, a);//把新對象放入緩存
		}
		return a;
	}
	
}
★ 單例變形——多例模式(“單例+緩存+控制實例個數”技術)

把上面緩存的單例實現,做成一個能夠控制對象個數的共享空間,供整個應用程序使用。在緩存中維護指定個數的對象,每個對象的key值由該類內部指定,有外部請求時直接返回其中一個對象出去。
說明:相當於維護一個指定數量的對象池,當請求個數超過控制的總數時,開始循環重複使用 。

import java.util.HashMap;
import java.util.Map;
public class Multiple {
	private static Map<Integer,Multiple> map = new HashMap<Integer,Multiple>();
	private static int num=1;
	private static int count=3;//控制實例的個數:3
	public static Multiple getInstance(){
		Multiple m = map.get(num);
		if(m==null){
			m = new Multiple();
			map.put(num, m);
		}
		num++;
		//如果num超過所控制的個數,則重新設爲1,以進行循環重複使用緩存中的對象
		if(num>count){
			num=1;
		}
		return m;
	}
}

二、工廠模式(Factory Pattern

工廠模式:Java程序開發講究面向接口編程,通過建立一個工廠類,隱藏具體的實現類。

首先創建公共接口:

public interface Api {
	public abstract String t1();
}
然後創建實現類:

public class Impl implements Api {
	@Override
	public String t1() {
		return "11111111111111";
	}
}
public class Impl2 implements Api {
	@Override
	public String t1() {
		return "222222";
	}
}
最後創建工廠類:

public class DepFactory {
	public static Api createApi(){
		return new Impl2();//通過配置文件+類反射,讓我們的程序依賴字符串
	}
}
測試:

public class Client {
	public static void main(String[] args) {
		Api obj = DepFactory.createApi();//new Impl();
		String str = obj.t1();
		System.out.println(str);
	}

}
結果:222222

三、值對象模式(Value Object Pattern

值對象模式:簡單的說就是封裝數據,方便各個模塊之間的數據的交流。

★ 基本的編寫步驟:

1、寫一個類,實現可序列化(如果以後數據是往數據庫裏存的,那麼可以不序列化,節省資源)

2、私有化所有屬性,保持一個默認構造方法(public無參)

3、爲每個屬性提供get()、set()方法(如果是boolean型變量,最好把get改成is)

4、推薦覆蓋實現equals()、hashCode()和toString()方法

public class UserModel implements Serializable {
	private String id,name,address;
	private boolean man;
	public UserModel(String name){
		this.name = name;
	}
	public UserModel(){
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	public boolean isMan() {
		return man;
	}
	public void setMan(boolean man) {
		this.man = man;
	}
	
	//hashCode和equals一般只用“主鍵”來生成
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		UserModel other = (UserModel) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}
	@Override
	public String toString() {
		return "UserModel [id=" + id + ", name=" + name + ", address="
				+ address + ", man=" + man + "]";
	}
	
}

四、裝飾模式(Decorator Pattern

裝飾模式:在不必改變原類文件和使用繼承的情況下,動態地擴展一個對象的功能。它是通過創建一個包裝對象,也就是裝飾來包裹真實的對象。

例:寫一個MyBufferedReader類,使它能夠對字符流(如FileReader、InputStreamReader和PipedReader等)進行功能增強:

(1) 提供帶緩衝的myRead()方法,對原有的read()方法進行增速;

(2)提供一個能夠每次讀取一行字符的myReadLine()方法。

思路:一般要實現一個類的功能就想到繼承這個類。但是這裏要實現多個類的功能,而繼承只能繼承一個。那就不得不寫多個類了,這樣就會顯得臃腫。

另一種方法就是封裝,不用繼承,但是缺點和上面一樣。

所以想到把二者結合起來,從而達到要求。也就是裝飾模式。

import java.io.IOException;
import java.io.Reader;

public class MyBufferedReader extends Reader{ //※※※讓加強類融入到體系中
	private Reader r;//封裝
	private char[] buf = new char[1024];
	private int count=0;//記錄當前緩衝區中字符的個數
	private int pos=0;//數組元素的下標,當前所讀的位置
	
	public MyBufferedReader(Reader r){
		this.r = r;
	}
	
	public int myRead() throws IOException{
		if(count==0){
			count = r.read(buf);
			pos=0;
		}
		if(count==-1){
			return -1;
		}
		char ch = buf[pos];
		pos++;
		count--;
		return ch;
	}
	
	public String myReadLine() throws IOException{
		StringBuilder sb = new StringBuilder();
		int ch=0;
		while((ch=myRead())!=-1){
			if(ch=='\r'){
				continue;
			}
			if(ch=='\n'){
				return sb.toString();
			}
			
			sb.append((char)ch);
		}
		if(sb.length()!=0)
		   return sb.toString();
		return null;
	}
	
	
	public void close() throws IOException{
		r.close();
	}

	@Override
	public int read(char[] cbuf, int off, int len) throws IOException {
		
		return 0;
	}
}








發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章