设计模式——多例模式(23种之外)
多例模式(Multiton Pattern)是一种特殊的设计模式,它属于创建型模式。与单例模式(Singleton Pattern)相比,多例模式允许一个类有多个实例,但是实例的数量是有限制的,并且这些实例在全局范围内是共享的。这种模式适用于当系统中有且仅有几个对象实例被频繁使用,且这些对象的创建和销毁开销较大时。
在我的SpringBoot项目中遇到的一个问题,最后使用多例模式解决了。问题是我需要通过传入参数实例化一个对象,希望如果出入的参数相同那么得到的实例应该是一样的,如果参数不同则实例化的参数是不同的。这个问题我一开始想到了单例模式和工厂模式结合来解决,在工厂中判断参数是否已经存在从而创建单例实例,后面越想越觉得自己搞复杂了,虽然可以解决这个问题,最后果然发现还有一种多例模式可以完美解决这个问题。那么,下面就开始演示如何使用多例模式:
需要实例化的User类
public class User { String id; public User(String id) { this.id = id; } public String getId() { return id; } public void setId(String id) { this.id = id; } @Override public String toString() { return "User{" + "id='" + id + '\'' + '}'; } }
直接获取对象的缺点
我们正常的实例化其实是不满足条件的,例如如下代码:
public class MainTest { public static void main(String[] args) { User user1 = new User("123"); User user2 = new User("123"); System.out.println(user1.hashCode()); System.out.println(user2.hashCode()); System.out.println(user1 == user2); } }
输出结果
128893786
1732398722
false
显然,这样new出来的两个对象不是同一个,但是在这种情况下,我们希望通过参数构造的对象只要参数相同就能拿到同一个对象,类似单例模式,而不同参数则创建新的对象。这种情况就非常适合多例模式,下面介绍多例模式解决这个问题。
多例模式
多例模式的特点
- 实例数量有限:与单例模式不同,多例模式允许创建多个实例,但实例的数量是有限的。
- 全局访问:所有实例都是全局可访问的,通常通过一个全局的访问点来获取实例。
- 实例唯一性:在允许的范围内,每个实例都是唯一的。
下面是线程安全的实现,这里我们使用一个工厂类来管理我们的User,只要是相同的id就一定能够获得相同的User,不同的id拿到的User不同。代码如下:
public class UserMultitonFactory { private static final ConcurrentHashMap userMap = new ConcurrentHashMap(); private UserMultitonFactory(){} // 私有构造方法防止new实例化 public static User getInstance(String id) { // 使用computeIfAbsent方法确保线程安全的实例创建 return userMap.computeIfAbsent(id,k -> new User(k)); // 这个k就是id } public static void destroyInstance(String id){ userMap.remove(id); } }
测试
public class MainTest { public static void main(String[] args) { User user1 = UserMultitonFactory.getInstance("123"); User user2 = UserMultitonFactory.getInstance("123"); User user3 = UserMultitonFactory.getInstance("001"); System.out.println(user1.hashCode()); System.out.println(user2.hashCode()); System.out.println(user3.hashCode()); } }
结果
1108411398
1108411398
1394438858
这里我们是使用一个工厂类来管理User的创建,当然也可以让User自己成为一个多例模式类,代码如下:
public class UserMultiton { String id; private static final ConcurrentHashMap userMap = new ConcurrentHashMap(); private UserMultiton(String id) { // 禁止外部创建该类 this.id = id; } public static UserMultiton getInstance(String id){ // 使用computeIfAbsent方法确保线程安全的实例创建 return userMap.computeIfAbsent(id,k -> new UserMultiton(k)); // 这个k就是id } public static void destroyInstance(String id){ userMap.remove(id); } public String getId() { return id; } public void setId(String id) { this.id = id; } @Override public String toString() { return "User{" + "id='" + id + '\'' + '}'; } }
应用场景
多例模式是一种创建型设计模式,其应用场景主要在于管理可重复使用的资源,如线程池、数据库连接池等。这些场景中,多例模式能够复用已有实例,避免重复创建对象,从而提高系统性能并避免浪费系统资源。
缺点
多例模式的缺点包括:
- 难以扩展:多例模式的实例数量是固定的,难以动态地增加或减少实例数量。
- 难以测试:由于多例模式的实例数量是固定的,难以对每个实例进行单独的测试。
- 破坏封装性:多例模式需要全局访问实例,这破坏了封装性,使得代码难以维护和扩展。
- 代码复杂度高:多例模式的实现需要考虑线程安全、序列化等问题,因此代码复杂度较高。