1.前言
大家还记得本文集最早提的单例模式吗?通过一个全局变量来避免重复创建对象而产生的消耗,若系统存在大量的相似对象时,又该如何处理?参照单例模式,可通过对象池缓存可共享的对象,避免创建多对象,尽可能减少内存的使用,提升性能,防止内存溢出。
2.概念
享元模式使用共享对象可有效地支持大量细粒度的对象。为了解释一下,何为细粒度对象,引用《Android源码设计模式解析与实战》中的描述。
对象中的部分状态是可以共享的,这些状态称为内部状态,它们不会随着环境变化;相反,不可共享的状态称为外部状态,它们会随着环境的改变而改变。
由于共享的不是一个对象,所以需要容器,最经典的便是Map。它的键是内部状态,值为对象本身。获取对象的过程:先判断容器中是否有缓存,若有,直接使用;若没有,创建对象并存入容器。
3.场景
一家网络会所想订购一批电脑,为了满足不同档次的用户需求,要多种配置和款式:
- 会员与普通用户使用的电脑配置基本相似,仅内存更大;
- 超级会员则从机箱开始完全不同。
老板便联系电脑销售点,希望服务人员带几款样机过来体验。服务人员详细询问老板的需求后,带了两个配置好的机箱,几张内存条就过来了。
4.写法
// 定义所有对象具有的行为
public interface Mainboard {
void showConfig();
}
// 被创建的具体对象
public class Computer implements Mainboard {
private String memery;
private String computerCase;
private String cpu;
public Computer(String memery, String computerCase, String cpu) {
this.memery = memery;
this.computerCase = computerCase;
this.cpu = cpu;
}
public void setMemery(String memery) {
this.memery = memery;
}
public void setCpu(String cpu) {
this.cpu = cpu;
}
@Override
public void showConfig() {
System.out.println("电脑的配置是: " + computerCase + " / " + cpu + " / " + memery);
}
}
若展示会员机,则只需要更换内存即可;若展示超级会员机,只能使用另一个机箱。
// 享元工厂,管理对象池和创建对象
public class Service {
static Map<String, Mainboard> computerMap = new ConcurrentHashMap<>();
public static void getInfo(String memery, String computerCase, String cpu) {
if (computerMap.containsKey(computerCase)) {
System.out.println("使用缓存->");
Mainboard mainboard = computerMap.get(computerCase);
if (mainboard instanceof Computer) {
Computer computer = (Computer) mainboard;
computer.setMemery(memery);
computer.setCpu(cpu);
computer.showConfig();
}
} else {
System.out.println("创建对象->");
Mainboard mainboard = new Computer(memery, computerCase, cpu);
computerMap.put(computerCase, mainboard);
mainboard.showConfig();
}
}
}
public class Client {
public static void main(String[] args) {
// 开始向会所老板展示
Service.getInfo("8G", "技嘉", "i5");
Service.getInfo("16G", "技嘉", "i5");
Service.getInfo("16G", "华硕", "i7");
}
}
Java中的String是存在常量池中的,逻辑与这个类似。当其它地方使用相同字符串时,则直接使用缓存中的,不会重复创建。这种只适合字面值赋值,即直接通过双引号设置的字符串值;若是通过new构建,则是新的对象。
5.总结
通过享元对象的内部状态来判断此对象是否需要创建,将“对象是否相同”细化到属性上。优势很明显,但需将部分状态(属性)外部化,也增加了系统的复杂性,还得确保与内部状态互不影响。