一, 单例模式存在的意义
在这里的单例就是只有一个实例 (这里的实例就像在面向对象的时候, 创建了一个对象也可以说创建了一个实例), 只用一个实例进行程序设计, 首先我们可以了解一下什么时候不适合使用单例模式, 比如我们需要使用类同时创建多个对象的时候, 且每个对象中封装了不同的数据的时候, 就不能使用单例模式, 就像下面的例子
- class Person:
- def __init__(self,name,age):
- self.name = name
- self.age = age
- mingming = Person('明明',19)
- mingyue = Person('明月',30)
- mingri = Person('明日',20)
那么什么时候适合使用单例模式, 如果每个对象中封装了相同的数据, 但是需要创建多个对象的时候, 而且这两个实例所有的功能是一样的, 所以我们就可以使用一个实例完成, 在这里我们就可以使用单例模式, 如下
- class Person:
- def __init__(self):
- self.name = '123123'
- self.age = '20'
- def f1(self):
- pass
- def f2(self):
- pass
- mingming = Person()
- mingming.f1()
- mingyue = Person()
- mingyue.f1()
为什么要使用单例模式, 就是为了在封装数据相同的情况下, 而且每个实例可执行的方法相同的时候, 不必创建多个对象, 只需要使用一个实例, 来完成多个相同实例所完成的功能, 通过这种方式减少内存的使用.
二, 单例模式应用的场景
需要频繁的进行创建和销毁的对象;
创建对象时耗时过多或耗费资源过多, 但又经常用到的对象;
工具类对象;
频繁访问数据库或文件的对象.
三, 单例模式的优点以及缺点
优点
系统内存中该类只存在一个对象, 节省了系统资源, 对于一些需要频繁创建销毁的对象, 使用单例模式可以提高系统性能.
由于单例模式在内存中只有一个实例, 减少了内存开销.
单例模式可以避免对资源的多重占用, 例如一个写文件时, 由于只有一个实例存在内存中, 避免对同一个资源文件的同时写操作.
单例模式可以再系统设置全局的访问点, 优化和共享资源访问.
其中使用到单例模式时, 考虑较多的就是多线程的情况下如何防止被多线程同时创建等问题.
当这个类的对象在多个地方创建的时候, 使得内部的方法多次调用, 但是希望只要一个对象操作这个方法, 或者不希望多个地方同时调用这个方法, 需要保持这个方法的单一性质, 就用单利模式
缺点
使用单例模式, 扩展很困难, 若要扩展, 除了修改代码基本上没有第二种途径可以实现.
四, 单例模式代码编写
接下来我们就模拟一个数据库连接池来实现单例模式
首先我们介绍一下数据库连接池
我们通过程序进行数据库的操作的时候, 每次都需要连接数据库, 但是连接数据库需要消耗较多的时间, 所以我们可以在我们主机的内存里维护一个数据库连接池, 在这个连接池中有若干个已经连接数据库的连接, 我们想要连接数据库的时候直接到连接池中取出一个连接即可, 省去了连接的时间.
非单例模式
- import random
- class SqlConnectionPool:
- __instance = None
- def __init__(self):
- self.ip = "127.0.0.1"
- self.port = 3306
- self.pwd = '123456'
- self.username = 'alexsel'
- #去连接
- self.conn_list = [1,2,3,4,5,6,7,8,9,10]
- def get_connection(self):
- #获取连接
- r = random.randrange(1,11)
- return r
- # 我们创建多个对象内存地址是一样的, 说明拿到的是同一个对象
- obj = SqlConnectionPool()
- print(obj)
- obj1 = SqlConnectionPool()
- print(obj1)
- obj2 = SqlConnectionPool()
- print(obj2)
输出结果:
- <__main__.SqlConnectionPool instance at 0x0000000002630788>
- <__main__.SqlConnectionPool instance at 0x0000000002630888>
- <__main__.SqlConnectionPool instance at 0x00000000026308C8>
非单例模式每次内存输出的结果都不同, 下面是单例模式.
- import random
- class SqlConnectionPool:
- __instance = None
- def __init__(self):
- self.ip = "127.0.0.1"
- self.port = 3306
- self.pwd = '123456'
- self.username = 'alexsel'
- #去连接
- self.conn_list = [1,2,3,4,5,6,7,8,9,10]
- @staticmethod #静态方法是由类调用的
- def get_instance():
- if SqlConnectionPool.__instance:
- return SqlConnectionPool.__instance
- else:
- #创建一个对象, 并将对象赋值给静态字段__instance
- SqlConnectionPool.__instance = SqlConnectionPool()
- return SqlConnectionPool.__instance
- #单例模式关键代码解析
- #当第一次调用这个静态方法的时候, 判断__instacne 的值是 None 所以执行 else, 然后在 else 中创建一个对象赋给静态字段__instance, 然后返回这个静态字段
- #第二次调用这个今静态方法的时候, 判断__instance 的值为真, 所以直接返回这个静态字段, 而这个静态字段中包含的是第一次创建的对象, 所以在以后调用这个方法
- #就只会调用第一次创建的对象, 这就是单例模式
- def get_connection(self):
- #获取连接
- r = random.randrange(1,11)
- return r
- # 我们创建多个对象内存地址是一样的, 说明拿到的是同一个对象
- obj = SqlConnectionPool.get_instance()
- print(obj)
- obj1 = SqlConnectionPool.get_instance()
- print(obj1)
- obj2 = SqlConnectionPool.get_instance()
- print(obj2)
输出结果:
- <__main__.SqlConnectionPool instance at 0x000000000260C808>
- <__main__.SqlConnectionPool instance at 0x000000000260C808>
- <__main__.SqlConnectionPool instance at 0x000000000260C808>
以上是基于类, 使用静态字段和静态方法实现的一个单例模式.
来源: https://www.cnblogs.com/liudi2017/p/9393614.html