享元模式采用一个共享来避免大量拥有相同内容对象的开销。这种开销最常见、最直观的就是内存的损耗。享元对象能做到共享的关键是区分内蕴状态(Internal State)和
外蕴状态(External State)。
一个内蕴状态是存储在享元对象内部的,并且是不会随环境的改变而有所不同。因此,一个享元可以具有内蕴状态并可以共享。
一个外蕴状态是随环境的改变而改变的、不可以共享的。享元对象的外蕴状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传入到享元对象内部。外
蕴状态不可以影响享元对象的内蕴状态,它们是相互独立的。
享元模式的优点:减少对象数量,节省内存空间。
享元模式的缺点:维护共享对象,需要额外的开销(用一个线程来维护垃圾回收)。
享元模式的本质:分离与共享
何时使用享元模式:
如果一个应用程序使用了大量的细粒度对象,可以使用享元模式来减少对象数量。
如果使用大量的对象,造成很大的存储开销,可以使用享元模式来减少对象数量,并节约内存。
如果对象的大多数状态都可以转变为外部状态,可以使用享元对象来实现外部状态与内部状态的分离。
享元模式可以分成单纯享元模式和复合享元模式两种形式。
具体享元(ConcreteFlyweight)角色:实现抽象享元角色所规定出的接口。如果有内蕴状态的话,必须负责为内蕴状态提供存储空间。
享元工厂(FlyweightFactory)角色 :本角色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当地共享。当一个客户端对象调用一个享元对象的
时候,享元工厂角色会检查系统中是否 已经有一个符合要求的享元对象。如果已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象
的话,享元工厂角色就应当创 建一个合适的享元对象。
好了,废话不多说,直接上代码,首先抽象一个场景:
公司的管理层和普通员工对员工的信息有不同的权限。管理层可以修改员工的个人信息,普员工只能查看不能修改,擅自修改系统会给出提示。员工的个人信息是可以共享的,但是起职能范围和操作权限可以被管理层修改。
抽象享元角色:
public interface IFlyWeight {
/**
* 判断传入的安全实体和权限,是否和享元对象内部状态匹配
* 参数 securityEntity 和 permit 是外蕴对象
* @param securityEntity
* @param permit
* @return
*/
public boolean match(String securityEntity, String permit);
}
public class AuthorizationFlyweight implements IFlyWeight {
/**
* 内蕴对象,安全实体
*/
private String mSecurityEntity;
/**
* 内蕴对象,权限
*/
private String mPermit;
public AuthorizationFlyweight(String state) {
String str[] = state.split(",");
this.mSecurityEntity = str[0];
this.mPermit = str[1];
}
@Override
public boolean match(String securityEntity, String permit) {
if (mSecurityEntity.equals(securityEntity) && mPermit.equals(permit)) {
return true;
}
return false;
}
}
享元工厂:
public class FlyWeightFactory {
private static FlyWeightFactory mFactory = new FlyWeightFactory();
private FlyWeightFactory() {
}
public static FlyWeightFactory getInstance() {
return mFactory;
}
/**
* 缓存多个人IFlyWeight 对象
*/
private Map flyMap = new HashMap();
/**
* 获取享元单元
* @param key
* @return
*/
public IFlyWeight getFlyWeight(String key) {
IFlyWeight fly = flyMap.get(key);
if(fly==null){
fly = new AuthorizationFlyweight(key);
flyMap.put(key, fly);
}
return fly;
}
}
员工信息的管理工具:
public class SecurityManager {
private static SecurityManager manager = new SecurityManager();
private SecurityManager() {
}
public static SecurityManager getInstance() {
return manager;
}
/**
* 存放登录人员的权限
*/
private Map> map = new HashMap>();
public void logon(String user) {
Collection coll = queryByUser(user);
map.put(user, coll);
}
/**
* 从数据库中获取某人所有的权限
*
* @param user
* @return
*/
private Collection queryByUser(String user) {
Collection coll = new ArrayList();
for (String s : TestDB.coll) {
String str[] = s.split(",");
if (str[0].equals(user)) {
IFlyWeight fly = FlyWeightFactory.getInstance().getFlyWeight(
str[1] + "," + str[2]);
coll.add(fly);
}
}
return coll;
}
/**
* 判断某个用户对某个安全实体是否有某种权限
*
* @param user
* 用户
* @param securityEntity
* 安全实体
* @param permit
* 权限
* @return
*/
public boolean hasPermit(String user, String securityEntity, String permit) {
Collection coll = map.get(user);
if (coll == null || coll.size() == 0) {
System.out.println(user + "没有登录或者没有该权限...");
return false;
}
for (IFlyWeight fly : coll) {
if (fly.match(securityEntity, permit)) {
return true;
}
}
return false;
}
}
模拟一个数据库保存员工信息:
public class TestDB {
public static Collection coll = new ArrayList();
static {
coll.add("张三,人员列表,查看");
coll.add("李四,人员列表,查看");
coll.add("李四,薪资列表,查看");
coll.add("李四,薪资列表,修改");
for (short i = 0; i < 3; i++) {
coll.add("张三" + i + ",人员列表,查看");
}
}
}
SecurityManager manager = SecurityManager.getInstance();
manager.logon("张三");
manager.logon("李四");
boolean b1 = manager.hasPermit("张三", "薪资列表", "查看");
boolean b2 = manager.hasPermit("李四", "薪资列表", "查看");
boolean b3 = manager.hasPermit("张三", "人员列表", "查看");
System.out.println("b1 = "+b1);
System.out.println("b2 = "+b2);
System.out.println("b3 = "+b3);
运行效果:
从数据库中可以看出来,张三是人事的普通员工,只能查看公司在职人员,对员工的薪资无权限查看,b1返回false,b3返回true
李四是人事普通员工兼财务主管,可以修改查询员工薪资并且可以查看在职员工信息,b2返回true
如果
boolean b4 = manager.hasPermit("王武", "人员列表", "查看");
因为“王武”这个人没有登录,所以提示信息是 “没有登录或者没有该权限...”