设计模式

来源:互联网 发布:行知实践园感悟 编辑:程序博客网 时间:2024/05/16 12:10

你肯定有过这样的体会。某某时候,听人说起**模式。这么牛逼,回去得看看。结果仔细一看原来自己早就是这么用了,只是不知道它还有个这么高大上的名字。当然,专业的名字方便我们业内交流和教学,对技术的发展和传播起着重要的作用。

废话不多说,和我一起来学习这些高大上的术语吧。本系列《设计模式学习》,通过对传统面向对象编程语言C#和函数为第一等的元素的javascript语言来对比学习加深对设计模式的领悟和运用。

定义


单例模式

个人理解:只能存在一个实例

官方解释:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

C#代码示例


示例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

且有缺点

在类被加载的时候就自动初始化了singleton

singleton应该被定义为只读属性

示例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()//禁止初始化

    {

    }

}

C#使用场景


上面用了那么多的笔墨分析单例模式的使用,可是我们在什么场景下使用单例呢?

最典型的就是配置文件的读取,通常我们的配置文件是在程序第一次启动的时候读取,运行中是不允许修改配置文件的。

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代码示例


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 

有没有头晕晕的,习惯就好了。你会说,我直接用最开始的全局变量字面量对象得了,可你不要忘记会造成变量名的污染。

JS使用场景


我们在做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。它也只会在第一次点击的时候请求加载页面数据了。

 

注意:

JS中不建议使用全局变量来达到单例的效果

其一,会引起变量名的全局污染

其二,不能惰性加载。

C#中不建议使用静态类来达到单例的效果

其一,不能继承类和接口

其二,内部变量和方法必须静态。

单例模式中实例变量要慎用。因为一个单例很可能被多处操作(修改了变量),从而影响的预期效果

0 0