世界上本来没有
。用的人多了,也就成了
- 设计模式
。所以,我们不是严格按照它的定义去执行,可以根据自己的实际场景、需求去变通。领悟了其中的思想,实现属于自己的
- 设计模式
。 你肯定有过这样的体会。某某时候,听人说起 ** 模式。这么牛逼,回去得看看。结果仔细一看原来自己早就是这么用了,只是不知道它还有个这么高大上的名字。当然,专业的名字方便我们业内交流和教学,对技术的发展和传播起着重要的作用。
- 设计模式
废话不多说,和我一起来学习这些高大上的术语吧。本系列《设计模式学习》,通过对传统面向对象编程语言
和函数为第一等的元素的
- C#
语言来对比学习加深对
- javascript
的领悟和运用。
- 设计模式
单例模式
个人理解:只能存在一个实例
官方解释:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
示例 1
- public static class Singleton
- {
- //TODO
- }
表激动,它确实不是我们平时使用的单例模式。它只是个静态对象。但是,我觉得也是可以当成单例来使用的。
当然,肯定有它的不足,不然也不会去搞个单例模式了。
示例 2
- public class Singleton
- {
- public static Singleton singleton = new Singleton();
- }
- Console.WriteLine(Singleton.singleton.Equals(Singleton.singleton)); //true
其实它是个假单例
- Singleton s1 = new Singleton();
- Singleton s2 = new Singleton();
- Console.WriteLine(s1.Equals(s2)); //false
且有缺点
示例 3
- public class Singleton
- {
- public static readonly Singleton singleton = new Singleton();//自读字段
- private Singleton()//禁止初始化
- {
- }
- }
这是一个比较简单的单例,但是自动化初始变量还是存在
示例 4
- public class Singleton
- {
- public static Singleton singleton = null;
- public static Singleton GetSingleton()
- {
- if (singleton == null)
- {
- singleton = new Singleton();
- }
- return singleton;
- }
- private Singleton()//禁止初始化
- {
- }
- }
如此一来,我们就可以在调用 GetSingleton 方法的时候再去实例话了。注意:实例化之后 singleton 变量值不能再被 GC 回收了,因为它是个静态变量。
如此就算完事了吗?不,如果多线程同时执行的时候还是会出现多个实例。
- public class Singleton
- {
- public static Singleton singleton = null;
- public static Singleton GetSingleton()
- {
- if (singleton == null) //线程二执行到这里singleton == null为true,会继续下面实例Singleton
- {
- //线程一执行到这里
- Thread.Sleep(1000);//假设这还有段耗时逻辑(也可以理解并发极限)
- singleton = new Singleton();
- }
- return singleton;
- }
- private Singleton()//禁止初始化
- {
- }
- }
所以还需要继续改进
示例 5
- public class Singleton
- {
- public static Singleton singleton = null;
- private static object obj = new object();
- public static Singleton GetSingleton()
- {
- if (singleton == null) //下面有锁了为什么还要判断,因为锁会阻塞线程。而singleton被实例化后这个判断永远为false,不在需要锁。
- {
- lock (obj)
- {
- //这里代码只可能存在一个线程同时到达
- if (singleton == null)
- {
- Thread.Sleep(1000);
- singleton = new Singleton();
- }
- }
- }
- return singleton;
- }
- private Singleton()//禁止初始化
- {
- }
- }
这就是我们常见的单例类代码了。当然你也可以改成读取属性的方式。但区别不大。
- public class Singleton
- {
- private static Singleton singleton = null;
- private static object obj = new object();
- public static Singleton Instance
- {
- get
- {
- if (singleton == null)
- {
- lock (obj)
- {
- if (singleton == null)
- {
- singleton = new Singleton();
- }
- }
- }
- return singleton;
- }
- }
- private Singleton()//禁止初始化
- {
- }
- }
上面用了那么多的笔墨分析单例模式的使用,可是我们在什么场景下使用单例呢?
最典型的就是配置文件的读取,通常我们的配置文件是在程序第一次启动的时候读取,运行中是不允许修改配置文件的。
- public class ConfigInfo {
- private static ConfigInfo singleton = null;
- private static object obj = new object();
- public static ConfigInfo Instance {
- get {
- if (singleton == null) {
- lock(obj) {
- if (singleton == null) {
- singleton = new ConfigInfo();
- //从配置文件读取并赋值
- singleton.Email = "zhaopeiym@163.com";
- singleton.EmailUser = "农码一生";
- singleton.EmailPass = "***********";
- }
- }
- }
- return singleton;
- }
- }
- public string Email {
- get;
- private set;
- }
- public string EmailUser {
- get;
- private set;
- }
- public string EmailPass {
- get;
- private set;
- }
- private ConfigInfo() //禁止初始化
- {}
- }
调用
- var emailInfo = ConfigInfo.Instance;
- EmailSend(emailInfo.Email,emailInfo.EmailUser,emailInfo.EmailPass);
好了,C# 中的单例模式大概就这样了。
js 和 C# 是不同的,一个是 "无类" 语言,一个是传统的面向对象语言。而在 js 中的单例就比较简单了。比如我们熟悉的 window 对象。
那么我们怎么在 js 中实现自己的单例模式呢?方法很多,先来个简单的:
示例 1
- var Singleton = {
- name: "农码一生",
- getName: function () {
- return this.name;
- }
- }
这就是一个最简单的单例,通过字面量创建一个对象。看着是不是非常像 C# 中的静态类?但是,它不存在静态类中的缺点。
继承毫无压力:
- var Person = {
- age: 27
- }
- var Me = Person;
- Me.name = "农码一生";
- Me.getName = function () {
- return this.name;
- }
- Me.getAge = function () {
- return this.age;
- }
虽然如此,但它并不完美。按理说字段不应该被外界随意修改的。可是 js"无类",更别说私有字段了。幸运的是 js 中有无处不在的闭包。
示例 2
- var Singleton = (function () {
- var name = "农码一生";
- return {
- getName: function () {
- return name;
- }
- }
- })();
如此一来,我们就实现了一个单例模式。经过前面对 C# 单例的分析,我们希望在使用的时候才去实例话对象怎么办?(且不要小看这个惰性加载,在实际开发中作用可大着呢。)
示例 3
- var Singleton = (function () {
- var Person = function () {
- this.name = "农码一生";
- }
- Person.prototype.getName = function () {
- return this.name;
- };
- var instance;
- return {
- getInstance: function () {
- if (!instance) {
- instance = new Person();
- }
- return instance;
- }
- }
- })();
- var person1 = Singleton.getInstance();
- var person2 = Singleton.getInstance();
- console.log(person1 === person2); //true
这算是 js 中比较标准的单例模式了。可能有同学会问,之前 C# 的时候我记得加了 lock 锁的啊。这里怎么就算比较标准了呢。不要忘记,==js 天生的单线程,后台天生的多线程 ==。这就是区别。
为了职责的单一,我应该改写成
示例 4
- var Person = function () {
- this.name = "农码一生";
- }
- Person.prototype.getName = function () {
- return this.name;
- };
- var Singleton = (function () {
- var instance;
- return {
- getInstance: function () {
- return instance || (instance = new Person(););//简化if判断
- }
- }
- })();
我们很多时候都会使用到单例,那我们可否把一个对象变成单例的过程抽象出来呢。如下:
示例 5
- //通用的创建单例对象的方法
- var getSingle = function(obj) {
- var instance;
- return function() {
- return instance || (instance = new obj());
- }
- };
- var PersonA = function() {
- this.name = "农码一生";
- }
- var PersonB = function() {
- this.name = "农码爱妹子";
- }
- var singlePersonA = getSingle(PersonA); //获取PersonA的单例
- var singlePersonB = getSingle(PersonB); //获取PersonB的单例
- var a1 = singlePersonA();
- var a2 = singlePersonA();
- var a3 = singlePersonB();
- var a4 = singlePersonB();
- console.log(a1 === a2); //true
- console.log(a3 === a4); //true
- console.log(a1 === a3); //false
有没有头晕晕的,习惯就好了。你会说,我直接用最开始的全局变量字面量对象得了,可你不要忘记会造成变量名的污染。
我们在做 Tab 也切换的时候就可以用到单例模式。在此,我们做个非单例和单例的比较
示例 6 非单例:
- //获取tab1的html数据
- var getTab1Html = function() {
- this.url = "/tab/tab1.json";
- //$.get(this.url, function (data) {
- // //这里获取请求到的数据,然后加载到tab页面
- //}, "json");
- console.log("执行");
- }
- var getTab2Html = function() {
- this.url = "/tab/tab2.json";
- //$.get(this.url, function (data) {
- // //这里获取请求到的数据,然后加载到tab页面
- //}, "json");
- console.log("执行");
- }
- //点击tab1的时候加载tab1的数据
- $("#tab1").on("click",
- function() {
- getTab1Html();
- }) $("#tab2").on("click",
- function() {
- getTab2Html();
- })
我们发现没点击一次 tab 的时候会请求一次后台数据,然后加载页面。这是不是有点傻。正确的姿势应该是第一次点击的时候加载,后面不在请求加载。那么我们就可以使用单例模式了。
示例 7 单例:
- //获取tab1的html数据
- var getTab1Html = function () {
- this.url = "/tab/tab1.json";
- //$.get(this.url, function (data) {
- // //这里获取请求到的数据,然后加载到tab页面
- //}, "json");
- console.log("执行");
- }
- var getTab2Html = function () {
- this.url = "/tab/tab2.json";
- //$.get(this.url, function (data) {
- // //这里获取请求到的数据,然后加载到tab页面
- //}, "json");
- console.log("执行");
- }
- var loadTab1 = getSingle(getTab1Html);
- var loadTab2 = getSingle(getTab2Html);
- //点击tab1的时候加载tab1的数据
- $("#tab1").on("click", function () {
- loadTab1();
- })
- $("#tab2").on("click", function () {
- loadTab2();
- })
此时,我们无论点击多少此 tab。它也只会在第一次点击的时候请求加载页面数据了。
注意:
设计模式之所以能成为设计模式,也是在不断尝试、改进后得到的最佳实践而已。所以,我们不需要生搬硬套,适合的才是最好的。在此,关于单例模式的学习到此结束。谢谢您的阅读。
本文已同步至索引目录:
本文 demo:
来源: http://www.cnblogs.com/zhaopei/p/Single.html