1、引言
一些大型的博客網站、電子商務網站,裡面每一個博客或者商家都可以理解為一個小的網站,他們是如何做到的呢,如何實現同樣的核心代碼,不同的用戶用有不同的效果,實現類型的網站得到復用而不是復制相同的代碼呢?
那些博客網站、電子商務網站是利用用戶ID的不同,來區別不同的用戶,具體的數據和模板可以不同,但代碼核心和數據庫卻是共享的。
假如很多項目到來時,他們需要的網站結構相似度很高,而且都不是那種高訪問量的網站,如果分成多個虛擬空間來處理,相當於一個相同網站的實例對象很多,這是造成服務器的大量資源浪費,當然更實際的其實是鈔票的浪費,如果整合到一個網站中,共享其相關的代碼和數據,那麼對於硬盤、內存、CPU、數據庫空間等服務器資源都可以達到共享,減少服務器資源,而對於代碼,由於是一份實例,維護和擴展都更加容易。
好像很不錯的樣子,我也這麼覺得,那我們就一起來了解一下, 如何做到共享一份實例吧。
2、定義:
享元模式(Flyweight),運用共享技術有效地支持大量細粒度的對象。
3、UML(結構圖)
類解析:
FlyWeightFactory:一個共享工廠,用來創建並管理Flyweight對象。它主要是用來確保合理地共享Flyweight,當用戶請求一個Flyweight是,FlyweightFactory對象提供一個已創建得實例或者創建一個(如果不存在的話)
Flyweight:所有具體享元類的超類或接口,通過這個接口,Flyweight可以接受並作用於外部狀態,
ConcreteFlyweight:繼承Flyweight超類或實現Flyweight接口,並為內部狀態增加存儲空間
UnsharedConcreteFlyweight:指那些不需要共享的Flyweight。因為Flyweight接口共享成為可能,但它不強制要求共享。
4、實踐
網站共享代碼
package com.zcr.flyweight;
//享元設計模式實現
//網站抽象類
public abstract class WebSite
{
public abstract void Use();
}
package com.zcr.flyweight;
//具體的網站類
public class ConcreteWebSite extends WebSite
{
private String name = "";
public ConcreteWebSite(String name)
{
this.name = name;
}
@Override
public void Use()
{
System.out.println("網站分類:"+name);
}
}
package com.zcr.flyweight;
import java.util.Hashtable;
//網站工廠
public class WebsiteFactory
{
private Hashtable<String,WebSite> flyweights = new Hashtable<String, WebSite>();
//獲得網站分類
public WebSite GetWebSiteCategory(String key)
{
//判斷是否存在這個對象,如果存在則直接返回,若不存在,則實例化它再返回
if(!flyweights.contains(key))
{
flyweights.put(key, new ConcreteWebSite(key));
}
return flyweights.get(key);
}
//獲取網站分類的總數
public int GetWebSiteCount()
{
return flyweights.size();
}
}
package com.zcr.flyweight;
public class FlyweightTest
{
public static void main(String[] args)
{
WebsiteFactory f = new WebsiteFactory();
WebSite fx = f.GetWebSiteCategory("產品展示");
fx.Use();
WebSite fy = f.GetWebSiteCategory("產品展示");
fy.Use();
WebSite fz = f.GetWebSiteCategory("產品展示");
fz.Use();
WebSite fl = f.GetWebSiteCategory("博客");
fl.Use();
WebSite fm = f.GetWebSiteCategory("博客");
fm.Use();
WebSite fn = f.GetWebSiteCategory("博客");
fn.Use();
System.out.println("網站分類總數為:" + f.GetWebSiteCount());
}
}
結果:
上面只實現了對象的共享,不管建立幾個網站,是要是‘產品展示’都好是一樣的的,只要是‘博客’也是完全相同的,但這樣是有問題的,你給企業建立的網站不是一家企業,它們的數據不會相同,所以至少它們應該有不同的賬號,那我們該怎麼辦呢?
概念:
內部狀態:在享元內部並且不會隨環境改變而改變的共享部分
外部狀態:隨環境改變而改變的、不可能共享的狀態就是外部狀態
享元模式可以避免大量非常相似類的開銷,在程序設計中,有時需喲生成大量細粒度的類實例來表示數據,如果能發現這些實例除了幾個參數外基本上都是相同的,有時就能夠大幅度的減少需要實例化類的數量。如果能把那些參數移到類實例的外面,在方法調用時將它們傳進來,就可以通過共享大幅度地減少單個實例的數目。也就是說,享元模式Flyweight執行時所需要的狀態有內部的也有外部的,內部狀態存儲於ConcreteFlyweight對象之中,而外部對象則應該考慮由客戶端對象存儲或計算,當調用Flyweight對象的操作時,將該狀態傳遞給它。
應用場景
如果一個應用程序使用了大量的對象,而大量的這些對象造成了很大的存儲開銷時就應該考慮使用;還有就是對象的大多數狀態可以外部狀態,如果刪除對象的外部狀態,那麼可以用相對較少的共享對象取代很多組對象,此時可以考慮使用享元模式。
java的String就是采用了這種方式。
package com.zcr.flyweight2;
public class User
{
private String name;
public User(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
package com.zcr.flyweight2;
//享元設計模式實現
//網站抽象類
public abstract class WebSite
{
//“使用”方法需要傳遞“用戶”對象
public abstract void Use(User user);
}
package com.zcr.flyweight2;
//具體的網站類
public class ConcreteWebSite extends WebSite
{
private String name = "";
public ConcreteWebSite(String name)
{
this.name = name;
}
@Override
public void Use(User user)
{
System.out.println("網站分類:"+name+"用戶名:"+user.getName());
}
}
package com.zcr.flyweight2;
import java.util.Hashtable;
//網站工廠
public class WebsiteFactory
{
private Hashtable<String,WebSite> flyweights = new Hashtable<String, WebSite>();
//獲得網站分類
public WebSite GetWebSiteCategory(String key)
{
//判斷是否存在這個對象,如果存在則直接返回,若不存在,則實例化它再返回
if(!flyweights.contains(key))
{
flyweights.put(key, new ConcreteWebSite(key));
}
return flyweights.get(key);
}
//獲取網站分類的總數
public int GetWebSiteCount()
{
return flyweights.size();
}
}
package com.zcr.flyweight2;
public class FlyweightTest
{
public static void main(String[] args)
{
WebsiteFactory f = new WebsiteFactory();
WebSite fx = f.GetWebSiteCategory("產品展示");
fx.Use(new User("小菜"));
WebSite fy = f.GetWebSiteCategory("產品展示");
fy.Use(new User("大煙籠"));
WebSite fz = f.GetWebSiteCategory("產品展示");
fz.Use(new User("大菜"));
WebSite fl = f.GetWebSiteCategory("博客");
fl.Use(new User("老頑童"));
WebSite fm = f.GetWebSiteCategory("博客");
fm.Use(new User("肥菜"));
WebSite fn = f.GetWebSiteCategory("博客");
fn.Use(new User("小小斌"));
System.out.println("網站分類總數為:" + f.GetWebSiteCount());
}
}
結果: