单例模式(Singleton Pattern)

单例模式

1. 啥时候使用单例模式
保证系统中某一服务有一个统一的入口,如:一个系统中可以存在多个打印服务,但只能有一个正在工作的任务;一个系统中只能有一个计时工具或序号生成器。

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

2. 单例模式的定义

单例模式(Singleton Pattern):确保某一个类有且仅有一个实例,并且自行实例化向整个系统提供这个实例。

分析:某个类仅有一个实例,并且必须是自身创建这个实例,还必须自身向整个系统提供这个实例。

3. 该模式中包含的角色及其职责

1)、Singleton:单实例
对整个系统提供有且仅有一个实例,并且是自身创建这个实例。

这里写图片描述

不废话了,看下面代码!

4. 撸代码

PHP的单例模式相对于其他语言是比较简单的,因为它不需要考虑多线程问题,下面我们给出两种代码实例(PHP和C#)。

先看下在PHP下的单例模式:


/** * 单实例角色:Singleton * 防止类被继承 */
final class Singleton
{
    /** * 定义一个私有的静态全局成员变量来保存该类的唯一实例 * @var Singleton */
    private static $instance;

    /** * 通过静态方法来构造对象实例 * */
    public static function getInstance()
    {
        //检查类是否已被实例化
        if (null === static::$instance) {
            static::$instance = new static();
        }

        return static::$instance;
    }

    /** * 定义私有的构造函数 * 防止外部通过new关键字实例化对象 * 只允许自身实例化 */
    private function __construct()
    {
    }

    /** * 防止被复制或克隆 */
    private function __clone()
    {
    }

    /** * 防止反序列化创建该实例 */
    private function __wakeup()
    {
    }
}
/** * 测试单例模式 * */
class TestSingleton
{
    public function test(){
        //获取并创建单例对象
        $singleton = Singleton::getInstance();
    }
}

通过上例代码看出,在PHP中创建单例模式,必须遵循一下几点:
1)、有一个私有的类对象成员变量
2)、构造函数必须是私有的,防止通过new关键字被实例化
3)、__clone()和__wakeup()必须为私有的,防止复制、克隆和反序列化
4)、必须提供一个可供外界访问的静态方法,并通过此静态方法实现自身实例化对象。

在PHP中实现单例模式相对简单,因为它不需要考虑多线程问题,但C#中就必须要靠线程安全,如果C#在多线程中访问单例模式就有可能被创建多个对象,所以不得不考虑线程安全问题,要确保在多线程下访问单例模式依然是被创建一个实例对象。

C#代码如下:


    /// <summary>
    /// 单实例角色:Singleton
    /// </summary>
    public sealed class Singleton
    {
        /// <summary>
        /// 定义一个私有的静态全局成员变量来保存该类的唯一实例
        /// </summary>
        private static Singleton instance = null;
        /// <summary>
        /// 定义一个只读静态对象,且这个对象是在程序运行时创建
        /// 必须为引用类型,确保访问同一地址
        /// </summary>
        private static readonly object syncObject = new object();
        /// <summary>
        /// 定义私有的构造函数
        /// 防止外部通过new关键字实例化对象
        /// 只允许自身实例化
        /// </summary>
        private Singleton() { }
        /// <summary>
        /// 通过静态方法来构造对象实例
        /// </summary>
        /// <returns></returns>
        public static Singleton GetInstance()
        {
            //第一次判断:主要判断单例对象是否已被实例化
            if (instance == null)
            {
                //锁住引用类型对象,进行同步操作
                //必须为引用类型,因为引用类型的变量地址是同一内存地址
                lock (syncObject)
                {
                    //第二次判断:主要是防止可能延迟加载或缓存,造成创建多个实例
                    if (instance == null)
                    {
                        instance = new Singleton();
                    }
                }
            }

            return instance;
        }
    }

通过上例代码看出,在C#或含有线程操作的面向对象语言中,必须遵循一下几点:
1)、有一个私有的类对象成员变量
2)、有一个私有的静态只读引用类型的对象变量,并且该对象在程序运行时被创建。
3)、构造函数必须是私有的,防止通过new关键字被实例化
4)、必须提供一个可供外界访问的静态方法,并通过此静态方法实现自身实例化对象。
并且在此方法内必须进行两次检查对象实例是否被创建,防止可能延迟加载或缓存,造成创建多个实例。

5. 单例模式的优点

实例控制

单例模式会防止其他对象实例化其自身的单例对象的副本,从而确保所有对象都访问唯一实例。

灵活性

因为类控制了实例化过程,所以类可以灵活更改实例化过程。

6. 单例模式的缺点

开销

虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。

对象生存期

不能解决删除单个对象的问题。在提供内存管理的语言中(c#),只有单例类能够导致实例被取消分配,因为它包含该实例的私有引用。

滥用单例带来的一些负面问题

如果为了节省资源将数据库连接池对象设计为单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;现在很多面向对象语言(java、C#)的运行环境都提供了自动垃圾回收的技术,因此,如果实例化的对象长时间不被利用,系统会认为它是垃圾,会自动销毁并回收资源,下次利用时又将重新实例化,这将导致对象状态的丢失。

7. 使用场景

系统只需要一个实例对象,如系统要求提供一个唯一的入口,或者考虑资源消耗太大而只允许创建一个实例。

转载于:https://my.oschina.net/u/3188870/blog/835046

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值