1. 模式概念
所谓享元模式就是以共享的方式支持大量细粒度对象的复用。
在了解享元模式之前我们先要了解两个概念:内部状态(Internal State)、外部状态(External State)。
- 内部状态:存储在享元对象内部,不随外界环境改变而改变的共享部分。
- 外部状态:随着环境的改变而改变,不能够共享的状态就是外部状态,享元对象的外部状态由客户端保存,在需要用到的时候再传入享元对象的内部。
内部状态存储于享元对象内部,而外部状态则应该由客户端来考虑。
2. 单纯享元模式:
角色如下:
- 抽象享元角色(Flyweight):规定所有具体享元角色需要实现的方法
- 具体享元角色(ConcreteFlyweight): 实现抽象享元中的方法,为内部状态提供存储空间
- 享元工厂角色(FlyweightFactory): 负责创建和管理享元对象,保证享元对象被系统共享。
代码实现:
public interface Flyweight {
//name 表示外部状态
public void operation(String name);
}
public class ConcreteFlyweight implements Flyweight {
//内部状态
private String intrinsicState;
public ConcreteFlyweight(String intrinsicState) {
this.intrinsicState = intrinsicState;
}
@Override
public void operation(String name) {
System.out.println("内部状态:" + intrinsicState);
System.out.println("外部状态:" + name);
}
}
/**
通过工厂来生成实例,传入内部状态生成不同的实例
缓存的对象:key-内部状态, value-具体享元对象
*/
public class FlyweightFactory {
private static HashMap<String, Flyweight> files = new HashMap<>();
public static Flyweight getInstance(String intrinsicState) {
Flyweight value = files.get(intrinsicState);
if(value == null) {
value = new ConcreteFlyweight(intrinsicState);
files.put(intrinsicState, value);
}
return value;
}
public static int getSize() {
return files.size();
}
}
//客户端程序
public class Client {
public static void main(String[] args) {
Flyweight fly1 = FlyweightFactory.getInstance("土豆");
fly1.operation("我");
Flyweight fly2 = FlyweightFactory.getInstance("辣椒");
fly2.operation("他");
Flyweight fly3 = FlyweightFactory.getInstance("土豆");
fly3.operation("你");
System.out.println(fly1 == fly3);
System.out.println(FlyweightFactory.getSize());
}
}
3. 复合享元
在复合享元模式中,将一些单纯享元对象复合,形成复合享元对象,这些复合享元对象是不可以共享的,但是它们可以分解成单纯享元对象,这些单纯享元对象可以共享。
源代码:
public interface Flyweight {
/**
* @param name 表示外部状态
*/
public void operation(String name);
}
//共享
public class ConcreteFlyweight implements Flyweight {
//内部状态
private String intrinsicState;
public ConcreteFlyweight(String intrinsicState) {
this.intrinsicState = intrinsicState;
}
@Override
public void operation(String name) {
System.out.println("点菜者:" + name);
System.out.println("菜名:" + intrinsicState);
}
}
//不共享
public class ConcreteCompositeFlyweight implements Flyweight {
private HashMap<String, Flyweight> map = new HashMap<>();
//添加单纯享元对象
public void add(String state, Flyweight fly) {
map.put(state, fly);
}
@Override
public void operation(String name) {
Flyweight temp = null;
for(String key : map.keySet()) {
temp = map.get(key);
temp.operation(name);
}
}
}
//生产实例
public class FlyweightFactory {
private static HashMap<String, Flyweight> files = new HashMap<>();
public static Flyweight getInstance(String state) {
Flyweight fly = files.get(state);
if(fly == null) {
fly = new ConcreteFlyweight(state);
files.put(state, fly);
}
return fly;
}
public static Flyweight getInstance(List<String> states) {
ConcreteCompositeFlyweight compositeFlyweight = new ConcreteCompositeFlyweight();
for(String state : states) {
compositeFlyweight.add(state, getInstance(state));
}
return compositeFlyweight;
}
}
public class Client {
public static void main(String[] args) {
List<String> compositeState = new ArrayList<String>();
compositeState.add("辣椒炒肉");
compositeState.add("牛肉");
compositeState.add("鸡肉");
compositeState.add("辣椒炒肉");
compositeState.add("牛肉");
Flyweight compositeFly1 = FlyweightFactory.getInstance(compositeState);
Flyweight compositeFly2 = FlyweightFactory.getInstance(compositeState);
compositeFly1.operation("我");//外蕴状态是同一个
System.out.println();
compositeFly2.operation("你");
System.out.println("---------------------------------");
System.out.println("复合享元模式是否可以共享对象:" + (compositeFly1 == compositeFly2));
String state = "牛肉";
Flyweight fly1 = FlyweightFactory.getInstance(state);
Flyweight fly2 = FlyweightFactory.getInstance(state);
System.out.println("单纯享元模式是否可以共享对象:" + (fly1 == fly2));
}
}
4. 研磨设计模式
问题描述:在系统当中,存在大量的细粒度对象,而且存在大量的重复数据,严重耗费内存,如何解决? 问题解答:享元模式
/***
* 描述授权数据的享元接口
*/
public interface Flyweight {
/**
* 判断传入的安全实体和权限,是否和享元对象内部状态匹配
* @param securityEntity 安全实体
* @param permit 权限
* @return true表示匹配,false表示不匹配
*/
public boolean match(String securityEntity,String permit);
/**
* 为flyweight添加子flyweight对象
* @param f 被添加的子flyweight对象
*/
public void add(Flyweight f);
}
/**
* 封装授权数据中重复出现部分的享元对象
*/
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;
}
public boolean match(String securityEntity, String permit) {
if(this.securityEntity.equals(securityEntity)
&& this.permit.equals(permit)){
return true;
}
return false;
}
public void add(Flyweight f) {
throw new UnsupportedOperationException("对象不支持这个功能");
}
}
/**
* 不需要共享的享元对象的实现,也是组合模式中的组合对象
*/
public class UnsharedConcreteFlyweight implements Flyweight{
/**
* 记录每个组合对象所包含的子组件
*/
private List<Flyweight> list = new ArrayList<Flyweight>();
public void add(Flyweight f) {
list.add(f);
}
public boolean match(String securityEntity, String permit) {
for(Flyweight f : list){
//递归调用
if(f.match(securityEntity, permit)){
return true;
}
}
return false;
}
}
public class TestDB {
/**
* 用来存放单独授权数据的值
*/
public static Collection<String> colDB = new ArrayList<String>();
/**
* 用来存放组合授权数据的值,key为组合数据的id,value为该组合包含的多条授权数据的值
*/
public static Map<String,String[]> mapDB = new HashMap<String,String[]>();
static{
//通过静态块来填充模拟的数据,增加一个标识来表明是否组合授权数据
colDB.add("张三,人员列表,查看,1");
colDB.add("李四,人员列表,查看,1");
colDB.add("李四,操作薪资数据,,2");
mapDB.put("操作薪资数据",new String[]{"薪资数据,查看","薪资数据,修改"});
//增加更多的授权数据
for(int i=0;i<3;i++){
colDB.add("张三"+i+",人员列表,查看,1");
}
}
}
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 key对应的享元对象
*/
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 被检测权限的用户
* @param securityEntity 安全实体
* @param permit 权限
* @return true表示拥有相应权限,false表示没有相应权限
*/
public boolean hasPermit(String user,String securityEntity,String permit){
Collection<Flyweight> col = map.get(user);
System.out.println("现在测试"+securityEntity+"的"+permit+"权限,map.size="+map.size());
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;
}
/**
* 从数据库中获取某人所拥有的权限
* @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 fm = null;
if(ss[3].equals("2")){
//表示是组合
fm = new UnsharedConcreteFlyweight();
//获取需要组合的数据
String tempSs[] = TestDB.mapDB.get(ss[1]);
for(String tempS : tempSs){
Flyweight tempFm = FlyweightFactory.getInstance().getFlyweight(tempS);
//把这个对象加入到组合对象中
fm.add(tempFm);
}
}else{
fm = FlyweightFactory.getInstance().getFlyweight(ss[1]+","+ss[2]);
}
col.add(fm);
}
}
return col;
}
}
public class Client {
public static void main(String[] args) throws Exception{
//需要先登录,然后再判断是否有权限
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,"薪资数据","查看");
}
}
}
5. 总结
1、享元模式可以极大地减少系统中对象的数量。
2、享元模式的核心在于享元工厂,它主要用来确保合理地共享享元对象。
3、内部状态为不变部分,存储于享元对象内部,而外部状态是可变部分,它应当由客户端来负责。