享元模式——分离和共享的思路体现

  享元模式的整体思路就是分离和共享,有些地方直接将享元模式理解成了缓存,其实缓存是享元模式的其中一种应用而已,其并不能完全代替享元模式的思想:享元模式是为了解决大量细粒度的对象占用内存而设计的,将大量细粒度的对象进行共享使用,这样能够在很大的程度上解决内存的问题,但是缓存其实是为了提高访问速度的一种空间换时间的方法,它并不强调内存占用少,事实上很多的缓存设计还增大了内存的占用;但是他们的实现方式上都会有一个缓存的位置,不管是享元的缓存Map还是缓存的服务器,都可以理解成一个放置共享对象的map。

享元模式的结构图和类关系

享元模式原始结构图

  • Flyweight : 享元的接口,是享元的顶层API实现
  • SharedFlyweight : 共享的享元实现类
  • UnsharedFlyweight : 非共享的享元实现类,并不是所有的享元对象都是需要共享的,这里可以将非共享的抽离出来
  • FlyweightFactory : 享元工厂,享元工厂是缓存享元类的实现类,里面缓存和新建了共享的享元对象,当客户端需要享元对象的时候,可以直接去工厂中获取。这个类一版会设计成单例的。

享元模式应用——权限校验示例

  在一般的权限校验中,我们可以将其抽象成【人员】对【安全实体】拥有【权限】,比如下面的权限逻辑:

张三 对 文件列表 有 查看的权限
李四 对 文件列表 有修改的权限

这个时候,【张三】和【李四】就是人员,【文件列表】是安全实体,【查看】和【修改】就是容许的权限

  在实际的应用中,人员会非常的多,但是安全实体和权限是可以枚举的,如果我们对每一个人拥有的权限和安全实体都生成一个对象,这个对象就会非常的多,所以,我们可以利用享元模式,将安全实体和权限做一个共享,这样的话,既能够提高匹配效率,也能减少内存的消耗。

  • 示例代码:
  1. 新建一个接口AuthFlyweight,里面新增一个match方法,用来匹配是否对某个安全实体有某个权限
/**
 * 鉴权的享元对象
 */
public interface AuthFlyweight {
    /**
     * 安全实体和对象是否匹配
     *
     * @param securityEntity :安全实体
     * @param permit :权限
     * @return :
     */
    boolean match(String securityEntity, String permit);
}
  1. 新建一个享元的具体实现类SecurityFlyweight,实现享元接口
/**
 * 具体的安全享元类
 */
public class SecurityFlyweight implements AuthFlyweight {

    private String securityEntity;

    private String permit;

    public SecurityFlyweight(String securityEntity, String permit) {
        this.securityEntity = securityEntity;
        this.permit = permit;
    }

    @Override
    public boolean match(String securityEntity, String permit) {
        return null != this.securityEntity && null != this.permit && this.securityEntity.equals(securityEntity)
            && this.permit.equals(permit);
    }

    @Override
    public String toString() {
        return "SecurityFlyweight{" + "securityEntity='" + securityEntity + '\'' + ", permit='" + permit + '\'' + '}';
    }
}
  1. 新建单例享元工厂AuthFlyweightFactory,在这个工厂中,缓存了用过的享元对象,如果需要的是已经缓存的 ,就不需要再新建,直接从缓存中获取
/**
 * 享元工厂,一般是单例
 */
public class AuthFlyweightFactory {
    /**
     * 享元key格式化样式
     */
    private static final String FLYWEIGHT_KEY_PATTERN = "%s_%s";

    private Map<String, AuthFlyweight> fsMap = new HashMap<>();

    private static AuthFlyweightFactory instance = new AuthFlyweightFactory();

    private AuthFlyweightFactory() {
    }

    public static AuthFlyweightFactory getInstance() {
        return instance;
    }

    public AuthFlyweight getFlyweight(String securityEntity, String permit) {
        String key = String.format(FLYWEIGHT_KEY_PATTERN, securityEntity, permit);
        AuthFlyweight authFlyweight = fsMap.get(key);
        if (null == authFlyweight) {
            authFlyweight = new SecurityFlyweight(securityEntity, permit);
            fsMap.put(key, authFlyweight);
        }
        return authFlyweight;
    }

    public void showAuthFlyweight() {
        for (AuthFlyweight value : fsMap.values()) {
            System.out.println(value);
        }
    }

}
  1. 为了示例,我们还需要新建一个权限管理的类,用来给某个人分配权限,这个类也可以设计成单例的,在这个类中,我们模拟数据库给莫个人缓存了权限的列表
/**
 * 权限管理类,主要管理人员对应的权限
 */
public class SecurityManager {

    private static SecurityManager instance = new SecurityManager();

    private SecurityManager() {
    }

    public static SecurityManager getInstance() {
        return instance;
    }

    private static Map<String, Collection<AuthFlyweight>> userSecurityMap = new HashMap<>();

    /**
     * 验证是否有权限
     *
     * @param user :
     * @param securityEntity :
     * @param permit :
     * @return :
     */
    public boolean hasPermit(String user, String securityEntity, String permit) {
        Collection<AuthFlyweight> authFlyweights = userSecurityMap.get(user);
        if (null == authFlyweights || authFlyweights.size() == 0) {
            return false;
        }
        for (AuthFlyweight flyweight : authFlyweights) {
            if (flyweight.match(securityEntity, permit)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 增加权限
     *
     * @param user :
     * @param securityEntity :
     * @param permit :
     */
    public void addAuth(String user, String securityEntity, String permit) {
        // 查看是否已经有权限
        boolean hasPermit = hasPermit(user, securityEntity, permit);
        if (hasPermit) {
            return;
        }
        Collection<AuthFlyweight> authFlyweights = userSecurityMap.get(user);
        if (null == authFlyweights) {
            authFlyweights = new ArrayList<>();
        }
        // 从享元工厂中获取对象,进行缓存
        AuthFlyweight authFlyweight = AuthFlyweightFactory.getInstance().getFlyweight(securityEntity, permit);
        authFlyweights.add(authFlyweight);
        userSecurityMap.put(user, authFlyweights);
    }

    /**
     * 删除权限
     *
     * @param user :
     * @param securityEntity :
     * @param permit :
     */
    public void deleteAuth(String user, String securityEntity, String permit) {
        // 查看是否已经有权限
        boolean hasPermit = hasPermit(user, securityEntity, permit);
        if (!hasPermit) {
            return;
        }
        Collection<AuthFlyweight> authFlyweights = userSecurityMap.get(user);
        if (null == authFlyweights || authFlyweights.size() == 0) {
            return;
        }
        authFlyweights.removeIf(authFlyweight -> authFlyweight.match(securityEntity, permit));
    }
}
  1. 编写一个测试类,在这个测试类中,我们先预制一些权限,然后新增和删除操作,最后打印出享元工厂中缓存的对象
public class Client {


    @Test
    public void testAuthFlyweight() {
        //预制一些权限数据
        SecurityManager.getInstance().addAuth("张三", "文件列表", "查看");
        SecurityManager.getInstance().addAuth("李四", "文件列表", "查看");
        System.out.print("预制的【张三】对【文件列表】的【查看】权限: ");
        System.out.println(SecurityManager.getInstance().hasPermit("张三", "文件列表", "查看"));
        System.out.println("----------------------------------------------------------------------------");
        System.out.print("预制的【李四】对【文件列表】的【查看】权限: ");
        System.out.println(SecurityManager.getInstance().hasPermit("李四", "文件列表", "查看"));
        System.out.println("----------------------------------------------------------------------------");
        System.out.print("预制的【张三】对【文件列表】的【修改】权限: ");
        System.out.println(SecurityManager.getInstance().hasPermit("张三", "文件列表", "修改"));
        System.out.println("----------------------------------------------------------------------------");
        // 增加【文件列表】的【修改】权限
        SecurityManager.getInstance().addAuth("张三", "文件列表", "修改");
        System.out.print("增加的【张三】对【文件列表】的【修改】权限: ");
        System.out.println(SecurityManager.getInstance().hasPermit("张三", "文件列表", "修改"));
        System.out.println("----------------------------------------------------------------------------");
        SecurityManager.getInstance().addAuth("李四", "文件列表", "修改");
        System.out.print("增加的【李四】对【文件列表】的【修改】权限: ");
        System.out.println(SecurityManager.getInstance().hasPermit("李四", "文件列表", "修改"));
        System.out.println("----------------------------------------------------------------------------");
        // 删除【张三】对【文件列表】的【修改】权限
        SecurityManager.getInstance().deleteAuth("张三", "文件列表", "修改");
        System.out.print("增加的【张三】对【文件列表】的【修改】权限: ");
        System.out.println(SecurityManager.getInstance().hasPermit("张三", "文件列表", "修改"));
        System.out.println("----------------------------------------------------------------------------");
        System.out.println("最后的共享的享元对象: ");
        AuthFlyweightFactory.getInstance().showAuthFlyweight();

    }

}
  1. 执行结果如下:
预制的【张三】对【文件列表】的【查看】权限: true
----------------------------------------------------------------------------
预制的【李四】对【文件列表】的【查看】权限: true
----------------------------------------------------------------------------
预制的【张三】对【文件列表】的【修改】权限: false
----------------------------------------------------------------------------
增加的【张三】对【文件列表】的【修改】权限: true
----------------------------------------------------------------------------
增加的【李四】对【文件列表】的【修改】权限: true
----------------------------------------------------------------------------
增加的【张三】对【文件列表】的【修改】权限: false
----------------------------------------------------------------------------
最后的共享的享元对象: 
SecurityFlyweight{securityEntity='文件列表', permit='修改'}
SecurityFlyweight{securityEntity='文件列表', permit='查看'}

从结果我们可以看出,虽然我们对张三和李四都新增过对文件列表的修改权限,但是享元工厂打印出来的只有两个享元实体对象,因为这些对象是公用的,在后面的逻辑中,无论新增了多少人,只要安全实体和权限没有变化,系统的存储就不会出现巨大的新增。

  • 示例类图如下

享元模式示例图


后记
  个人总结,欢迎转载、评论、批评指正

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值