23种设计模式之享元设计模式
(Flyweight Pattern)
结构型设计模式
通过共享技术,避免相同细粒度对象的重复创建。
享元模式可以避免大量非常相似类的开销。在程序设计中,有时候需要生成大量细粒度的类实例来表示数据,如果能发现这些实例除了几个参数外基本相同,就能将这些不相同的参数移出类外,在方法调用时将它们传递进来,就可以通过共享大幅度地减少单个实例的数目。
介绍
意图: 运用共享技术有效地支持大量细粒度的对象。(缓存内部状态)
优点: 大大减少对象的创建,降低系统的内存,使效率提高。
缺点: 提高了系统的复杂度,需要分离出外部状态和内存状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。
核心角色:
- 抽象享元角色(Flyweight): 享元对象抽象基类或者接口,同时定义出对象的外部状态和内部状态的接口或实现。
- 具体享元角色(ConcreteFlyweight):实现抽象角色定义的业务。该角色的内部状态处理应该与环境有关,不能出现有一个操作改变内部状态,同时修改了外部状态。
- 享元工厂(FlyweightFactory): 负责管理享元对象池和创建享元对象。
主要代码: 内部状态和外部状态的划分
何时使用:
- 系统中有大量的重复对象
- 这些对象消耗大量的内存
- 这些对象的状态大部分可以内部化
- 这些对象可以按照内蕴状态分为很多组,当把外蕴对象从对象中剔除出来时,每一组对象都可以用一个对象来代替。
- 系统不依赖于这些对象身份,这些对象是不可分辨的。
1. 代码实现
我们先来看一段没有使用享元模式的代码
abstract class Phone{
public abstract void Use();
}
class NokiaPhone extend Phone{
private String phoneType;
//手机中的服务
private String serverIp;
private String serverPort;
private String serverConf;
//手机号码和外观
private String phoneNumber;
private String phoneAppearance;
public NokiaPhone(String phoneType,String serverIp,String serverPort,String phoneNumber){
this.phoneType = phoneType;
this.serverIp = serverIp;
this.serverPort = serverPort;
this.phoneNumber = phoneNumber;
}
@Override
public void user(){
sout("serverIp"+serverIp);
sout("serverPort"+serverPort);
sout("serverConf"+serverConf);
sout("call:phoneNumebr"+ this.phoneNumber)
}
}
可以看到,我们每次想要调用use方法时,都会给里面的IP,port,等信息重复赋值。同一个手机的型号,他的服务器配置信息都是相同的,接下来我们通过享元模式,将一个大的对象进行细粒度的划分。
abstract class Phone{
public abstract void Use(UnShardProperties unShardProperties);
}
class NokiaPhone extend Phone{
private String phoneType;
//手机中的服务
private NetworkService networkService; // 服务器配置信息(Shard)
//手机号码和外观
private UnShardProperties unShardProperties; //不共享的配置信息
public NokiaPhone(String phoneType){
//这里假设手机连接网络的服务配置都是相同的,所以可以从缓存的代码处,取出直接赋值即可
this.networkService = defaultNetworkService;
this.phoneType = phoneType;
}
@Override
public void user(UnShardProperties unShardProperties){
sout("serverIp"+networkService.getServerIp());
sout("serverPort"+networkService.getServerPort());
sout("serverConf"+networkService.getServerConf();
sout("call:phoneNumebr"+ unShardProperties.getPhoneNumber())
}
}
class PhoneFactory{
private Map<String,Phone> cacheMap = new HashMap<String, Phone>();
public Phone getPhone(String key){
if (!cacheMap.containsKey(key)){
cacheMap.put(key,new Phone(key))
}
return cacheMap.get(key);
}
}
class FlyweightPatternDemo{
psvm{
PhoneFactory factory = new PhoneFactory();
Phnoe phone = factory.getPhone("NOkia E66666666");
phone.use(new UnShardProperties("133233XXXXX","red"));
}
}
注意看上方细粒度的提取(不会随环境改变而改变的共享部分抽取为内部状态)。
享元模式也可以看作是对象的部分数据做缓存操作,继而重复使用。
2. 享元设计模式的内部状态和外部状态
2.1 什么是内部状态?
在享元对象内部并且不会随环境改变而改变的共享部分。
2.2 什么是外部状态?
而随外部环境改变而改变的就是外部状态。