浅谈对单例模式的认识演变及代码实践

     记得n年前从VB6开始学编程(那时对面向对象没有太深理解), VB里面有个Module-模块,用来放全局公用的函数,过程,常数,自定义结构,全局变量等等。等能用VB做些常用的软件了,开始转C#了,动手写程序的时候,突然觉得不适应,全局变量放哪里呢?(那时候真是有点菜菜),刚开始是用static。static 函数,static 变量,凡是想要全局用的都扔到一个static类里面。(其实全局常量和工具方法这么用也没错)但慢慢又发现不对了,静态方法无法调用示例方法啊,怎么办呢,傻眼了。


     忘了从那里看到了单例模式,如获至宝,这不就是我要的么,简单易用?于是乎,程序中单例到处可见,当成万金油了,也没觉得有什么不好。那时候的单例大概是这么用的:



      随着对面向对象认识的深入,越发的觉得单例模式不简单,有人批判,有人离不开,总而言之,没有最好的单例模式,只有最合适的。下面的代码分别演示了我见过的不同形式的用法,加入一点个人意见,算是抛砖引玉了。



(来自Robert C. Martin大牛的“敏捷软件开发”的一个例子)

       怎么样,用属性是不是觉某些人觉得更.net。

 


      加了锁,防止多个线程同时第一次访问,初始化的时候带来线程不同步的隐患(如果Singleton构造函数中有不少逻辑的话,线程同步的问题就很明显了)。


 

       还是在Robert的书中看到他这么用,并且提示是利用的了.net的初始化功能。 没用if语句来避免多次创建,我很理解,但是没有锁,会不会有隐患呢?困扰了我好一阵, 直至看了Jeffrey Richter的“CLR via C#”,书中谈到类型构造器,是这么说的,“CLR希望确保在每个AppDomain中,一个类型构造器只执行一次。为了保证这一点,在调用类型构造器时,调用线程要获取一个互斥线程同步锁。这样一来,如果多个线程试图同时调用某个类型的静态构造器,只有一个线程才可以获得锁,其他线程会被阻塞(blocked)。第一个线程会执行静态构造器中的代码。当第一个线程离开构造器后,正在等待的线程将被唤醒,然后发现构造器的代码已经被执行过。因此,这些线程不会再次执行代码,将直接从构造器方法返回。”从而得出结论:“由于CLR保证一个类型构造器在每个AppDomain中只执行一次,而且(这种执行)是线程安全的,所以非常适合在类型构造器中初始化类型需要的任何单实例(Singleton)对象”。


 

 

      身边的一位哥们总爱这么用,问他为什么,他也不知道。现在理解了,static SingletonInstance中的初始化是线程安全的,但是为啥要放在嵌套类中呢??向下面这样不就得了?

 

 

 

       另外,还是从Jeffrey的书中看到上面代码和下面代码是等效的:



       C#提供了简单的语法来初始化类型的静态字段,生成上述代码时,编译器自动为Singleton类生成一个类型构造器 static SomeType(){ Instance = new Singleton; },呵呵,代码是不是越来越短了?

 

      该结束了,关于单例模式的话题很深奥,我在Christian Gross的“.Net 2.0模式开发实战中”还看过一种通用的基于泛型的读-写线程安全的可复用的单例,一个程序中所有单例类实现不必丢的到处都是,最终用法类似于


 

      可复用,线程安全,并且单例允许传入参数,一劳永逸,哈哈。代码太长了,就不再引用了,有兴趣的人找书看吧。现在我的代码中,单例的使用越来越谨慎了,有很多好的模式可以用,不要把单例当成万能解决方案,要看到单例的隐患和复杂性,从而选择最合适的用法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值