flyweight模式
名词解释
flyweigh表示蝇量级,是拳击比赛中最轻量的。用在设计模式里,将其翻译为享元模式,不
知道最开始是由谁翻译。享元,按照我的理解,元就是实例的意思,享,就是共享的意思。通
过共享,减少对象所占内存以及对象创建时间。
概述
Name | category | intent |
---|---|---|
flyweight | Structural | 用于重用有可能大量重复存在的细粒度对象,使这些大量的细粒度对象只存在一个 |
应用在
当系统中存在大量重复的对象时,可以考虑使用享元模式。该模式将对象放在一个容器里面,
当需要创建对象时,首先从容器中获取对象。若容器中不存在,则新建对象,并将其放在到容
器中。从而实现对象复用。这就像是缓存,区别于缓存的地方在于享元容器永远不失效,同时数量可控。
类图
代码
下面代码模拟的是一个搜索过程,在搜索中可以使用google,baidu,bing进行搜索,因为相同搜索引擎只需要创建一个,没有必要创建多个,所以利用到了享元模式。下面代码两次使用享元模式。
package com.liang.designpattern.flyweight;
import java.util.List;
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);
}
package com.liang.designpattern.flyweight;
import java.util.ArrayList;
import java.util.List;
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;
}
}
package com.liang.designpattern.flyweight;
import java.util.ArrayList;
import java.util.List;
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;
}
}
package com.liang.designpattern.flyweight;
import java.util.ArrayList;
import java.util.List;
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("莆田医院", "莆田医院", 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;
}
}
package com.liang.designpattern.flyweight;
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);
}
}
package com.liang.designpattern.flyweight;
import java.util.HashMap;
import java.util.Map;
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();
}
}
package com.liang.designpattern.flyweight;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
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();
}
}
package com.liang.designpattern.flyweight;
import java.util.List;
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());
}
}
}
真实案例
- jvm中的字符串常量池。相同的字符串会共享一个字符串对象;
- 在HystrixCommandKey.Factory类会存储所有线程group相同的HystrixCommandKey
- HystrixPropertiesFactory会存储线程熔断配置,这些都是享元模式的使用。