93°

2.设计模式之一:单例模式【创建型模式】

创建型模式分为以下几种。

  • 单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。
  • 原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。
  • 工厂方法(FactoryMethod)模式:定义一个用于创建产品的接口,由子类决定生产什么产品。
  • 抽象工厂(AbstractFactory)模式:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。
  • 建造者(Builder)模式:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。

以上 5 种创建型模式,除了工厂方法模式属于类创建型模式,其他的全部属于对象创建型模式

定义与特点

单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。

例如,Windows 中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容的不一致等错误。

在计算机系统中,还有 Windows 的回收站、操作系统中的文件系统、多线程中的线程池、显卡的驱动程序对象、打印机的后台处理服务、应用程序的日志对象、数据库的连接池、网站的计数器、Web 应用的配置对象、应用程序中的对话框、系统中的缓存等常常被设计成单例。

单例模式有 3 个特点:

  1. 单例类只有一个实例对象;
  2. 该单例对象必须由单例类自行创建;
  3. 单例类对外提供一个访问该单例的全局访问点;

结构与实现

单例模式是设计模式中最简单的模式之一。通常,普通类的构造函数是公有的,外部类可以通过“new 构造函数()”来生成多个实例。

但是,如果将类的构造函数设为私有的,外部类就无法调用该构造函数,也就无法生成多个实例。

这时该类自身必须定义一个静态私有实例,并向外提供一个静态的公有函数用于创建或获取该静态私有实例。

单例模式的结构

单例模式的实现

Singleton 模式通常有两种实现形式。

懒汉式单例

该模式的特点是类加载时没有生成单例,只有当第一次调用 get_instance 方法时才去创建这个单例。

class LazySingleton(object):
    __instance = None
def __init__(self):
    """ Virtually private constructor. """
    pass

@staticmethod
def get_instance():
    """ Static access method. """
    if LazySingleton.__instance == None:
        LazySingleton.__instance = LazySingleton()
    return LazySingleton.__instance

s1 = LazySingleton() s2 = LazySingleton() print(s1._LazySingleton__instance, s2._LazySingleton__instance, end='\n') # 未调用get_instance方法时未创建 print(s1.get_instance(), s2.get_instance(), end='\n') # 指向同一对象

None None
<__main__.LazySingleton object at 0x0000020A56899FD0> <__main__.LazySingleton object at 0x0000020A56899FD0>

注意:如果编写的是多线程程序,注意存在线程非安全的问题。如果保证线程安全,那么每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点。

饿汉式单例

该模式的特点是类一旦加载就创建一个单例,保证在调用 get_instance 方法之前单例已经存在了

class HungrySingleton(object):
    __instance = None
    def __new__(cls, *args, **kwargs):
        if cls.__instance == None:
            cls.__instance = super(HungrySingleton, cls).__new__(cls)
        return cls.__instance
@staticmethod
def get_instance():
    return HungrySingleton.__instance

h1 = HungrySingleton() h2 = HungrySingleton() print(h1._HungrySingleton__instance, h2._HungrySingleton__instance, end='\n') # 未调用get_instance方法时已经创建 print(h1.get_instance(), h2.get_instance(), end='\n') # 与上面一致

<__main__.HungrySingleton object at 0x0000020A568A1048> <__main__.HungrySingleton object at 0x0000020A568A1048>
<__main__.HungrySingleton object at 0x0000020A568A1048> <__main__.HungrySingleton object at 0x0000020A568A1048>

饿汉式单例在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以是线程安全的,可以直接用于多线程而不会出现问题。

应用实例

【例1】用懒汉式单例模式模拟产生美国当今总统对象。

分析:在每一届任期内,美国的总统只有一人,所以本实例适合用单例模式实现,图所示是用懒汉式单例实现的结构图。

class President(object):
    __instance = None
def __init__(self):
    print("产生一个新总统")

@staticmethod
def get_instance():
    if President.__instance == None:
        President.__instance = President()
    else:
        print("已经有一个总统了")
    return President.__instance

def get_name(self):
    print("特朗普是新总统")

if name == 'main': p1 = President.get_instance() p1.get_name() p2 = President.get_instance() p2.get_name() if p1 == p2: print('他们是同一人') else: print('他们不是同一人')

产生一个新总统
特朗普是新总统
已经有一个总统了
特朗普是新总统
他们是同一人

应用场景

前面分析了单例模式的结构与特点,以下是它通常适用的场景的特点。

  • 在应用场景中,某类只要求生成一个对象的时候,如一个班中的班长、每个人的身份证号等。
  • 当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接池等。
  • 当某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。

单例模式的扩展

单例模式可扩展为有限的多例(Multitcm)模式,这种模式可生成有限个实例并保存在 ArmyList 中,客户需要时可随机获取,其结构图如图所示。

本文由【Eappo_Geng】发布于开源中国,原文链接:https://my.oschina.net/gain/blog/3118226

全部评论: 0

    我有话说: