1. 程式人生 > >設計模式十五之亨元模式(java)

設計模式十五之亨元模式(java)

這是我看Head first設計模式書籍之後想要總結的知識點,一方面是對自己學習的東西總結和提煉加強自己的理解和記憶,另一方面是給大家簡化這本書,方便大家快速瞭解各種設計模式。

我想提醒大家的是,設計模式只是前人總結的一些經驗套路,實際上還是要在開發專案中慢慢體會,不可成為設計模式的中毒患者,強行照搬設計模式的一些規則。

(1) 亨元模式     亨元模式(Flyweight Pattern)主要用於減少建立物件的數量,以減少記憶體佔用和提高效能。這種型別的設計 屬於結構型模式,它提供了減少物件數量從而改善應用所需的物件結構的方式。     在亨元模式中可以共享的相同內容成為內部狀態(Intrinsic State),而那些需要外部環境來設定的不能共享的 內容成為外部狀態(Extrinsic State),其中外部狀態和內部狀態是相互獨立的,外部狀態的變化不會引起內部 狀態的變化。由於區分了內部狀態和外部狀態,因此可以通過設定不同的外部狀態使得相同的物件可以具有一些 不同的特徵,而相同的內部狀態是可以共享的。也就是說亨元模式就是把一個物件的狀態分成內部狀態和外部狀態, 內部狀態即是不變的,外部狀態是變化的;然後通過共享不變的部分,達到減少物件數量並節約記憶體的目的。     在亨元模式中通常會出現工廠模式,需要建立一個亨元工廠來負責維護一個亨元池(Flyweight Pool)(用於 儲存具有相同內部狀態的亨元物件)。在亨元模式中,共享的亨元物件的內部狀態,外部狀態需要通過環境來設定。在 實際使用中,能夠共享的內部狀態是有限的,因此亨元物件一般都設計為較小的物件,它所包含的內部狀態較少,這種物件 也成為細粒度物件。亨元模式的目的就是使用共享技術來實現大量細粒度物件的複用。     由於亨元模式要求能夠共享的物件必須是細粒度物件,因此它又稱為輕量級模式,它是一種物件結構型模式。

(2) 模式所涉及的角色 Flyweight: 亨元介面,通過這個介面傳入外部狀態並作用於外部狀態 ConcreteFlyweight: 具體的亨元實現物件,必須是可共享的,需要封裝亨元物件的內部狀態 UnsharedConcreteFlyWeight: 非共享的亨元實現物件,並不是所有的亨元物件都可以共享,非     共享的亨元物件通常是亨元物件的組合物件。 FlyweightFactory: 亨元工廠,主要用來建立並管理共享的亨元物件,並對外提供訪問共享亨元的介面。

(3) 亨元模式嘗試重用現有的同類物件,如果未找到匹配的物件,則建立新物件。我們將通過建立5個物件來畫出20 個分佈不同位置的圓來演示這種模式。由於只有5種可用的顏色,所以color屬性被用來檢查現有的Circle物件意圖: 

    運用共享技術有效地支援大量細粒度的物件主要解決:      在有大量物件時,有可能會造成記憶體溢位,我們把其中共同的部分抽象出來,如果有相同的業務請求,直接 返回在記憶體中已有的物件,避免重新建立。何時使用:      1.系統中有大量物件。 2.這些物件消耗大量記憶體 3.這些物件的狀態大部分可以外部化 4.這些物件可以按照 內蘊狀態(內部狀態,是儲存在亨元物件內部的,並且使不會隨環境的改變而有所不同)分為很多組,當把外蘊物件從物件 中剔除出來,每一組物件都可以用一個物件來替代。 5.系統不依賴於這些物件身份,這些物件是不可分辨的 如何解決: 用唯一標識碼判斷,如果在記憶體中有,則返回這個唯一標識碼所標識的物件。 關鍵程式碼: 用HashMap儲存這些物件應用例項:
    1.Java中的String,如果有則返回,如果沒有則建立一個字串儲存在字串快取池裡面(Java字串常量池, python有小整數常量池和字串常量池),下個月可能會看本Java虛擬機器的書,會總結這方面的東西     2.資料庫的資料池。 池這個東西緩衝池、常量池、資料庫連線池、執行緒池、程序池、會話池,到處可見為了效能減少物件建立和銷燬的優化方法。

優點: 大大減少物件的建立,降低系統的記憶體,使效率提高。缺點: 提高了系統的複雜度,需要分離出外部狀態和內部狀態,而且外部狀態具有固有化的性質,不應該隨著內部狀態的變化而變化,否則會造成系統的混亂。使用場景: 1.系統有大量相似物件 2.需要緩衝池的場景注意事項:

         1.注意劃分外部狀態和內部狀態,否則可能會引起執行緒安全問題          2.這些類必須有一個工廠物件加以控制。

實現     我們將建立一個Shape介面和實現了Shape介面的實體類Circle.下一步是定義工廠類ShapeFactory ShapeFactory有一個Circle的HashMap,其中鍵名為Circle物件的顏色。無論何時接受到請求,都會建立一個特定 顏色的圓。ShapeFactory檢查它的HashMap中的circle物件,如果找到Circle物件,則返回該物件,否則將建立一個儲存 在hashmap中以備後續使用的新物件,並把該的物件返回到客戶端。 FlyWeightPatternDemo,我們的演示類使用ShapeFactory來獲取Shape物件。它將向ShapeFactory傳遞資訊(red/green/blue/black/ white),以便獲取它所需物件的顏色。

建立一個介面 Shape.java

package com.gougoucompany.designpattern.flyweight;

//建立一個介面
public interface Shape {
	void draw();
}

建立實現介面的實體類Circle.java

package com.gougoucompany.designpattern.flyweight;

public class Circle implements Shape {
	private String color;
	private int x;
	private int y;
	private int radius;
	
	public Circle(String color) {
		this.color = color;
	}
	
	public void setX(int x) {
		this.x = x;
	}
	
	public void setY(int y) {
		this.y = y;
	}
	
	public void setRadius(int radius) {
		this.radius = radius;
	}

	@Override
	public void draw() {
		// TODO Auto-generated method stub
		System.out.println("Cicle: Draw() [Color : " + color
				+ ", x: " + x + ", y: " + y + ", radius: " + radius);
	}

}

建立一個工廠,生成基於給定資訊的實體類物件ShapeFactory.java

package com.gougoucompany.designpattern.flyweight;

import java.util.HashMap;

public class ShapeFactory {
	//HashTable是執行緒安全的,其實現方式是在對應的方法上加上synchronized關鍵字,效率不高
	private static final HashMap<String, Shape> circleMap = 
			new HashMap<>();
	
	public static Shape getCircle(String color) {
		/*
		 Returns the value to which the specified key 
		 is mapped, or null if this map contains no mapping
		 for the key.
		 */
		Circle circle = (Circle)circleMap.get(color);
		
		//如果沒有快取在,則重新建立,這個和安卓開發的RecyclerView快取類似
		if(circle == null) {
			circle = new Circle(color);
			circleMap.put(color, circle);
			System.out.println("Creating circle of color : " + color);
		}
		
		return circle;
	}
}

使用該工廠,通過傳遞顏色資訊來獲取實體類的物件FlyweightPatternDemo.java

package com.gougoucompany.designpattern.flyweight;

public class FlyweightPatternDemo {
	private static final String colors[] =
		{"Red", "Green", "Blue", "White", "Black"};
	
	public static void main(String args[]) {
		for(int i = 0; i < 20; ++i) {
			Circle circle = 
					(Circle)ShapeFactory.getCircle(getRandomColor());
			circle.setX(getRandomX());
			circle.setY(getRandomY());
			circle.setRadius(100);
			circle.draw();
		}
	}
	
	private static String getRandomColor() {
		return colors[(int)(Math.random() * colors.length)];
	}
	
	private static int getRandomX() {
		return (int)(Math.random() * 100);
	}
	
	private static int getRandomY() {
		return (int)(Math.random() * 100);
	}

}

result:

result:
Creating circle of color : White
Cicle: Draw() [Color : White, x: 56, y: 56, radius: 100
Creating circle of color : Green
Cicle: Draw() [Color : Green, x: 85, y: 5, radius: 100
Creating circle of color : Red
Cicle: Draw() [Color : Red, x: 92, y: 95, radius: 100
Cicle: Draw() [Color : Red, x: 72, y: 88, radius: 100
Cicle: Draw() [Color : Green, x: 61, y: 48, radius: 100
Cicle: Draw() [Color : Green, x: 26, y: 85, radius: 100
Cicle: Draw() [Color : Green, x: 39, y: 31, radius: 100
Cicle: Draw() [Color : Green, x: 53, y: 77, radius: 100
Creating circle of color : Black
Cicle: Draw() [Color : Black, x: 81, y: 15, radius: 100
Cicle: Draw() [Color : Green, x: 41, y: 70, radius: 100
Creating circle of color : Blue
Cicle: Draw() [Color : Blue, x: 87, y: 36, radius: 100
Cicle: Draw() [Color : Blue, x: 72, y: 80, radius: 100
Cicle: Draw() [Color : White, x: 61, y: 28, radius: 100
Cicle: Draw() [Color : Red, x: 97, y: 20, radius: 100
Cicle: Draw() [Color : White, x: 16, y: 99, radius: 100
Cicle: Draw() [Color : Green, x: 91, y: 35, radius: 100
Cicle: Draw() [Color : Blue, x: 66, y: 64, radius: 100
Cicle: Draw() [Color : White, x: 3, y: 75, radius: 100
Cicle: Draw() [Color : Green, x: 4, y: 82, radius: 100
Cicle: Draw() [Color : Green, x: 58, y: 39, radius: 100