`

享元模式flyweight

阅读更多
问题引人:给系统加入权限控制,如普通人员可以查看本部门人员列表时,但是每个人员的工资信息,普通人员看不到。而部门经理可以看到
所有的权限系统都分为两个部分,一个是授权部分,一个是验证部分,为了理解它们,引人两个基本名词:安全实体和权限
安全实体:就是被权限系统检测的对象,比如工资数据
权限:就是需要被校验的权限对象,比如查看、修改等。
两者要一起描述才有意义。
授权:是指将对某些安全实体的某些权限分配给某些人员的过程。
验证:值判断某个人员对某个安全实体是否拥有某个或某些权限
多数采用数据库来存储授权过程产生的数据,也就是说:授权是向数据库添加数据,验证是从数据库中获取相应数据进行匹配。

假设授权过程已经完成:
张三  人员列表 查看的权限
李四 人员列表 查看的权限
李四 工资数据 查看的权限
李四  工资数据 修改的权限

试想,每次登陆都要到数据库中查询,是多么的耗时。
为了加快速度,采用一定的缓存。当人员登录的时候,就把该人员能操作的权限获取到,存储在内存中。这样每次操作进行权限验证时,从缓存中获取验证信息,典型的空间换时间。
如不使用设计模式的解决示例,其问题,1,缓存时间长度的问题,即这些数据应该被缓存多久。如果是web应用,这种跟登录人员相关的权限数据,大多放在session中缓存,
2,缓存数据与真实数据的同步问题3,缓存的多线程并发控制,有时要从缓存中取值,有时要加值。
不用设计模式的解决方法:
权限表
package notusingmode;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * 在内存中模拟数据库,准备点测试数据
 */
public class TempDB {
	private TempDB() { //不允许外部创建该对象
	}

	/**
	 * 记录每个人的权限
	 */
	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+",人员列表,查看");
		}
		
	}
}


授权模型
package notusingmode;
//授权模型
public class AuthorizationModel {

	private String user; //用户
	private String securityEntity; //安全实体
	private String permit; //权限
	public String getUser() {
		return user;
	}
	public void setUser(String user) {
		this.user = user;
	}
	public String getSecurityEntity() {
		return securityEntity;
	}
	public void setSecurityEntity(String securityEntity) {
		this.securityEntity = securityEntity;
	}
	public String getPermit() {
		return permit;
	}
	public void setPermit(String permit) {
		this.permit = permit;
	}
//	public String toString(){
//		return user+"对"+this.securityEntity+"拥有"+permit+"的权限";
//	}
//	
}

安全管理的业务逻辑
package notusingmode;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

//安全管理,实现成单例
public class SecurityMgr {
	// 缓存权限
	private Map<String, Collection<AuthorizationModel>> map = new HashMap<String, Collection<AuthorizationModel>>();
	private static SecurityMgr sm = new SecurityMgr();

	private SecurityMgr() {

	}

	public static SecurityMgr getInstance() {
		return sm;
	}

	// 模拟登录的功能
	public void login(String user) {

		// 登录时,就需要把该用户拥有的权限,从数据库中取出来,放入缓存
		Collection<AuthorizationModel> col = queryByUser(user);
		map.put(user, col);
	}

	private Collection<AuthorizationModel> queryByUser(String user) {
		Collection<AuthorizationModel> col = new ArrayList<AuthorizationModel>();

		for (String s : TempDB.colDB) {
			String ss[] = s.split(",");
			if (ss[0].equals(user)) {
				AuthorizationModel am = new AuthorizationModel();
				am.setUser(user);
				am.setPermit(ss[2]);
				am.setSecurityEntity(ss[1]);
				col.add(am);
			}
		}

		return col;
	}

	// 判断某个用户对某个安全实体是否拥有对应的权限
	public boolean hasPermit(String user, String securityEntity, String permit) {
		Collection<AuthorizationModel> col = map.get(user);
		if (col == null || col.size() == 0) {
			System.out.println(user + "没有登录或是没有被分配任何权限");
			return false;

		}

		for (AuthorizationModel am : col) {
			System.out.println("am==" + am);
			if (am.getSecurityEntity().equals(securityEntity)
					&& am.getPermit().equals(permit)) {
				return true;
			}
		}
		return false;
	}

}


package notusingmode;

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("李四","工资数据","查看");
		
		System.out.println("f1=" + f1);
		System.out.println("f1=" + f2);
		
		
		for(int i = 0;i<3;i++){
			mgr.login("张三"+i);
			mgr.hasPermit("张三"+i,"工资数据","查看");
		}
		
		
	}

}


在java中,默认的equals方法比较的是内存地址,而equals和hashCode方法的关系是,如果equals返回为true,两个实例的hashCode必须相同,反之则不成立
上面的实例,存在大量的细粒度对象,而且存在大量的重复数据,严重耗费内存,如何解决?————》享元模式
定义:运用共享技术有效地支持大量细粒度的对象。
需要分离出被换成对象实例中,哪些数据是不变且重复出现的,哪些数据是经常变化的,真正应该被缓存的数据就是那些不变的,把
它们称为对象的内部状态,而变化的数据就不缓存了,称为对象的外部状态。
在实现时,把内部状态分离出来共享,叫享元,享元工厂用来管理享元。
结构:
FlyWeight:享元接口,通过这个接口传入外部的状态,在享元对象的方法处理中可能会使用这些外部的数据。
ConcreteFlyWeight:具体的享元实现对象,必须是可共享的,需要封装FlyWeight的内部状态。
UnsharedConcreteFlyWeight:非共享的享元实现对象,并不是所有的FlyWeight实现对象都需要共享,非共享的享元实现对象通常是对共享享元对象的组合对象
FlyWeightFactory:享元工厂,主要用来创建并管理共享的享元对象。
Client:客户端,主要工作时维持一个对FlyWeight的引用,计算或存储享元对象的外部状态,可以访问共享和不共享的FlyWeight对象
package flyweight;
//享元接口
public interface FlyWeight {
//示意操作,传入外部状态
	public void operation(String extrinsicState);
}

package flyweight;

import java.util.HashMap;
import java.util.Map;

//享元工厂
public class FlyWeightFactory {

//	示意一下,缓存多个享元对象
	Map<String,FlyWeight> map = new HashMap<String,FlyWeight>();
	
	public FlyWeight getFlyWeight(String key){
//		先从缓存中查找,是否存在key对应的FlyWeight对象
		FlyWeight f = map.get(key);
		
		
		if(f==null){ //如果不存在,创建一个新的FlyWeight对象
			f= new ConcreteFlyWeight(key);
			map.put(key, f);
		}
		return f;
	}
}


package flyweight;
//享元对象
public class ConcreteFlyWeight implements FlyWeight {
	
	private String intrinsicState; // 内部状态
	public ConcreteFlyWeight(String intrinsicState) {
		this.intrinsicState = intrinsicState;
	}

	@Override
	public void operation(String extrinsicState) {
//                 具体的功能处理,可能用到享元的内部、外部状态
	}

}

package flyweight;
//不需要共享的享元对象,通常是将共享的数据进行组合而成的对象
public class UnsharedConcreteFlyWeight implements FlyWeight {
	private String allState;

	@Override
	public void operation(String extrinsicState) {
		// TODO Auto-generated method stub

	}

}

使用享元模式解决权限问题;
分析:实际上重复出现的数据主要是对安全实体和权限的描述,又二者是不分开的,而且是可以重用的。
所以安全实体和权限描述定义成享元,而和它们结合的人员数据,就可以作为享元的外部数据。
在享元模式中,享元对象分为共享与不共享对象,通常共享的是叶子对象,不共享的部分是由共享部分组合而成的,由于所有细粒度的叶子对象都已经缓存了,缓存组合对象就没有必要了
工业级的实例池有两个难点:1,动态控制实例数量,2,动态分配实例给外部使用
在flyweightSolve包中的实例中,添加上不需共享的享元对象实现如包unsharedFlyweight

对享元对象的管理,所谓垃圾,就是在缓存中存在,但是不再需要被使用的缓存中的对象。
引用计数,就是要记录享元对象被引用的次数
包manageFlyweight中实现引用计数和垃圾清除
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics