设计模式-单例模式

摘要

       本文将主要讲解创建型模式中的单例模式先来讲解,因为单例模式是最简单也是最容易理解的设计模式,上手快,易使用的设计模式。本文将从下面的流程来讲解

单例模式,后面讲述的设计模式也将使用这样的方式。

       1、什么是单例模式?

       2、单例模式的应用场景。

       3、举例说明单例模式的使用。

       4、总结单例模式的用法。

本文大纲

       a、摘要。

       b、本文大纲。

       c、单例模式的简介。

       d、相关应用场景分析。

       e、本文总结。

单例模式的简介

       本章我们将来讲述下单例模式的使用,首先我们来看看单例模式的定义:

       单例模式:是一种软件设计中常用的设计模式,主要是用来控制某个类必须在某个应用程序中只能有一个实例存在。

       有时候我们需要确保整个系统中只有某个类的一个实例存在,这样有利于我们协调控制系统的行为。例如:我们在某个系统中使用了发送短信的这样的服务,那么

我们可能希望通过单一的短信服务类的实例,而不是多个对象实例完成短信的发送服务。这时我们可以通过单例模式来完成。

       image 上图简单描述了单例模式应用的位置。

        我们看看单例模式的几种实现方式:

        image

        下面我们来举例说明下这2种方式的实现。

        1、外部控制的方式

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Instance
{
    private List<SendMessage> lists =new List<SendMessage>();
    private SendMessage sendInstance;
 
    public SendMessage SInstance
    {
        get
        {
            return sendInstance;
        }
    }
 
    public void InstanceMethod()
    {
        if (lists.Count == 0)
        {
            sendInstance =new SendMessage();
            lists.Add(sendInstance);
        }
        else
        {
            sendInstance = lists[0];
        }
    }
}

       2、内部控制方式

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Instance1
 {
    private static SendMessage sendInstance;
    private static object _lock =new object();
 
    protected Instance1()
    {
    }
 
    public static SendMessage SInstance
    {
        get
        {
            lock (_lock)
            {
                if (sendInstance ==null)
                    sendInstance =new SendMessage();
                return sendInstance;
            }
        }
    }
}

        这里有几点需要注意的地方,对于第二种方式有几个地方需要说明下,首先是要控制全局只有一个实例的类,请定义成静态实例,这样可以确保只有一个实例对

象,其次,这个对象的构造函数请声明成保护类型的成员,这样可以屏蔽通过直接实例化的形式来访问。通过这样的形式,客户可以不需要知道某个单例实例对象的内

部实现细节。一般情况下满足上面的2点需求就可以完成全局唯一访问入口的控制。当然可能在多线程的情况下采用这样的形式还会有一定的弊端,当然我们这里也简单

的讲解下相应的控制方案。方案如下:

 

?
1
2
3
4
5
6
7
public class CoolInstance
{
    private CoolInstance()
    {
    }
    public static readonly CoolInstance Instance =new CoolInstance();
}

    

 

       看吧很简单吧,当然我们这里来简单解释下原理:

       1、我们先把构造函数声明为私有的构造函数,这样我们能够屏蔽外部通过实例化的形式访问内部的成员函数。所有的成员函数的访问必须通过静态成员Instance

来完成访问。

       2、这段代码通过定义公共、静态、只读的成员相当于在类被第一次使用时执行构造,由于是只读的,所以一旦构造后不允许修改,就不用担心不安全的问题。

        相信对上面的介绍大家应该基本上知道单例模式的应用了,那么下面我们来看看项目中的实际应用场景及用法。

相关应用场景讲解

        1、场景短信及邮件发送服务

        那么我们将采用上面介绍的最“COOL”的方式来进行控制,提供发送短信及发送邮件的服务。

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class CoolInstance
{
    private CoolInstance()
    {
    }
 
    public static readonly CoolInstance Instance =new CoolInstance();
 
    /// <summary>
    /// 发送手机短信
    /// </summary>
    public bool SendMessage(string telNumber,string content)
    {
        return true;
    }
 
    /// <summary>
    /// 发送邮件
    /// </summary>
    /// <param name="content"></param>
    /// <param name="toMail"></param>
    public bool SendMail(string content,string toMail)
    {
        return true;
    }
}

        我们再来看看调用类中如何书写完成调用。例如我们有个订单类,当有人新下订单时,将给卖家发送短信提醒功能。

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/// <summary>
 /// 订单业务
/// </summary>
public class Order
{
    public int Save()
    {
        //先是将订单的相关信息生成,
        this.InitOrderInfo();
 
        //执行订单的持久化方法
        int count=this.Add();
 
        //发送短信
        CoolInstance.Instance.SendMessage(string.Empty,string.Empty);
        //发送邮件
        CoolInstance.Instance.SendMail(string.Empty,string.Empty);
 
        return count;
    }
 
    /// <summary>
    /// 初始化订单信息
    /// </summary>
    private void InitOrderInfo()
    {
    }
 
    /// <summary>
    /// 新增订单信息
    /// </summary>
    /// <returns></returns>
    private int Add()
    {
        return 0;
    }
}

       

 

        这样我们就完成了短信发送服务及邮件发送服务的控制。主要还是根据自己的业务需要。

        2、例如我们现在提供一个系统日志服务或者打印或者扫描的服务,我们希望全局只有一个访问入口,那么我们就可以通过这样的单例模式来实现这样的需求。

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class PrintHelper
 {
     #region 构造函数
     private PrintHelper()
     {
     }
 
     public static readonly PrintHelper Instance =new PrintHelper();
     #endregion
 
     #region 打印服务
 
     /// <summary>
     /// 直接打印服务
     /// </summary>
     /// <returns></returns>
     public bool Print()
     {
         return true;
     }
 
     /// <summary>
     /// 打印预览
     /// </summary>
     /// <returns></returns>
     public bool PrintPreview()
     {
         return true;
     }
 
     #endregion
 }

        具体的调用类我就不写相应的代码,都和上面的形式类同,下面我们讲解下可能更特殊的需求,有时候我们可能需要更新我们创建的唯一实例,这时我们如何控

制单例实例对象的更新呢,有时候可能我们有这样的需求。下面我们来看看如何实现这样的需求。

        3、可更新单例对象的场景

        首先我们先说下什么情况下会遇到这样的更新方式呢?例如我们想在单例模式的类的构造函数是带有一定参数的情形时:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class UpdateHelper
{
    private string type =string.Empty;
    private static object _lock =new object();
    private static UpdateHelper instance;
    private UpdateHelper(string valueType)
    {
        type = valueType;
    }
 
    public static UpdateHelper Instance
    {
        get
        {
            lock (_lock)
            {
                if (instance ==null)
                {
                    //如果这里有多个条件需求的话,可能写起来会比较复杂,那么有更好的方式来处理吗?
                    instance =new UpdateHelper("test!");
                }
 
                return instance;
            }
        }
    }
}

        那么我们来分析几种办法,有没有更好的办法来处理呢?

        1、首先我们不能手动实例化,所以我们没有办法动态传入构造函数参数,只能在类的内部指定这个参数,但是有时候我们需要动态的更新这个参数,那么这样的

形式显然就没有办法实现。

        2、通过属性的方式,来动态的设置属性的内容来完成输出参数的改变,但是这样的方式可能太过自由,无法满足单例模式的初衷。

        3、接口方式,因为接口必须要靠类来实现,所以更不靠谱,可以不考虑这样的方式。

        4、通过Attribute的方式来将信息动态的注入到构造函数中,但是怎么说这样的方式是不是太兴师动众了呢?毕竟单例模式本来就是很简单的。

        5、通过配置文件,通过config文件配置节点的形式来动态的配置相关信息,实现更新实例对象内容的情况。

        通过上面的5种情况的分析,那么通过2、4、5可以实现这个要求,但是对比相应的代价来说,5的方式是最灵活也是最符合单例模式本来的规范要求,相对来说

成本和代价也可以接收。

 

?
1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.Web>
    <add key="ssss" >value</add>
 
  </system.Web>
</configuration>

 

         那么我们上面的单力模型中的代码只需要稍微的变化下即可,请看如下代码:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class UpdateHelper
{
    private string type =string.Empty;
    private static object _lock =new object();
    private static UpdateHelper instance;
    private UpdateHelper(string valueType)
    {
        type = valueType;
    }
 
    public static UpdateHelper Instance
    {
        get
        {
            lock (_lock)
            {
                if (instance ==null)
                {
                    //如果这里有多个条件需求的话,可能写起来会比较复杂,那么有更好的方式来处理吗?
                    instance =new UpdateHelper(System.Configuration.ConfigurationManager.AppSettings["ssss"].ToString());
                }
 
                return instance;
            }
        }
    }
}

        我想到这里大家都对单例模式有个简单的认识了,本文的内容就讲到这里。我们来回顾下我们讲述的内容:

        image

本文总结

        本文主要讲述了创建型模式中的单例模式,单例模式主要是用来控制系统中的某个类的实例的数量及全局的访问入口点。我们主要讲述了实现单例模式的方式,

分为外部方式及内部方式,当然我们现在采用的方式都是内部方式,还讲述了线程安全的单例模式及带有参数的构造函数的情况,根据配置文件来实现参数值的动态配

置的情况。希望本文的讲解能对不熟悉设计模式的同仁能够了解知道单例模式的应用,而对已熟知单例模式的同仁可以温故而知新,我会努力写好这个系列,当然我这

里可能在大牛的面前可能是班门弄斧吧,不过我会继续努力,争取写出让大家一看就明白的设计模式系列。本文错误之处再所难免,还请大家批评之处,我会继续改

转载于:https://my.oschina.net/OQKuDOtsbYT2/blog/127427

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值