面向对象技术可以很好的解决一些灵活性或可扩展性问题,但在很多情况下需要在系统中增加类和对象的个数。当对象数量太多时,将导致对象创建及垃圾回收的代价过高,造成性能下降等问题。享元模式通过共享相同或者相似的细粒度对象解决了这一类问题。
享元模式(Flyweight Pattern),又称轻量级模式(这也是其英文名为FlyWeight的原因),通过共享技术有效地实现了大量细粒度对象的复用。
享元模式类图如下
本文代码可从 作者Github 下载
享元接口,定义共享接口
package com.jasongj.flyweight;
public interface FlyWeight {
void action(String externalState);
}
具体享元类,实现享元接口。该类的对象将被复用
package com.jasongj.flyweight;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ConcreteFlyWeight implements FlyWeight {
private static final Logger LOG = LoggerFactory.getLogger(ConcreteFlyWeight.class);
private String name;
public ConcreteFlyWeight(String name) {
this.name = name;
}
@Override
public void action(String externalState) {
LOG.info("name = {}, outerState = {}", this.name, externalState);
}
}
享元模式中,最关键的享元工厂。它将维护已创建的享元实例,并通过实例标记(一般用内部状态)去索引对应的实例。当目标对象未创建时,享元工厂负责创建实例并将其加入标记-对象映射。当目标对象已创建时,享元工厂直接返回已有实例,实现对象的复用。
package com.jasongj.factory;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jasongj.flyweight.ConcreteFlyWeight;
import com.jasongj.flyweight.FlyWeight;
public class FlyWeightFactory {
private static final Logger LOG = LoggerFactory.getLogger(FlyWeightFactory.class);
private static ConcurrentHashMap<String, FlyWeight> allFlyWeight = new ConcurrentHashMap<String, FlyWeight>();
public static FlyWeight getFlyWeight(String name) {
if (allFlyWeight.get(name) == null) {
synchronized (allFlyWeight) {
if (allFlyWeight.get(name) == null) {
LOG.info("Instance of name = {} does not exist, creating it");
FlyWeight flyWeight = new ConcreteFlyWeight(name);
LOG.info("Instance of name = {} created");
allFlyWeight.put(name, flyWeight);
}
}
}
return allFlyWeight.get(name);
}
}
从上面代码中可以看到,享元模式中对象的复用完全依靠享元工厂。同时本例中实现了对象创建的懒加载。并且为了保证线程安全及效率,本文使用了双重检查(Double Check)。
本例中, name
可以认为是内部状态,在构造时确定。 externalState
属于外部状态,由客户端在调用时传入。