【读书活动感悟分享】极客充电队——《Head First 设计模式》第五章单件模式读书心得_文章

【读书活动感悟分享】极客充电队——《Head First 设计模式》第五章单件模式读书心得

孙治国
发表于 2025-11-10 19:58:09

一、读书前的疑问

什么是单件模式? 就是构造函数为私有方法private,无法从外部创建对象,只能通过此类提供的一个静态方法获取对象。

什么情况下才应该使用单件模式呢?我直接使用静态方法不行吗,不是也不用创建对象吗?

于是,翻了下代码,找到了下面一个类,这个类使用单件模式的意义是什么?这是一个用于生成随机码的工具类,直接使用静态方法不行吗?


    public class CheckCodeUtils
    {
        private static readonly Lazy lazy = new Lazy(() => new CheckCodeUtils());

        public static CheckCodeUtils Instance { get { return lazy.Value; } }

        private Random Rand { get; set; }

        private CheckCodeUtils()
        {
            this.Rand = new Random();
        } 
        public string Create(int length = 12)
        {
            StringBuilder str = new StringBuilder();
            for (int i = 0; i < length; i++)
            {
                str.Append(Rand.Next(0, 9));
            }
            return str.ToString();
        }
    }


二、从书中寻找答案

1、单件模式的定义

单件模式确保一个类只有一个实例,并提供一个全局访问点。



2、单件模式的类图


3、书中的巧克力工厂锅炉程序的例子

巧克力工厂的锅炉控制对象,存储了锅炉的实时状态,例如是否空载,是否煮沸等。锅炉对象需要根据当前的状态去做相应的动作。如果状态维护的不对,就会非常危险。

为什么要用单件模式,就很容易理解了,如果存在多个对象的话,锅炉状态就会混乱,因为每个实例中的状态无法保证全局一致。

所以就得到了第一个答案,什么情况下需要使用单价模式:   需要确保数据状态全局统一,避免错乱。


4、单件模式如何保证全局只有一个对象

如果我们的程序是单线程操作,这个很容易实现,只需要简单的if判断条件即可。

但是如果出现在多线程场景下,就需要考虑会不会创建出多个对象。

目前有很多固定的实现方式,例如常见的双重检查锁、以及c#固有的Lazy懒加载方式。

其中Lazy的实现方式,在上面示例代码中已经给出(虽然那个场景可能不需要使用单件模式)。  


5、单件模式在特来电的应用


(1)数据库访问 DaoService,使用单例模式,避免重复重建数据库服务实例带来的性能开销


public class DaoService
{
    private class IntercepterEqulityComparer : IEqualityComparer
    {
        public bool Equals(StandardInterceptor x, StandardInterceptor y)
        {
            return string.Equals(x.GetType().FullName, y.GetType().FullName, StringComparison.OrdinalIgnoreCase);
        }

        public int GetHashCode(StandardInterceptor obj)
        {
            return obj.GetType().FullName.GetHashCode();
        }
    }

    private static IDaoHandler handler;

    private static Dictionary dicDaoSvc;

    private static object lockObj;

    private string connName = string.Empty;

    //
    // 摘要:
    //     公共拦截器
    public static readonly ConcurrentBag CommonInterceptors;

    static DaoService()
    {
        handler = new DaoHandler();
        dicDaoSvc = new Dictionary();
        lockObj = new object();
        CommonInterceptors = new ConcurrentBag();
        CommonInterceptors.Add(new FlowControlInterceptor());
        CommonInterceptors.Add(new DaoExceptionInterceptor());
    }

    private DaoService(string connName)
    {
        this.connName = connName;
    }

    //
    // 摘要:
    //     获取服务实例
    //
    // 参数:
    //   connName:
    public static DaoService GetInstance(string connName)
    {
        DaoService value = null;
        if (!dicDaoSvc.TryGetValue(connName, out value))
        {
            lock (lockObj)
            {
                if (!dicDaoSvc.TryGetValue(connName, out value))
                {
                    value = new DaoService(connName);
                    dicDaoSvc[connName] = value;
                }
            }
        }

        return value;
    }

(2) KafkaProducer的单例实现, 避免重复kafka连接带来的开销。


    public class KafkaProducer where T : ILog
    {
        private static readonly object LOCK_OBJ = new object();
        private static IKafkaProducer Producer = null;
        public static IKafkaProducer Instance
        {
            get
            {
                if (Producer == null)
                {
                    lock (LOCK_OBJ)
                    {
                        if (Producer == null)
                        {
                            Producer = KafkaProducerProvider.Current.GetDefaultProducer(KafkaMessageSerializer.ProtobufDeserializer);
                        }
                    }
                }
                return Producer;
            }
        }
    }


三、知识点总结

1、需要全局访问,且需要确保全局状态统一,可以使用单件模式(例如全局缓存)

2、TPS较高的场合,如果初始化实例,需要高耗时或者占用资源较多时,可以通过单件模式避免重复创建实例带来的性能问题(例如数据库连接、配置加载)

3、学习了常用的几种单件模式的实现方式(静态方法、双重检查锁、Lazy懒加载方式)







59 0

评论


意见反馈