Java 设计模式 之 享元模式(Flyweight)
享元模式的重点在于分离变与不变。把一个对象的状态分成内部状态和外部状态,内部状态是不变的,外部状态是可变的。然后通过共享不变的部分,达到减少对象数量并节约内存的目的。
内部状态:通常指的是包含在享元对象内部的、对象本身的状态,不会随享元环境而变化,因此可共享。
外部状态:是享元对象之外的状态,取决于使用享元的场景,会根据使用场景而变化,因此不可共享。如果享元对象需要这些外部状态的话,可以从外部传到外部状态中。
外部状态和内部状态是相互独立的,外部状态的变换不会因起内部状态的变化。
享元对象的优点:减少对象数量,节省内存空间。
享元模式的缺点:维护共享对象,需要额外的开销(用一个线程来维护垃圾回收)。
享元模式的本质:分离与共享。
何时使用享元对象:
● 如果一个应用程序使用了大量的细粒度对象,可以使用享元模式来减少对象数量。
● 如果使用大量的对象,造成很大的存储开销,可以使用享元模式来减少对象数量,并节约内存。
● 如果对象的大多数状态都可以转变为外部状态,可以使用享元对象来实现外部状态与内部状态的分离。
◆ Flyweight :享元接口,通过这个接口可以接受并作用于外部状态。通过这个接口传入外部的状态。
◆ ConcreteFlyweight :具体的享元实现对象,必须是可共享的,需要封装享元对象的内部状态。
◆ UnsharedConcreteFlyweight :非共享的享元实现对象,并不是所有的享元对象都可以共享,非共享的享元对象通常是享元对象的组合对象。
◆ FlyweightFactory :享元工厂,主要用来创建并管理共享的享元对象,并对外提供访问共享享元的接口。
样例:
/**
* 享元接口
*/
public interface Flyweight {
/**
* 判断传入的安全实体和权限,是否和享元对象内部状态匹配
* @param securityEntity
* @param permit
* @return
*/
public boolean match(String securityEntity, String permit);
}
/**
* 享元对象
*/
public class AuthorizationFlyweight implements Flyweight {
/**
* 内部状态,安全实体
*/
private String securityEntity;
/**
* 内部状态,权限
*/
private String permit;
public AuthorizationFlyweight(String state) {
String ss[] = state.split(",");
this.securityEntity = ss[0];
this.permit = ss[1];
}
@Override
public boolean match(String securityEntity, String permit) {
if(this.securityEntity.equals(securityEntity) && this.permit.equals(permit)){
return true;
}
return false;
}
public String getSecurityEntity() {
return securityEntity;
}
public String getPermit() {
return permit;
}
}
/**
* 享元工厂,通常实现为单例
*
*/
public class FlyweightFactory {
private static FlyweightFactory factory = new FlyweightFactory();
private FlyweightFactory(){
}
public static FlyweightFactory getInstance(){
return factory;
}
/**
* 缓存多个Flyweight对象
*/
private Map<String, Flyweight> fsMap = new HashMap<String, Flyweight>();
/**
* 获取享元对象
* @param key
* @return
*/
public Flyweight getFlyweight(String key) {
Flyweight f = fsMap.get(key);
if(f == null){
f = new AuthorizationFlyweight(key);
fsMap.put(key, f);
}
return f;
}
}
/**
* 安全管理器,实现成单例
*
*/
public class SecurityMgr {
private static SecurityMgr securityMgr = new SecurityMgr();
private SecurityMgr(){
}
public static SecurityMgr getInstance() {
return securityMgr;
}
/**
* 在运行期间,用来存放登陆人员对应的权限
* 在Web应用中,这些数据通常存放到session中
*/
private Map<String, Collection<Flyweight>> map = new HashMap<String, Collection<Flyweight>>();
/**
* 登陆功能
* @param user
*/
public void login(String user) {
Collection<Flyweight> col = queryByUser(user);
map.put(user, col);
}
/**
* 从数据库中获取某人所拥有的权限
* @param user
* @return
*/
private Collection<Flyweight> queryByUser(String user) {
Collection<Flyweight> col = new ArrayList<Flyweight>();
for(String s : TestDB.colDB){
String ss[] = s.split(",");
if(ss[0].equals(user)){
//ss[0] - user
//ss[1] - securityEntity
//ss[2] - permit
Flyweight fm = FlyweightFactory.getInstance().getFlyweight(ss[1]+","+ss[2]);
col.add(fm);
}
}
return col;
}
/**
* 判断用户对某个安全实体是否拥有某种权限
* @param user 用户
* @param securityEntity 安全实体
* @param permit 权限
* @return
*/
public boolean hasPermit(String user, String securityEntity, String permit){
Collection<Flyweight> col = map.get(user);
if(col == null || col.size() == 0){
System.out.println(user + "没有登陆或没有被分配任何权限");
return false;
}
for(Flyweight fm : col) {
System.out.println("fm == " + fm);
if(fm.match(securityEntity, permit)){
return true;
}
}
return false;
}
}
/**
* 测试使用,在内存中存放模拟数据库的值
*
*/
public class TestDB {
public static Collection<String> colDB = new ArrayList<String>();
static {
colDB.add("张三,人员列表,查看");
colDB.add("李四,人员列表,查看");
colDB.add("李四,薪资列表,查看");
colDB.add("李四,薪资列表,修改");
for(int i = 0; i < 3; i++){
colDB.add("张三" + i + ",人员列表,查看");
}
}
}
public class Client {
public static void main(String[] args) {
SecurityMgr mgr = SecurityMgr.getInstance();
mgr.login("张三");
mgr.login("李四");
boolean f1 = mgr.hasPermit("张三", "薪资列表", "查看");
boolean f2 = mgr.hasPermit("李四", "薪资列表", "查看");
boolean f3 = mgr.hasPermit("张三", "人员列表", "查看");
System.out.println("f1 == " + f1);
System.out.println("f2 == " + f2);
System.out.println("f3 == " + f3);
for(int i = 0; i < 3; i++){
mgr.login("张三" + i);
mgr.hasPermit("张三" + i, "薪资列表", "查看");
}