享元模式以共享的方式高效地支持大量細粒度對象的重用,享元對象能作到共享的關鍵是區分了內部狀態(Intrinsic State)和外部狀態(Extrinsic State)。下面將對享元的內部狀態和外部狀態進行簡單的介紹:java
(1)內部狀態是存儲在享元對象內部而且不會隨環境改變而改變的狀態,內部狀態能夠共享。如字符的內容,不會隨外部環境的變化而變化,不管在任何環境下字符「a」始終是「a」,都不會變成「b」。 (2)外部狀態是隨環境改變而改變的、不能夠共享的狀態。享元對象的外部狀態一般由客戶端保存,並在享元對象被建立以後,須要使用的時候再傳入到享元對象內部。一個外部狀態與另外一個外部狀態之間是相互獨立的。如字符的顏色,能夠在不一樣的地方有不一樣的顏色,例若有的「a」是紅色的,有的「a」是綠色的,字符的大小也是如此,有的「a」是五號字,有的「a」是四號字。並且字符的顏色和大小是兩個獨立的外部狀態,它們能夠獨立變化,相互之間沒有影響,客戶端能夠在使用時將外部狀態注入享元對象中。
在享元模式結構圖中包含以下幾個角色:mysql
● Flyweight(抽象享元類):一般是一個接口或抽象類,在抽象享元類中聲明瞭具體享元類公共的方法,這些方法能夠向外界提供享元對象的內部數據(內部狀態),同時也能夠經過這些方法來設置外部數據(外部狀態)。 ● ConcreteFlyweight(具體享元類):它實現了抽象享元類,其實例稱爲享元對象;在具體享元類中爲內部狀態提供了存儲空間。一般咱們能夠結合單例模式來設計具體享元類,爲每個具體享元類提供惟一的享元對象。 ● UnsharedConcreteFlyweight(非共享具體享元類):並非全部的抽象享元類的子類都須要被共享,不能被共享的子類可設計爲非共享具體享元類;當須要一個非共享具體享元類的對象時能夠直接經過實例化建立。 ● FlyweightFactory(享元工廠類):享元工廠類用於建立並管理享元對象,它針對抽象享元類編程,將各類類型的具體享元對象存儲在一個享元池中,享元池通常設計爲一個存儲「鍵值對」的集合(也能夠是其餘類型的集合),能夠結合工廠模式進行設計;當用戶請求一個具體享元對象時,享元工廠提供一個存儲在享元池中已建立的實例或者建立一個新的實例(若是不存在的話),返回新建立的實例並將其存儲在享元池中。
1.主要優勢sql
(1) 能夠極大減小內存中對象的數量,使得相同或類似對象在內存中只保存一份,從而能夠節約系統資源,提升系統性能。 (2) 享元模式的外部狀態相對獨立,並且不會影響其內部狀態,從而使得享元對象能夠在不一樣的環境中被共享。
2.主要缺點數據庫
(1) 享元模式使得系統變得複雜,須要分離出內部狀態和外部狀態,這使得程序的邏輯複雜化。 (2) 爲了使對象能夠共享,享元模式須要將享元對象的部分狀態外部化,而讀取外部狀態將使得運行時間變長。
3.適用場景編程
(1) 一個系統有大量相同或者類似的對象,形成內存的大量耗費。 (2) 對象的大部分狀態均可之外部化,能夠將這些外部狀態傳入對象中。 (3) 在使用享元模式時須要維護一個存儲享元對象的享元池,而這須要耗費必定的系統資源,所以,應當在須要屢次重複使用享元對象時才值得使用享元模式。
享元模式的主要目的是實現對象的共享,即共享池,當系統中對象多的時候能夠減小內存的開銷,一般與工廠模式一塊兒使用。性能
一提到共享池,咱們很容易聯想到Java裏面的JDBC鏈接池,想一想每一個鏈接的特色,咱們不難總結出:適用於做共享的一些個對象,他們有一些共有的屬性,就拿數據庫鏈接池來講,url、driverClassName、username、password及dbname,這些屬性對於每一個鏈接來講都是同樣的,因此就適合用享元模式來處理,建一個工廠類,將上述相似屬性做爲內部數據,其它的做爲外部數據,在方法調用時,當作參數傳進來,這樣就節省了空間,減小了實例的數量。看個例子:url
看下數據庫鏈接池的代碼:設計
public class ConnectionPool { /*享元池*/ private Vector<Connection> pool; /*內部狀態*/ private String url = "jdbc:mysql://localhost:3306/test"; private String username = "root"; private String password = "root"; private String driverClassName = "com.mysql.jdbc.Driver"; private int poolSize = 100; private static ConnectionPool instance = null; Connection conn = null; /*構造方法,作一些初始化工做*/ private ConnectionPool() { pool = new Vector<Connection>(poolSize); for (int i = 0; i < poolSize; i++) { try { Class.forName(driverClassName); conn = DriverManager.getConnection(url, username, password); pool.add(conn); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } } } /* 返回鏈接到鏈接池 */ public synchronized void release() { pool.add(conn); } /* 返回鏈接池中的一個數據庫鏈接 */ public synchronized Connection getConnection() { if (pool.size() > 0) { Connection conn = pool.get(0); pool.remove(conn); return conn; } else { return null; } } }
經過鏈接池的管理,實現了數據庫鏈接的共享,不須要每一次都從新建立鏈接,節省了數據庫從新建立的開銷,提高了系統的性能!code