享元方式(Flyweight)

享元模式(Flyweight)
@@@模式定义:
运用共享技术有效地支持大量细粒度的对象。

@@@练习示例: 
给系统加入权限控制

@@@示例代码:
[不使用模式的实现]
\usual\AuthorizationModel.java

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

package usual;

/**
 * 描述授权数据的数据model
 */
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;
	}
}

\usual\TestDB.java
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

package usual;

import java.util.ArrayList;
import java.util.Collection;

/**
 * 供测试用,在内存中模拟数据库中的值
 */
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 + ",人员列表,查看");
		}
	}
}

\usual\SecurityMgr.java
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

package usual;

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

/**
 * 安全管理,实现成单例
 */
public class SecurityMgr {
	private static SecurityMgr securityMgr = new SecurityMgr();
	private SecurityMgr() {
	}
	
	public static SecurityMgr getInstance() {
		return securityMgr;
	}
	
	/**
	 * 在运行期间,用来存放登录人员对应的权限
	 * 在Web应用中,这些数据通常会存放到session中
	 */
	private Map<String, Collection<AuthorizationModel>> map =
		new HashMap<String, Collection<AuthorizationModel>>();
	
	/**
	 * 模拟登录的功能
	 * @param user 登录的用户
	 */
	public void login(String user) {
		// 登录时就需要把该用户所拥有的权限,从数据库中取出来,放到缓存中去
		Collection<AuthorizationModel> col = queryByUser(user);
		map.put(user, col);
	}
	
	/**
	 * 判断某用户对某个安全实体是否拥有某种权限
	 * @param user 被检测权限的用户
	 * @param securityEntity 安全实体
	 * @param permit 权限
	 * @return true表示拥有相应权限, false表示没有相应权限
	 */
	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 + "没有登录或是没有被分配任何权限");
		}
		
		for (AuthorizationModel am : col) {
			// 输出当前实例,看看是否同一个实例对象
			System.out.println("am==" + am);
			if (am.getSecurityEntity().equals(securityEntity)
					&& am.getPermit().equals(permit)) {
				return true;
			}
		}
		
		return false;
	}
	
	/**
	 * 从数据库中获取某人所拥有的权限
	 * @param user 需要获取所拥有的权限的人员
	 * @return 某人所拥有的权限
	 */
	private Collection<AuthorizationModel> queryByUser(String user) {
		Collection<AuthorizationModel> col = 
			new ArrayList<AuthorizationModel>();
		
		for (String s : TestDB.colDB) {
			String ss[] = s.split(",");
			if (ss[0].equals(user)) {
				AuthorizationModel am = new AuthorizationModel();
				am.setUser(ss[0]);
				am.setSecurityEntity(ss[1]);
				am.setPermit(ss[2]);
				
				col.add(am);
			}
		}
		
		return col;
	}
}

\user\ClientUsual.java
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

package user;

import usual.SecurityMgr;

public class ClientUsual {
	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("f2==" + f2);
        for (int i = 0; i < 3; i++) {
        	mgr.login("张三" + i);
        	mgr.hasPermit("张三" + i, "薪资数据", "查看");
        }
	}
}

运行结果
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
am==usual.AuthorizationModel@c21495
am==usual.AuthorizationModel@1d5550d
am==usual.AuthorizationModel@c2ea3f
f1==false
f2==true
am==usual.AuthorizationModel@a0dcd9
am==usual.AuthorizationModel@186d4c1
am==usual.AuthorizationModel@f9f9d8

[存在的问题]
对象实例数目太多。
在系统当中,存在大量的细粒度对象,而且存在大量的重复数据,
严重耗费内存,如何解决?

[使用模式的实现]
\pattern\Flyweight.java
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

package pattern;

/**
 * 描述授权数据的享元接口
 */
public interface Flyweight {
	/**
	 * 判断传入的安全实体和授权,是否和享元对象内部状态匹配
	 * 
	 * @param securityEntity
	 *            安全实体
	 * @param permit
	 *            权限
	 * @return true表示匹配, false表示不匹配
	 */
	public boolean match(String securityEntity, String permit);
}

\pattern\AuthorizationFlyweight.java
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

package pattern;

/**
 * 封装授权数据中重复出现部分的享元对象
 */
public class AuthorizationFlyweight implements Flyweight {
	/**
	 * 内部状态,安全实体
	 */
	private String securityEntity;

	/**
	 * 内部状态,权限
	 */
	private String permit;

	/**
	 * 构造方法,传入状态数据
	 * 
	 * @param state
	 *            状态数据,包含安全实体和权限的数据,用","分隔
	 */
	public AuthorizationFlyweight(String state) {
		String ss[] = state.split(",");
		securityEntity = ss[0];
		permit = ss[1];
	}

	public String getSecurityEntity() {
		return securityEntity;
	}

	public String getPermit() {
		return permit;
	}

	@Override
	public boolean match(String securityEntity, String permit) {
		if (this.securityEntity.equals(securityEntity)
				&& this.permit.equalsIgnoreCase(permit)) {
			return true;
		}

		return false;
	}
}

\pattern\FlyweightFactory.java
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

package pattern;

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

/**
 * 享元工厂,通常实现成为单例
 */
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>();
    
    /**
     * 获取key对应的享元对象
     * @param key 获取享元对象的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;
    }
}

\pattern\TestDB.java
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

package pattern;

import java.util.ArrayList;
import java.util.Collection;

/**
 * 供测试用,在内存中模拟数据库中的值
 */
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 + ",人员列表,查看");
		}
	}
}

\pattern\SecurityMgr.java
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

package pattern;

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

/**
 * 安全管理,实现成单例
 */
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 被检测权限的用户
	 * @param securityEntity 安全实体
	 * @param permit 权限
	 * @return true表示拥有相应权限, false表示没有相应权限
	 */
	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 + "没有登录或是没有被分配任何权限");
		}
		
		for (Flyweight fm : col) {
			// 输出当前实例,看看是否同一个实例对象
			System.out.println("fm==" + fm);
			if (fm.match(securityEntity, permit)) {
				return true;
			}
		}
		
		return false;
	}
	
	/**
	 * 从数据库中获取某人所拥有的权限
	 * @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)) {
				Flyweight fs = FlyweightFactory.getInstance()
				.getFlyweight(ss[1] + "," + ss[2]);
				col.add(fs);
			}
		}
		
		return col;
	}
}

\user\ClientPattern.java
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

package user;

import pattern.SecurityMgr;

public class ClientPattern {
	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("f2==" + f2);
        for (int i = 0; i < 3; i++) {
        	mgr.login("张三" + i);
        	mgr.hasPermit("张三" + i, "薪资数据", "查看");
        }
	}
}


运行结果
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

fm==pattern.AuthorizationFlyweight@c2ea3f
fm==pattern.AuthorizationFlyweight@c2ea3f
fm==pattern.AuthorizationFlyweight@a0dcd9
f1==false
f2==true
fm==pattern.AuthorizationFlyweight@c2ea3f
fm==pattern.AuthorizationFlyweight@c2ea3f
fm==pattern.AuthorizationFlyweight@c2ea3f


@@@模式的实现:
享元模式设计的重点就在于分离变与不变。
把一个对象的状态分为不可变的内部状态和可变的外部状态。
然后通过共享不变的部分,达到减少对象数量并节约内存的目的。

@@@模式的优点:
减少对象数量,节省内存空间。

@@@模式的缺点:
维护共享对象,需要额外内存。

@@@模式的本质:
分离与共享。

@@@模式体现的设计原则:
NA