1、描述
以共享的方法高效地支持大量细粒度对象的复用。在 Java 中,通过提前初始化对象或者首次使用后记录对象,后续使用就可以复用对象来实现享元模式。类似缓存技术。
2、模式结构
-
享元对象:可复用对象。
-
享元工厂类:享元对象的工厂类,负责创建、储存享元对象。客户端从工厂类请求对象有则返回,没有则创建
一个放入工厂类。例如 String 类的缓存池和数据库的连接池。
3、实现逻辑
享元模式实现的关键是需要区分对象的内蕴状态和外蕴状态。简单点解释就是,内蕴状态就是可被共享的部分;外蕴状态就是不可共享的部分,需要客户端提供的部分。Java 中实现享元模式,就是把内蕴部分剥离出来静态化,客户端调用时提供外蕴状态(当然对象可以没有外蕴部分)。
4、实战代码
RBAC 模型基于角色的权限控制。通过角色关联用户,角色关联权限的方式间接赋予用户权限。
我们知道对于用户来讲,每个用户都有自己的 编号、姓名,但是会存在多个用户都是同一个角色。在这里编号、姓名就属于外蕴状态,而角色就属于内蕴状态。
/** * 享元对象 * * @author Eajur.Wen * @version 1.0 * @date 2022-11-15 19:01:20 */ @Data public class Role { private String name; private List<String> permissions; } /** * 业务对象 * * @author Eajur.Wen * @version 1.0 * @date 2022-11-15 19:00:57 */ @Data @NoArgsConstructor @AllArgsConstructor public class Member { private Long id; private String name; private Role role; } /** * 享元工厂类 * 这里结合静态内部类单例模式实现 RoleFactory * * @author Eajur.Wen * @version 1.0 * @date 2022-11-15 19:04:47 */ public class RoleFactory { private static Map<String, Role> roleMap = new HashMap<>(); public RoleFactory() { Role admin = new Role(); admin.setName("admin"); admin.setPermissions(List.of("add", "update", "select", "delete")); Role user = new Role(); user.setName("user"); user.setPermissions(List.of("select")); roleMap.put("admin", admin); roleMap.put("user", user); } public Role getRole(String name) { return roleMap.get(name); } public static final RoleFactory getInstance() { return SingletonHolder.INSTANCE; } private static class SingletonHolder { private static final RoleFactory INSTANCE = new RoleFactory(); } } /** * 测试类 * * @author Eajur.Wen * @version 1.0 * @date 2022-11-15 19:13:48 */ public class Client { public static void main(String[] args) { // 创建 10 个 Admin 用户 for (int i = 0; i < 10; i++) { Member member = new Member((long) i, "admin" + i, RoleFactory.getInstance().getRole("admin")); System.out.println(member); } System.out.println("------------------"); // 创建 100 个 User 用户 for (int i = 0; i < 100; i++) { Member member = new Member((long) i, "user" + i, RoleFactory.getInstance().getRole("user")); System.out.println(member); } } }
这样我们通过提前创建 role 对象,使得频繁创建 member 对象时复用 role 对象,减少了 role 对象的频繁创建与销毁,大大节约了内存占用。
5、适用场景
相同对象或者相似对象需要频繁创建时,适合使用享元模式。
6、享元模式与单例模式的区别
单例模式的目的是为了确保一个类只存在一个对象,需要自行实例化并提供全局访问方法。
享元模式的目的是为了对对象内蕴部分的复用,无需保证一个类只存在一个对象。