三种单例模式

1.什么是单例模式?

基于某种方式是实例化多次得到的实例是同一个。

2.为何用单例模式?

当实例化多次得到的对象中存放的属性都是一样的情况,应该将多个对象指向同一个内存,即同一个实例。节省内存。

单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中
一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,
单例模式是最好的解决方案。

【采用单例模式动机、原因】
对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统
只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。如在Windows中就只能打开一个任务管理器。如果
不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;如果这些窗口显示
的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态。因此有时确保系统中
某个对象的唯一性即一个类只能有一个实例非常重要。

如何保证一个类只有一个实例并且这个实例易于被访问呢?定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。
一个更好的解决办法是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。
这就是单例模式的模式动机。


【单例模式优缺点】

【优点】
一、实例控制
单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。
二、灵活性
因为类控制了实例化过程,所以类可以灵活更改实例化过程。
【缺点】
一、开销
虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。
二、可能的开发混淆
使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用new关键字实例化对象。因为可能无法访问库源代码,
因此应用程序开发人员可能会意外发现自己无法直接实例化此类。


三、对象生存期

不能解决删除单个对象的问题。在提供内存管理的语言中(例如基于.NET Framework的语言),只有单例类能够导致实例被取消分配,

因为它包含对该实例的私有引用。在某些语言中(如 C++),其他类可以删除对象实例,但这样会导致单例类中出现悬浮引用

单例模式的简单理解

1 单例模式 只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等
2 单例的缺点 就是不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。
用单例模式,就是在适用其优点的状态下使用

单例模式有三种实现方式,下面一一讲述。

 

单例模式实现方式一:在类中通过if判断,如果实例化过就直接返回之前实例化的对象,不会再去产生一个内存空间

 

import settings

class Mysql:
    __instance=None
    def __init__(self,ip,port):
        self.ip=ip
        self.port=port
        print(self)

    @classmethod
    def from_conf(cls): # 类作为参数自动传入 cls = Mysql
        if cls.__instance is None:  # Mysql.__instance 初始为None
            cls.__instance=cls(settings.IP,settings.PORT) # Mysql(IP,PORT)
            print(cls.__instance) # 实例化过一次就不会再去执行if下的语句了,直接return cls.__instance
        return cls.__instance # 不是空的直接返回,可以保证返回的是同一个实例

# obj=Mysql('1.1.1.10',3306)

obj1=Mysql.from_conf()
obj2=Mysql.from_conf()
obj3=Mysql.from_conf()

print(obj1)
print(obj2)
print(obj3)

obj4=Mysql('10.10.10.11',3307)

 

单例模式实现方式二:通过在类上加装饰器的方法实现。装饰器里的wrapper就是Mysql,调用wrapper相当于通过Mysql往里面传值,如果不传值args和kwargs长度为0,可以以此为关键进行判断。

import settings
def singleton(cls):
    cls.__instance=cls(settings.IP,settings.PORT) # 去Mysql中初始化对象的属性,得到一个对象。
    def wrapper(*args,**kwargs): # wrapper就是Mysql
        if len(args) == 0 and len(kwargs) == 0:
            return cls.__instance  # 如果Mysql不传值,args 和kwargs就是0,直接返回cls.__instance
        return cls(*args,**kwargs) # 如果传值,就执行cls(*args,**kwargs)
    return wrapper

@singleton #Mysql=singleton(Mysql) #Mysql=wrapper
class Mysql:
    def __init__(self,ip,port):
        self.ip=ip
        self.port=port


obj1=Mysql() #wrapper()
obj2=Mysql() #wrapper()
obj3=Mysql() #wrapper()
print(obj1 is obj2 is obj3)
print(obj1)
print(obj2)
print(obj3)
obj4=Mysql('1.1.1.4',3308)
print(obj4)

  

单例模式实现方式三:

 

import settings
class Mymeta(type):
    def __init__(self,class_name,class_bases,class_dic): #self=Mysql
        super(Mymeta,self).__init__(class_name,class_bases,class_dic )
        self.__instance=self.__new__(self) #造出一个Mysql的对象
        self.__init__(self.__instance,settings.IP,settings.PORT) #从配置文件中加载配置完成Mysql对象的初始化

        print(self.__instance)
        print(self.__instance.__dict__)

    def __call__(self, *args, **kwargs): #self=Mysql
        if len(args) == 0 and len(kwargs) == 0:
            return self.__instance

        obj=self.__new__(self)
        self.__init__(obj,*args,**kwargs)
        return obj

class Mysql(object,metaclass=Mymeta): #Mysql=Mymeta(...)
    def __init__(self,ip,port):
        self.ip=ip
        self.port=port

obj1=Mysql()
obj2=Mysql()
obj3=Mysql()
obj4=Mysql('10.10.10.11',3308)

print(obj1)
print(obj2)
print(obj3)
print(obj4)

  

 

转载于:https://www.cnblogs.com/Roc-Atlantis/p/9255324.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值