1. 享元模式
1.1 簡介
??享元(FlyWeight)模式顧名思義,既是輕量級的茫蛹。享元即是共享元素,或者說是共享對象烁挟。如何共享對象呢婴洼?就是在檢測對象產(chǎn)生的時候,如果產(chǎn)生的是同一個對象撼嗓,那么直接使用已經(jīng)產(chǎn)生的柬采,聽起來很像是單例模式,其實享元模式的內(nèi)部實現(xiàn)就是很類似與單例模式的懶漢模式且警。享元的好處就是粉捻,在某些場景下可以節(jié)省內(nèi)存,從而使得程序的性能得到提升斑芜。
flyweight設(shè)計模式意圖是:
使用共享可以有效地支持大量細(xì)粒度對象
??Flyweight設(shè)計模式是一種結(jié)構(gòu)設(shè)計模式肩刃,當(dāng)需要創(chuàng)建一個類的很多對象時,可以使用Flyweight設(shè)計模式杏头。應(yīng)用Flyweight模式主要考慮一下幾個因素:
- 應(yīng)用程序要創(chuàng)建的對象數(shù)量應(yīng)該很大盈包。
- 對象創(chuàng)建在內(nèi)存上很重要,也可能很耗時醇王。
- 對象屬性可以分為內(nèi)在屬性和外在屬性呢燥,對象的外在屬性應(yīng)該由客戶端程序定義。
??應(yīng)用flyweight模式寓娩,我們需要將Object屬性劃分為內(nèi)部(intrinsic)屬性和外部(extrinsic)屬性叛氨。內(nèi)在屬性使對象唯一,而外在屬性由客戶端代碼設(shè)置并用于執(zhí)行不同的操作棘伴。
??Flyweight模式一般會和Factory模式搭配使用寞埠,一般會創(chuàng)建一個管理共享對象的Flyweight工廠,這樣實現(xiàn)對象的創(chuàng)建排嫌、保存畸裳、返回等。
2. FlyWeight示例
??我們模擬一個搜索過程淳地,在搜索中可以使用google,baidu,bing進(jìn)行搜索怖糊,因為相同搜索引擎只需要創(chuàng)建一個帅容,不需要創(chuàng)建多個,同時相同的搜索結(jié)果(網(wǎng)址)只需要返回同一個即可伍伤,所以都用到了享元模式并徘。
搜索結(jié)果類:
public class Website {
private String title;
private String url;
private long publishTimestamp;
public Website(String title, String url, long publishTimestamp) {
super();
this.title = title;
this.url = url;
this.publishTimestamp = publishTimestamp;
}
public String getTitle() {
return title;
}
public String getUrl() {
return url;
}
public long getPublishTimestamp() {
return publishTimestamp;
}
public String toString() {
return String.format("[title:%s, url:%s, time:%d]", title, url, publishTimestamp);
}
}
搜索結(jié)果享元類:
public class WebsiteRepository {
private static WebsiteRepository repo = new WebsiteRepository();
public Map<String, Website> websites = new HashMap<String, Website>();
public static WebsiteRepository getInstance() {
return repo;
}
public Website getWebsite(String name, String url, long timestamp) {
if (!websites.containsKey(url)) {
Website newsite = new Website(name, url, timestamp);
websites.put(url, newsite);
return newsite;
}
return websites.get(url);
}
public int howmanyWebsite() {
return websites.size();
}
}
搜索引擎抽象類:
public abstract class SearchEngine {
private long timeoutInMs;
private String name;
public SearchEngine (Long timeoutInMs, String name) {
this.timeoutInMs = timeoutInMs;
this.name = name;
}
public List<Website> search(String keyword) {
System.out.println(name + " search engine with timeout set to " + timeoutInMs + "start to search");
return doSearch(keyword);
}
protected abstract List<Website> doSearch(String keyword);
}
** Google:**
public class Google extends SearchEngine {
public Google(Long timeoutInMs, String name) {
super(timeoutInMs, name);
}
@Override
public List<Website> doSearch(String keyword) {
List<Website> website = new ArrayList<Website>();
website.add(WebsiteRepository.getInstance().getWebsite("title1", "url1", 1l));
website.add(WebsiteRepository.getInstance().getWebsite("title2", "url2", 1l));
website.add(WebsiteRepository.getInstance().getWebsite("title3", "url3", 1l));
website.add(WebsiteRepository.getInstance().getWebsite("title4", "url4", 1l));
return website;
}
}
** Bing:**
public class Bing extends SearchEngine {
public Bing(Long timeoutInMs, String name) {
super(timeoutInMs, name);
}
@Override
public List<Website> doSearch(String keyword) {
List<Website> website = new ArrayList<Website>();
website.add(WebsiteRepository.getInstance().getWebsite("title1", "url1", 1l));
website.add(WebsiteRepository.getInstance().getWebsite("title2", "url2", 1l));
website.add(WebsiteRepository.getInstance().getWebsite("title4", "url4", 1l));
return website;
}
}
BaiDu:
public class Baidu extends SearchEngine {
public Baidu(Long timeoutInMs, String name) {
super(timeoutInMs, name);
}
@Override
public List<Website> doSearch(String keyword) {
List<Website> website = new ArrayList<Website>();
website.add(WebsiteRepository.getInstance().getWebsite("莆田醫(yī)院", "莆田醫(yī)院", 1l));
website.add(WebsiteRepository.getInstance().getWebsite("title1", "url1", 1l));
website.add(WebsiteRepository.getInstance().getWebsite("title2", "url2", 1l));
website.add(WebsiteRepository.getInstance().getWebsite("title3", "url3", 1l));
return website;
}
}
搜索引擎享元類:
public class SearchEngineFlyweightFactory {
private static final Map<String, SearchEngine> engines = new ConcurrentHashMap<>();
public static SearchEngine getEngine(String name, long timeoutInMs, Class<? extends SearchEngine> clazz) {
String key = name + timeoutInMs;
SearchEngine engine = null;
if (engines.get(key) == null) {
try {
Constructor<? extends SearchEngine> cons = clazz.getConstructor(Long.class, String.class);
engine = cons.newInstance(timeoutInMs, name);
} catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
SearchEngine existing = engines.put(key, engine);
if (existing != null) {
engine = existing;
}
} else {
engine = engines.get(key);
}
return engine;
}
public static int howManyEngine() {
return engines.size();
}
}
客戶端調(diào)用:
public class FlyWeightPatternClient {
public static void main(String[] args) throws SecurityException, ClassNotFoundException {
search("baidu", 1000L, Baidu.class, "keyword");
search("google", 1000L, Google.class, "keyword");
search("bing", 1000L, Bing.class, "keyword");
search("baidu", 1000L, Baidu.class, "keyword");
}
private static void search(String name, long timeoutInms, Class<? extends SearchEngine> clazz, String keyword) {
SearchEngine engine = SearchEngineFlyweightFactory.getEngine(name, timeoutInms, clazz);
List<Website> websites = engine.search(keyword);
renderWebsite(websites);
System.out.println(SearchEngineFlyweightFactory.howManyEngine());
System.out.println(WebsiteRepository.getInstance().howmanyWebsite());
}
public static void renderWebsite(List<Website> websites) {
System.out.println("start to render " );
for (Website website : websites) {
System.out.println(website.toString());
}
}
}
??在WebsiteRepository類中,使用了HashMap來將Website對象保存起來扰魂,這樣就形成了一個DAC(有向無環(huán)圖)麦乞,只要websites變量不被釋放,我們使用的共享單元是不會被釋放的劝评。這樣就保證了Website對象數(shù)組不被釋放姐直,在使用享元模式的時候一定要特別注意這種情況,因為垃圾回收器(GC)在內(nèi)存占用過多的時候被喚醒蒋畜,然后清理那些被再被使用的內(nèi)存声畏,采用的方式就是DAC。
3. FlyWeight應(yīng)用
FlyWeight應(yīng)用:
- jvm中的字符串常量池姻成。相同的字符串會共享一個字符串對象插龄;
- 在HystrixCommandKey.Factory類會存儲所有線程group相同的HystrixCommandKey
- HystrixPropertiesFactory會存儲線程熔斷配置,這些都是享元模式的使用科展。
FlyWeight模式和Factory模式異同:
- 二者都提供了對象工廠的功能均牢,生成了類的實例供外部調(diào)用。
- 但Flyweight模式的目的是使某些相似對象共用類的同一個實例以達(dá)到節(jié)省內(nèi)存空間的目的才睹。
- 工廠模式則不強(qiáng)制這一點徘跪,它只是負(fù)責(zé)生成類的實例,另外砂竖,工廠模式還通過工廠的繼承來生成具有繼承關(guān)系的不同類的實例真椿,而Flyweight模式不強(qiáng)調(diào)這一點。