设计模式-多例模式

发布时间:2023年12月29日
设计模式专栏


模式介绍

多例模式是一种创建型设计模式,属于对象创建类型。多例模式的特点是允许一个类有多个实例,并且这些实例都是该类本身。多例模式通常用于设计复杂的系统,例如数据库连接、网络通信等。

多例模式的优点包括:

  1. 可以限制实例数量:多例模式可以限制实例数量,确保系统中只有指定数量的实例存在,避免过多的资源消耗。
  2. 可以复用已有实例:多例模式可以复用已有实例,避免重复创建对象,提高系统性能。
  3. 可以灵活控制实例的生命周期:多例模式可以灵活控制实例的生命周期,可以在需要时创建实例,也可以在不需要时销毁实例,避免浪费系统资源。

多例模式的缺点包括:

  1. 难以扩展:多例模式的实例数量是固定的,难以动态地增加或减少实例数量。
  2. 难以测试:由于多例模式的实例数量是固定的,难以对每个实例进行单独的测试。
  3. 破坏封装性:多例模式需要全局访问实例,这破坏了封装性,使得代码难以维护和扩展。
  4. 代码复杂度高:多例模式的实现需要考虑线程安全、序列化等问题,因此代码复杂度较高。

多例模式可以通过实现一个内部类来管理实例的创建和销毁,这个内部类通常被称为“工厂”或“容器”。多例模式有多种实现方式,包括有上限多例模式和无上限多例模式等。在实际应用中,需要根据具体需求选择适合的实现方式。

在这里插入图片描述

多例模式和单例模式的区别

多例模式和单例模式都属于对象创建类型的模式,但它们在实现和应用上有明显的区别。

首先,单例模式要求一个类只有一个实例,并提供一个全局访问点。这意味着无论在程序中的哪个位置访问该类,都将获得同一个实例。单例模式通常用于需要频繁创建和销毁对象的场景,如线程池、缓存等。单例模式的优点包括提供对唯一实例的受控访问、节约系统资源、提高性能等。缺点是扩展困难、职责过重等。

多例模式则允许一个类有多个实例。多例模式的实现通常需要借助静态工厂方法来向外界提供循环使用的实例。多例模式的应用场景包括管理可重复使用的资源,如数据库连接池、线程池等。多例模式的优点是可以复用已有实例、避免重复创建对象、提高系统性能和资源利用率等。缺点是难以扩展、难以测试、破坏封装性、代码复杂度高。

综上所述,多例模式和单例模式在实现和应用上有明显的区别。单例模式强调唯一性,适用于需要频繁创建和销毁对象的场景;而多例模式则允许有多个实例,适用于管理可重复使用的资源。在实际应用中,需要根据具体需求选择适合的模式。

在这里插入图片描述

应用场景

多例模式是一种创建型设计模式,其应用场景主要在于管理可重复使用的资源,如线程池、数据库连接池等。这些场景中,多例模式能够复用已有实例,避免重复创建对象,从而提高系统性能并避免浪费系统资源。

具体来说,多例模式的应用场景包括:

  1. 数据库连接 :在数据库应用中,通常需要为每个客户端请求创建一个数据库连接。然而,创建新的数据库连接是一项开销较大的操作,而且如果每个请求都创建新的连接,会很快耗尽系统的资源。因此,我们通常会使用多例模式来管理数据库连接。创建一个数据库连接池,该连接池可以预先创建一定数量的数据库连接,并保存在连接池中。当有新的请求需要连接数据库时,系统会先从连接池中获取一个空闲的连接,如果连接池中没有空闲的连接,则等待一段时间或者创建新的连接。当请求结束后,该连接会被放回连接池中,以供其他请求使用。通过这种方式,我们可以重复利用已有的连接,避免频繁创建和销毁连接,提高了系统的性能和稳定性。
  2. EJB无状态会话Bean的实例池 :EJB无状态会话Bean是一种特殊的会话Bean,它不保存客户端的状态。由于无状态会话Bean不需要保存客户端的状态,因此可以重复使用同一个实例来处理多个客户端请求。为了实现这个目标,我们可以使用多例模式来管理无状态会话Bean的实例。创建一个实例池,该池中包含了多个无状态会话Bean的实例。当客户端请求到来时,从实例池中获取一个空闲的实例,处理请求后将该实例放回实例池中,以供其他请求使用。通过这种方式,我们可以复用无状态会话Bean的实例,提高系统的性能和并发处理能力。

总的来说,多例模式的应用场景主要涉及可重复使用的资源管理,通过复用已有实例来提高系统性能和资源利用率。在实际应用中,需要根据具体需求选择适合的实现方式。

在这里插入图片描述

Spring中多例模式的优缺点

在Spring框架中,多例模式(prototype scope)是一种常用的依赖注入模式。以下是多例模式的优缺点:

优点:

  1. 性能更好:每次请求都会创建一个新的Bean实例,避免了单例模式中可能存在的线程安全问题,也避免了共享资源带来的开销。
  2. 降低了耦合度:由于每次请求都有独立的实例,因此降低了Bean之间的耦合度。
  3. 支持原型作用域的Bean的生命周期:在Spring中,使用原型作用域的Bean会在每次请求时重新创建,这使得我们可以利用Spring的AOP(面向切面编程)功能来对Bean进行各种增强操作,例如事务管理、日志记录等。

缺点:

  1. 资源消耗较多:由于每次请求都需要创建一个新的Bean实例,因此如果Bean的创建和销毁开销较大,或者应用的请求量非常大,那么可能会导致资源消耗过多。
  2. 不适合需要跨请求保持状态的Bean:由于多例模式为每个请求创建一个新的实例,因此如果Bean需要在跨请求之间保持状态,那么使用多例模式就不合适了。

多例模式适用于那些需要独立实例的Bean,尤其是那些创建和销毁开销较小、状态不需要跨请求保持的Bean。在选择使用单例模式还是多例模式时,需要根据具体的应用场景和需求来决定。

在这里插入图片描述

代码示例

Java实现多例模式

Java实现多例模式可以使用以下步骤:

  1. 创建一个类,该类将包含一个静态的内部类,用于管理实例的创建和销毁。
  2. 在静态内部类中,使用一个静态的Map来保存实例。Map的键是类名,值是该类的实例。
  3. 在静态内部类中,提供一个静态的工厂方法,用于创建实例。该方法首先检查Map中是否已经存在该类的实例,如果存在则直接返回该实例,否则创建一个新的实例并将其保存到Map中。
  4. 在静态内部类中,提供一个静态的销毁方法,用于销毁实例。该方法从Map中移除该类的实例。
  5. 在外部类中,提供一个公共的静态变量,用于访问内部类的实例。

以下是一个示例代码:

public class MultiInstanceExample {
    private static class InstanceContainer {
        private static final Map<String, MultiInstanceExample> instances = new HashMap<>();
        private static final AtomicInteger counter = new AtomicInteger();
        
        public static MultiInstanceExample getInstance() {
            String className = MultiInstanceExample.class.getName();
            MultiInstanceExample instance = instances.get(className);
            if (instance == null) {
                instance = new MultiInstanceExample();
                instances.put(className, instance);
            }
            return instance;
        }
        
        public static void destroyInstance(MultiInstanceExample instance) {
            String className = instance.getClass().getName();
            instances.remove(className);
        }
    }
    
    private MultiInstanceExample() {}
    
    public static MultiInstanceExample getInstance() {
        return InstanceContainer.getInstance();
    }
    
    public static void destroyInstance(MultiInstanceExample instance) {
        InstanceContainer.destroyInstance(instance);
    }
}

在上面的代码中,MultiInstanceExample类是一个多例模式的实现。InstanceContainer是一个静态内部类,用于管理实例的创建和销毁。getInstance()方法返回当前类的唯一实例,如果该实例不存在则创建它。destroyInstance()方法从Map中移除该实例。最后,通过调用MultiInstanceExample.getInstance()来获取当前类的唯一实例。

Python实现多例模式

Python实现多例模式可以使用装饰器或者元类的方式。

以下是使用装饰器实现多例模式的示例代码:

def multi_instance(cls):
    instances = {}
    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return get_instance

@multi_instance
class MyClass:
    def __init__(self, name):
        self.name = name
    
    def say_hello(self):
        print(f"Hello, {self.name}!")

在上面的代码中,multi_instance是一个装饰器函数,它接受一个类作为参数,并返回一个获取类实例的函数。在这个函数中,我们使用一个字典instances来保存类的实例。当获取实例时,我们首先检查该实例是否已经在instances中,如果是则直接返回,否则创建一个新的实例并将其保存到instances中。

接下来,我们使用@multi_instance装饰器来修饰MyClass类。这样,每次调用MyClass()时,都会返回同一个实例。

以下是使用元类实现多例模式的示例代码:

class MultiInstanceMeta(type):
    _instances = {}
    
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]

class MyClass(metaclass=MultiInstanceMeta):
    def __init__(self, name):
        self.name = name
    
    def say_hello(self):
        print(f"Hello, {self.name}!")

在上面的代码中,我们定义了一个元类MultiInstanceMeta,它继承自type类。在元类的__call__方法中,我们同样使用一个字典_instances来保存类的实例。当调用类时,我们首先检查该实例是否已经在_instances中,如果是则直接返回,否则创建一个新的实例并将其保存到_instances中。最后,我们在MyClass类的定义中指定了元类为MultiInstanceMeta。这样,每次调用MyClass()时,都会返回同一个实例。

在这里插入图片描述

多例模式在spring中的应用

在Spring框架中,多例模式(prototype scope)是一种常用的依赖注入模式。与单例模式(singleton scope)不同,多例模式为每个请求创建一个新的Bean实例。

在Spring中,可以通过在Bean上使用@Scope("prototype")注解或者在XML配置文件中设置prototype作用域来实现多例模式。

多例模式适用于那些需要独立实例的Bean,例如:

  1. 每次请求都需要一个新的实例。
  2. Bean的状态不需要跨请求保持。
  3. Bean的创建和销毁开销较小。

多例模式可以提高应用的性能和资源利用率,因为它可以避免对单例的过度依赖和线程安全问题。但是,如果Bean的创建和销毁开销较大,或者Bean的状态需要在跨请求之间保持,那么使用单例模式可能更为合适。

在这里插入图片描述

文章来源:https://blog.csdn.net/zhangzehai2234/article/details/135280631
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。