享元模式的宗旨:通过共享来为大量的细粒度对象提供有效支持。
避免大量拥有相同类型的小类(细粒度,小类中的不可变部份)的开销(如耗费内存),使所有客户代码共享一个实例(元类)。
怎么使用?(以人力资源系统中的公司员工为例):
一般来说,在需要对某个员工进行访问的时候,我们都会new一个员工类,如果两个客户代码同时访问同一个员工,则会产生两个相同(或部份相同)的同一员工实例,这样,对于一个员工来说,内存中就可能有该员工的多个实例存在,如果一个企业有10000个员工,那内存中实际的员工实例可能就不止10000个。在这种情况下,我们就可以用享元模式来解决这个问题。
(假设我们系统中原有的类如下图OldPerson所示)
public class OldPerson { private String id; private String name; private double basicSalary; private double variableSalary; /* 省略setter和getter */ public OldPerson(String id, String name) { this.id = id; this.name = name; } } |
我们需要分两步来完成这个模式:
1、提取类中的不可变部份
仔细思考上面的OldPerson类,我们发现,在任何一种情况下,员工的id号和name都是不变的,不管是计算基础薪资的客户代码,还是计算绩效薪资的客户代码,id和name都具有不变性,所以我们可以将这两个属性列为员工类的不可变部份,利用这两个不变属性,创建一个接口NewPerson。
1 2 3 4 5 | public interface NewPerson { public String getId(); public String getName(); public String toString(); } |
2、创建享元工厂
在这一步,我们实例化享元并组织共享享元的客户代码,而且我们必须保证各个客户代码都使用享元工厂创建享元实例,而不能自己创建。下图是我们创建的享元工厂。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | public class PersonFactory { private static Map<string , NewPerson> persons = new HashMap(); static { persons.put("001", new NewPersonImpl("001", "Tom")); persons.put("002", new NewPersonImpl("002", "Jerry")); } public static NewPerson getPerson(String id) { return persons.get(id); } private static class NewPersonImpl implements NewPerson { private String id; private String name; public NewPersonImpl(String id, String name) { this.id = id; this.name = name; } public String getId() {return id;} public String getName() {return name;} public String toString() { return this.id + " ===> " + this.name; } } } </string> |
在PersonFactory中,我们创建了它的一个私有内部类NewPersonImpl,并实现了NewPerson,这样即可禁止客户代码自己实例化NewPersonImple,这也是为什么我们需要创建NewPerson接口的原因了。
客户代码调用都需要使用PersonFactory.getPerson来获得某员工的唯一实例。比如:
1 2 3 4 5 6 | public class TestNewPerson { public static void main(String[] args) { System.out.println(PersonFactory.getPerson("001")); System.out.println(PersonFactory.getPerson("002")); } } |
需要注意的是,享元对象中的属性必须是不可变的。