单例
意图
单例是一种创建设计模式,它允许您确保类只有一个实例,同时提供对该实例的全局访问点。

问题
单例模式同时解决了两个问题,违反了单一责任原则:
确保一个类只有一个实例.为什么有人想要控制一个类有多少个实例呢?最常见的原因是为了控制对某些共享资源的访问——例如,数据库或文件。
下面是它的工作原理:假设您创建了一个对象,但过了一段时间后决定创建一个新的对象。您将得到一个已经创建的对象,而不是接收一个新的对象。
注意,这种行为是不可能用常规构造函数实现的,因为需要调用构造函数必须总是按设计返回一个新对象。

客户甚至可能没有意识到他们一直在使用同一个对象。
提供该实例的全局访问点.还记得你(好吧,我)用来存储一些基本对象的全局变量吗?虽然它们非常方便,但也非常不安全,因为任何代码都可能覆盖这些变量的内容并使应用程序崩溃。
就像全局变量一样,单例模式允许您从程序中的任何地方访问某个对象。但是,它也保护该实例不被其他代码覆盖。
这个问题还有另一方面:您不希望解决问题#1的代码分散在整个程序中。最好将它放在一个类中,特别是如果您的其余代码已经依赖于它。
现在,单例模式已经变得如此流行,以至于人们可能会把一些东西称为a单例即使它只解决了上面列出的一个问题。
解决方案
所有单例的实现都有以下两个共同的步骤:
- 属性的默认构造函数设为私有,以防止其他对象使用
新
操作符与Singleton类。 - 创建一个充当构造函数的静态创建方法。在底层,这个方法调用私有构造函数来创建一个对象并将其保存在一个静态字段中。对该方法的所有后续调用都会返回缓存的对象。
如果你的代码可以访问单例类,那么它就可以调用单例的静态方法。所以无论何时调用该方法,都会返回相同的对象。
真实的模拟
政府是单例模式的一个很好的例子。一个国家只能有一个官方政府。不管组成政府的个人身份如何,“X的政府”这一标题是一个全球性的访问点,可以识别负责的人群。
结构


的单例类声明静态方法
getInstance
返回自己类的相同实例。单例的构造函数应该对客户端代码隐藏。调用
getInstance
方法应该是获取Singleton对象的唯一方法。
伪代码
在本例中,数据库连接类充当单例.该类没有公共构造函数,因此获取其对象的唯一方法是调用getInstance
方法。该方法缓存第一个创建的对象,并在随后的所有调用中返回它。
数据库类定义了' getInstance '方法,该方法允许//客户端在整个程序中访问数据库连接的同一个实例。存储单例实例的字段应该声明为static。单例对象的构造函数应该始终是私有的,以防止使用' new ' //操作符直接构造调用。private构造函数Database() is //一些初始化代码,例如实际的//到数据库服务器的连接。/ /……//控制对单例的访问的静态方法。公共静态方法getInstance()是if(数据库。instance == null),然后acquireThreadLock(),然后//确保实例还没有被另一个线程初始化,而这个//已经在等待锁释放。如果数据库。instance == null)然后数据库。instance = new Database() return Database.instance // Finally, any singleton should define some business logic // which can be executed on its instance. public method query(sql) is // For instance, all database queries of an app go // through this method. Therefore, you can place // throttling or caching logic here. // ... class Application is method main() is Database foo = Database.getInstance() foo.query("SELECT ...") // ... Database bar = Database.getInstance() bar.query("SELECT ...") // The variable `bar` will contain the same object as // the variable `foo`.
适用性
当你的程序中的一个类只有一个对所有客户端可用的实例时,使用单例模式;例如,由程序的不同部分共享的单个数据库对象。
单例模式禁止创建类对象的所有其他方法,除了特殊的创建方法。这个方法要么创建一个新对象,要么返回一个已经创建的对象。
当您需要对全局变量进行更严格的控制时,请使用单例模式。
与全局变量不同,单例模式保证一个类只有一个实例。除了Singleton类本身,没有任何东西可以替换缓存的实例。
请注意,您总是可以调整这个限制,允许创建任意数量的单例实例。的代码体是唯一需要更改的代码段getInstance
方法。
如何实施
向类中添加一个私有静态字段,用于存储单例实例。
声明一个用于获取单例实例的公共静态创建方法。
在静态方法中实现“惰性初始化”。它应该在第一次调用时创建一个新对象,并将其放入静态字段中。该方法应该总是在所有后续调用中返回该实例。
将类的构造函数设为private。类的静态方法仍然能够调用构造函数,但不能调用其他对象。
检查客户端代码,将所有对单例构造函数的直接调用替换为对其静态创建方法的调用。
利与弊
- 可以确定一个类只有一个实例。
- 您将获得该实例的全局访问点。
- 单例对象只有在第一次被请求时才被初始化。
- 违反了单一责任原则.该模式同时解决了两个问题。
- 单例模式可以掩盖糟糕的设计,例如,当程序的组件彼此了解太多时。
- 该模式需要在多线程环境中进行特殊处理,以便多个线程不会多次创建单个对象。
- 对单例的客户端代码进行单元测试可能很困难,因为许多测试框架在生成模拟对象时依赖于继承。由于单例类的构造函数是私有的,并且在大多数语言中不可能重写静态方法,因此您需要考虑一种创造性的方法来模拟单例。或者干脆不写测试。或者不要使用单例模式。