劍指架構師系列-設計模式

 

一、單例模式:

確保一個類只有一個實例,並且自行實例化並向整個系統提供這個實例。java

單例模式有如下幾個要素:算法

  • 私有的構造方法
  • 指向本身實例的私有靜態引用
  • 以本身實例爲返回值的靜態的公有的方法

        單例模式根據實例化對象時機的不一樣分爲兩種:一種是餓漢式單例,一種是懶漢式單例。餓漢式單例在單例類被加載時候,就實例化一個對象交給本身的引用;而懶漢式在調用取得實例方法的時候纔會實例化對象。spring

餓漢式:數據庫

public class Singleton_Simple {  
      
    private static final Singleton_Simple simple = new Singleton_Simple();  
      
    private Singleton_Simple(){}  
      
    public static Singleton_Simple getInstance(){  
        return simple;  
    }  
  
}

懶漢式:編程

//雙鎖機制
class Singleton {
	private volatile static Singleton singleton;
	public static Singleton getInstance() {
		if (singleton == null) {
			synchronized (Singleton.class) { // 因爲每次調用都須要進行同步,因此能夠在前面進行判斷便可提升效率,同時注意使用的是Singleton.class的類鎖
				if (singleton == null) {
					singleton = new Singleton();
				}
			}
		}
		return singleton;
	}
}

爲了提升效率。咱們能夠用double check機制,如今來看兩個問題:設計模式

(1)爲什麼用double check的檢測機制?緩存

因爲兩個線程均可以經過第一重的判斷 ,進入後,因爲存在鎖機制,因此會有一個線程進入同步塊和第二重判斷,而另外的一個線程在同步塊處阻塞。安全

當第一個線程退出同步代碼塊後,第二個進入,沒有通過第二重判斷,保證了單例。因此這裏必需要使用雙重檢查鎖定。多線程

其實沒有第一重的判斷,咱們也能夠實現單例,只是爲了考慮性能問題。併發

(2)爲什麼要對實例變量加volatile關鍵字?

java內存模型(jmm)並不限制處理器重排序,在執行instance=new Singleton()時,並非原子語句,實際是包括了下面三大步驟: 

1.爲對象分配內存

2.初始化實例對象

3.把引用instance指向分配的內存空間

ps:對象建立可參看《深刻理解Java虛擬機》第44頁

這個三個步驟並不能保證按序執行,處理器會進行指令重排序優化,存在這樣的狀況:

優化重排後執行順序爲:1,3,2, 這樣在線程1執行到3時,instance已經不爲null了,線程2此時判斷instance!=null,則直接返回instance引用,但如今實例對象尚未初始化完畢,此時線程2使用instance可能會形成程序崩潰。

如今要解決的問題就是怎樣限制處理器進行指令優化重排。

volatile的做用:

(1)volatile變量不會以被緩存到寄存器或者對其它處理器不可見的地方,所以在讀取volatile類型的變量時老是返回最新寫入的值。 

(2)volatile關鍵字可以經過提供內存屏障,來保證某些指令順序處理器不可以優化重排,編譯器在生成字節碼時,會在指令序列中插入內存屏障來禁止特定類型的處理器重排序。

 

JVM會對分配內存空間的動做進行同步處理。

(1)可能實際上採用CAS(Compare And Set)配上失敗重試的方式保證更新操做的原子性

(2)把內存分配的動做按照線程劃分在不一樣的空間之中進行。就是每一個線程在Java堆中預先分配一小塊內存,

參考Java虛擬機第45頁

 

其實還有種寫法,以下:

public class Singleton { 
    
	  private static class SingletonClassInstance { // 私有靜態內部類
	    // 多是final解決了併發的問題,基本類型聲明就可,可是對象類型時,這個對象不能有狀態,
	    // 若是有狀態,則必定要聲明爲final,例如String類就是一個不可變類
	    private static final Singleton instance = new Singleton(); 
	  } 

	  public static Singleton getInstance() { 
	    return SingletonClassInstance.instance; // 只在這裏調用了靜態內部類,私有屬性讓他人沒法使用這個類型
	  } 

	  private Singleton() {  } 
}

因爲Singletom沒有static屬性且SingletonClassInstance是私有靜態內部類,不會被其餘類調用,因此不會被提早初始化,實現了懶漢式的構造實例。

static語義也要求不會有多個實例存在。而且,JSL規範定義,類的構造必須是原子性的,非併發的,所以不須要加同步塊。一樣,因爲這個構造是併發的,因此getInstance()也並不須要加同步。
  
虛擬機會保證一個類的<clinit>()方法在多線程環境中被 正確地加鎖、同步。若是多個線程同時去初始化一個類,那麼只會有一個線程去執行這個類的<clinit>()方法,其餘線程都須要阻塞等待,直到活動線程執行<clinit>()方法完畢。

 

重點要知道爲何靜態內部類能保證單例:由於只有一個線程去初始化這個類,在初始化後static final就是一個常量了,固然線程安全了。 

 

其在實際中有重要的應用,如:

一、單例模式在Log4j中的應用。將日誌輸出到一個文件中必須使用單例模式,不然會覆蓋文件的原有內容
二、數據庫鏈接池中的應用

 

二、迭代器模式:

提供一種方法訪問一個容器對象中各個元素,而又不暴露該對象的內部細節

interface Iterable{
	public Iterator iterator();
}

interface Aggregate extends Iterable{
	public void add(Object obj);
	public void remove(Object obj);
	public Iterator iterator();
}

class ConcreteAggregate implements Aggregate {
	private List list = new ArrayList();

	public void add(Object obj) {
		list.add(obj);
	}

	public Iterator iterator() {
		return new ConcreteIterator(list);
	}

	public void remove(Object obj) {
		list.remove(obj);
	}
}

調用iterator()方法後返回一個Iterator迭代器,實現以下:

interface Iterator {
	public Object next();
	public boolean hasNext();
}

class ConcreteIterator implements Iterator {
	private List list = new ArrayList();
	private int cursor = 0;

	public ConcreteIterator(List list) {
		this.list = list;
	}

	public boolean hasNext() {
		if (cursor == list.size()) {
			return false;
		}
		return true;
	}

	public Object next() {
		Object obj = null;
		if (this.hasNext()) {
			obj = this.list.get(cursor++);
		}
		return obj;
	}
}

測試一下:

Aggregate ag = new ConcreteAggregate();
ag.add("小明");
ag.add("小紅");
ag.add("小剛");
Iterator it = ag.iterator();
while (it.hasNext()) {
	String str = (String) it.next();
	System.out.println(str);
}

 

三、組合設計模式:

蜜蜂是昆蟲:繼承實現

蜜蜂有向前移動後攻擊人的行爲:組合實現

昆蟲:

class Insect {
    private String name;
    public Insect(String name) {
        this.name = name;
    }
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

攻擊行爲:

interface Attack {
    public void move();
    public void attack();
}

class AttackImpl implements Attack {
    private String move;
    private String attack;
 
    public Attack Impl(String move, String attack) {
        this.move = move;
        this.attack = attack;
    }
    @Override
    public void move() {
        System.out.println(move);
    }
    @Override
    public void attack() {
        move();
        System.out.println(attack);
    }
}

蜜蜂是昆蟲,有向前一步攻擊人的屬性:

class Bee extends Insect implements Attack {
    private Attack attack;
 
    public Bee(String name, Attack attack) {
        super(name);
        this.attack = attack;
    }
 
    public void move() {
        attack.move();
    }
 
    public void attack() {
        attack.attack();
    }
}

因爲繼承機制太過依賴於父類的實現細節,若是父類變更,則子類也會跟着變更,這是糟糕的。 

 

四、策略設計模式:

// Different types of function objects:
interface Combiner<T> {
	T combine(T x, T y);
}


public class Functional {
	// Calls the Combiner object on each element to combine
	// it with a running result, which is finally returned:
	public static <T> T reduce(Iterable<T> seq, Combiner<T> combiner) {
		Iterator<T> it = seq.iterator();
		if (it.hasNext()) {
			T result = it.next();
			while (it.hasNext()){
				result = combiner.combine(result, it.next());
			}
			return result;
		}
		// If seq is the empty list:
		return null; // Or throw exception
	}


	// To use the above generic methods, we need to create
	// function objects to adapt to our particular needs:
	static class IntegerAdder implements Combiner<Integer> {
		public Integer combine(Integer x, Integer y) {
			return x + y;
		}
	}

	static class IntegerSubtracter implements Combiner<Integer> {
		public Integer combine(Integer x, Integer y) {
			return x - y;
		}
	}

	static class BigIntegerAdder implements Combiner<BigInteger> {
		public BigInteger combine(BigInteger x, BigInteger y) {
			return x.add(y);
		}
	}


	public static void main(String[] args) {
		
		// Generics, varargs & boxing working together:
		List<Integer> li = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
		
		Integer result = reduce(li, new IntegerAdder());
		print(result); // 28

		result = reduce(li, new IntegerSubtracter());
		print(result);


		// Use the prime-generation facility of BigInteger:
		List<BigInteger> lbi = new ArrayList<BigInteger>();
		BigInteger bi = BigInteger.valueOf(11);
		for (int i = 0; i < 11; i++) {
			lbi.add(bi);
			bi = bi.nextProbablePrime();
		}
		print(lbi);

		BigInteger rbi = reduce(lbi, new BigIntegerAdder());
		print(rbi);
		
	}
}

如上例子參考了Java編程思想關於泛型實現策略模式的例子。  

 

下面來總結一下各個設計模式在Java中的具體應用。

結構型模式

 

單例模式(Single Pattern)

一、Log4j中將日誌輸出到一個文件中必須使用單例模式,不然會覆蓋文件的原有內容

二、數據庫鏈接池中的應用

工廠模式(Factory)

Spring工廠模式:經過XML文件或Java註解來表示Bean之間的依賴關係,不多直接new一個類進行代碼編寫

建造者模式(Builder)

Struts2中建立容器(Container)對象

原型模式(Protype)

Java的克隆clone()

 結構型模式

 

適配器模式(Adapter)

一、在Java的I/O類庫中,StringReader將一個String類適配到Reader接口,InputStreamReader將InputStream適配到Reader類。

二、在Spring中的AOP中,因爲Advisor須要的是MethodInterceptor對象,因此每個Advisor中的Advice都要配成對應的MethodInterceptor對象。

橋接模式(Bridge)

 

裝飾模式(Decorator)

一、Collections.synchronizedList例子也是一個裝飾器模式

二、裝飾器在Junit中的應用。TestDecorator是Test的裝飾類,其TestDecorator有一些擴展功能的子類。如RepeatedTest類,TestSetup類等

三、通常狀況下,須要使用FileReader和StringReader,若是要增長緩存功能的類有不少,那麼子類也就須要不少,因此Java就使用了裝飾模式,BufferedReader就是這樣的裝飾類。其實Java I/O 庫中的全部輸入流、輸出流的類都採用了裝飾器模式,它們能夠無限次地進行裝飾轉換,轉換的目的就是獲得本身想要的數據類型的流對象

組合模式(Composite)

 

外觀模式(Façade)

一、在Spring中已經提供了很好的封裝,在org.springframework.jdbc.support包下提供的JdbcUtils類就是這樣的工具類

二、在Hibernate的配置工做中,有一個Configuration類,負責管理運行時須要的一些信息

享元模式(Flyweight)

數據據鏈接池是享元模式的重要應用。在Java中,對於基本類型和字符串類型的實現就採用了享元模式

代理模式(Proxy)

一、在Spring中已經提供了很好的封裝,在org.springframework.jdbc.support包下提供的JdbcUtils類就是這樣的工具類

二、在Hibernate的配置工做中,有一個Configuration類,負責管理運行時須要的一些信息

行爲型模式

 

模版方法模式(Template Method)

一、在Junit中的TestCase類中

二、在MVC框架的HttpServlet中

三、Spring採用開放式的處理方式,在使用JpaTemplate時,只須要處理具體的SQL語句便可,而建立鏈接、關閉鏈接等方法都不須要編寫代碼,由於無論理是哪一種持久層的操做應運,其建立和關閉鏈接都是一致的,按照相同的順序執行,這就是模板方法模式的應用

命令模式(Command )

 

命令模式在Struts2中的應用。其中action就是命令模式中命令接口,ActionInvocation就是命令模式中命令的調用者

迭代器模式(Iterator )

java中的Collection,List、Set、Map等,這些集合都有本身的迭代器

觀察者模式(Oberver Pattern)

 

中介者模式(Mediator)

 

備忘錄模式(Memento)

 

解釋器模式(Interpreter)

 

狀態模式(State)

 

職責鏈模式(Chain of Responsibility)

責任鏈在Struts2中的攔截器上有重要應用。



策略模式(Strategy)

一、策略模式容許在程序執行時選擇不一樣的算法.好比在排序時,傳入不一樣的比較器(Comparator),就採用不一樣的算法

二、Spring的Resource實現思想是典型的策略模式的應用

訪問者模式(Visitor)

相關文章
相關標籤/搜索