其实我本来不是打算把系统架构中的一些设计模式单独抽出来讲解的,因为很多的好朋友也比较关注这方面的内容,所以我想通过我理解及平时项目中应用到的一些常见的设计模式,拿出来给大家做个简单讲解,我这里只是抛砖引玉,如果某个地方讲解的不正确或者不详细,请大家批评指出。园子里面的很多的大牛写的设计模式都非常的经典,我这里写可能有点班门弄斧的感觉,不过我还是决定把它写出来,希望能对初学者有一定的帮助和指导的作用。当然我这里如果说某个地方解释的有问题或者说是某个地方写的不符合逻辑之处,还请大家多多指出,提出宝贵意见。
软件工程中其实有很多总结性的话语,比如说软件=算法+数据结构等等这样的描述,当然我们这里可能算法就是泛指一些软件中的编程方法了,设计模式怎么去理解呢?为什么要有设计模式?它能带来什么?等等这些都是我们需要讨论的问题。首先我们需要知道设计模式能带来什么。可能这才是我们学习它的主要原因,如果说不能为我们在书写软件的过程中带来更方面的好处,那我们也不会使用和学习它。
设计模式是什么?
设计模式可以简单的理解为解决某一系列问题的完美的解决方案。我们在软件开发的过程中经常遇到设计功能实现的问题,而设计模式正是为了解决软件设计功能实现时遇到的某一类问题的解决方案。因为一般情况下来说,我们在某个软件功能的开发过程中遇到的功能设计问题,可能是前人很早就遇到过的问题,所以通过这种设计模式的方式来解决,能让我们在软件实现的过程中少走弯路,或者说是给我们的软件设计带来很好的灵活性和适应性。
设计模式带来了什么?
设计模式是源于实践,并且每种设计模式都包含了一个问题描述,问题涉及到的参与者并且提供了一个实际的解决方案。设计模式的好处我们可以通过下图来简单
说明:
当然我这里可能总结还不完全,还请大家补充,我会更新这里面的内容。当然设计模式带来了这么多的好处,所以我们学习设计模式就显得比较必要了,也是从事软件开发及设计必须掌握的基本技能之一。
设计模式的简单分类:
当然这里可以简单的分为这3大类,下面我们在讲述的过程中将会分别讲解,当然我这里是以创建型模
式开始讲解,我想创建型模式也是大家项目中必备的吧?下面我就从创建型模式先来讲解。
本文将主要讲解创建型模式中的单例模式先来讲解,因为单例模式是最简单也是最容易理解的设计模式,上手快,易使用的设计模式。本文将从下面的流程来讲解单例模式,后面讲述的设计模式也将使用这样的方式。
1、什么是单例模式?
2、单例模式的应用场景。
3、举例说明单例模式的使用。
4、总结单例模式的用法。
a、开篇。
b、摘要。
c、本文大纲。
d、单例模式的简介。
e、相关应用场景分析。
f、本文总结。
g、系列进度。
h、下篇预告。
本章我们将来讲述下单例模式的使用,首先我们来看看单例模式的定义:
单例模式:是一种软件设计中常用的设计模式,主要是用来控制某个类必须在某个应用程序中只能有一个实例存在。
有时候我们需要确保整个系统中只有某个类的一个实例存在,这样有利于我们协调控制系统的行为。例如:我们在某个系统中使用了发送短信的这样的服务,那么我们可能希望通过单一的短信服务类的实例,而不是多个对象实例完成短信的发送服务。这时我们可以通过单例模式来完成。
上图简单描述了单例模式应用的位置。
我们看看单例模式的几种实现方式:
下面我们来举例说明下这2种方式的实现。
1、外部控制的方式
01 |
public class Instance |
02 |
{ |
03 |
private List<SendMessage> lists = new List<SendMessage>(); |
04 |
private SendMessage sendInstance; |
05 |
06 |
public SendMessage SInstance |
07 |
{ |
08 |
get |
09 |
{ |
10 |
return sendInstance; |
11 |
} |
12 |
} |
13 |
14 |
public void InstanceMethod() |
15 |
{ |
16 |
if (lists.Count == 0) |
17 |
{ |
18 |
sendInstance = new SendMessage(); |
19 |
lists.Add(sendInstance); |
20 |
} |
21 |
else |
22 |
{ |
23 |
sendInstance = lists[0]; |
24 |
} |
25 |
} |
26 |
} |
2、内部控制方式
01 |
public class Instance1 |
02 |
{ |
03 |
private static SendMessage sendInstance; |
04 |
private static object _lock = new object (); |
05 |
06 |
protected Instance1() |
07 |
{ |
08 |
} |
09 |
10 |
public static SendMessage SInstance |
11 |
{ |
12 |
get |
13 |
{ |
14 |
lock (_lock) |
15 |
{ |
16 |
if (sendInstance == null ) |
17 |
sendInstance = new SendMessage(); |
18 |
return sendInstance; |
19 |
} |
20 |
} |
21 |
} |
22 |
} |
这里有几点需要注意的地方,对于第二种方式有几个地方需要说明下,首先是要控制全局只有一个实例的类,请定义成静态实例,这样可以确保只有一个实例对象,其次,这个对象的构造函数请声明成保护类型的成员,这样可以屏蔽通过直接实例化的形式来访问。通过这样的形式,客户可以不需要知道某个单例实例对象的内部实现细节。一般情况下满足上面的2点需求就可以完成全局唯一访问入口的控制。当然可能在多线程的情况下采用这样的形式还会有一定的弊端,当然我们这里也简单的讲解下相应的控制方案。
方案如下:
1 |
public class CoolInstance |
2 |
{ |
3 |
private CoolInstance() |
4 |
{ |
5 |
} |
6 |
public static readonly CoolInstance Instance = new CoolInstance(); |
7 |
} |
看吧很简单吧,当然我们这里来简单解释下原理:
1、我们先把构造函数声明为私有的构造函数,这样我们能够屏蔽外部通过实例化的形式访问内部的成员函数。所有的成员函数的访问必须通过静态成员Instance
来完成访问。
2、这段代码通过定义公共、静态、只读的成员相当于在类被第一次使用时执行构造,由于是只读的,所以一旦构造后不允许修改,就不用担心不安全的问题。
相信对上面的介绍大家应该基本上知道单例模式的应用了,那么下面我们来看看项目中的实际应用场景及用法。
1、场景短信及邮件发送服务
那么我们将采用上面介绍的最“COOL”的方式来进行控制,提供发送短信及发送邮件的服务。
01 |
public class CoolInstance |
02 |
{ |
03 |
private CoolInstance() |
04 |
{ |
05 |
} |
06 |
07 |
public static readonly CoolInstance Instance = new CoolInstance(); |
08 |
09 |
/// <summary> |
10 |
/// 发送手机短信 |
11 |
/// </summary> |
12 |
public bool SendMessage( string telNumber, string content) |
13 |
{ |
14 |
return true ; |
15 |
} |
16 |
17 |
/// <summary> |
18 |
/// 发送邮件 |
19 |
/// </summary> |
20 |
/// <param name="content"></param> |
21 |
/// <param name="toMail"></param> |
22 |
public bool SendMail( string content, string toMail) |
23 |
{ |
24 |
return true ; |
25 |
} |
26 |
} |
我们再来看看调用类中如何书写完成调用。例如我们有个订单类,当有人新下订单时,将给卖家发送短信提醒功能。
01 |
/// <summary> |
02 |
/// 订单业务 |
03 |
/// </summary> |
04 |
public class Order |
05 |
{ |
06 |
public int Save() |
07 |
{ |
08 |
//先是将订单的相关信息生成, |
09 |
this .InitOrderInfo(); |
10 |
11 |
//执行订单的持久化方法 |
12 |
int count= this .Add(); |
13 |
14 |
//发送短信 |
15 |
CoolInstance.Instance.SendMessage( string .Empty, string .Empty); |
16 |
//发送邮件 |
17 |
CoolInstance.Instance.SendMail( string .Empty, string .Empty); |
18 |
19 |
return count; |
20 |
} |
21 |
22 |
/// <summary> |
23 |
/// 初始化订单信息 |
24 |
/// </summary> |
25 |
private void InitOrderInfo() |
26 |
{ |
27 |
} |
28 |
29 |
/// <summary> |
30 |
/// 新增订单信息 |
31 |
/// </summary> |
32 |
/// <returns></returns> |
33 |
private int Add() |
34 |
{ |
35 |
return 0; |
36 |
} |
37 |
} |
这样我们就完成了短信发送服务及邮件发送服务的控制。主要还是根据自己的业务需要。
2、例如我们现在提供一个系统日志服务或者打印或者扫描的服务,我们希望全局只有一个访问入口,那么我们就可以通过这样的单例模式来实现这样的需求。
01 |
public class PrintHelper |
02 |
{ |
03 |
#region 构造函数 |
04 |
private PrintHelper() |
05 |
{ |
06 |
} |
07 |
08 |
public static readonly PrintHelper Instance = new PrintHelper(); |
09 |
#endregion |
10 |
11 |
#region 打印服务 |
12 |
13 |
/// <summary> |
14 |
/// 直接打印服务 |
15 |
/// </summary> |
16 |
/// <returns></returns> |
17 |
public bool Print() |
18 |
{ |
19 |
return true ; |
20 |
} |
21 |
22 |
/// <summary> |
23 |
/// 打印预览 |
24 |
/// </summary> |
25 |
/// <returns></returns> |
26 |
public bool PrintPreview() |
27 |
{ |
28 |
return true ; |
29 |
} |
30 |
31 |
#endregion |
32 |
} |
具体的调用类我就不写相应的代码,都和上面的形式类同,下面我们讲解下可能更特殊的需求,有时候我们可能需要更新我们创建的唯一实例,这时我们如何控制单例实例对象的更新呢,有时候可能我们有这样的需求。下面我们来看看如何实现这样的需求。
3、可更新单例对象的场景
首先我们先说下什么情况下会遇到这样的更新方式呢?例如我们想在单例模式的类的构造函数是带有一定参数的情形时:
01 |
public class UpdateHelper |
02 |
{ |
03 |
private string type = string .Empty; |
04 |
private static object _lock = new object (); |
05 |
private static UpdateHelper instance; |
06 |
private UpdateHelper( string valueType) |
07 |
{ |
08 |
type = valueType; |
09 |
} |
10 |
11 |
public static UpdateHelper Instance |
12 |
{ |
13 |
get |
14 |
{ |
15 |
lock (_lock) |
16 |
{ |
17 |
if (instance == null ) |
18 |
{ |
19 |
//如果这里有多个条件需求的话,可能写起来会比较复杂,那么有更好的方式来处理吗? |
20 |
instance = new UpdateHelper( "test!" ); |
21 |
} |
22 |
23 |
return instance; |
24 |
} |
25 |
} |
26 |
} |
27 |
} |
那么我们来分析几种办法,有没有更好的办法来处理呢?
1、首先我们不能手动实例化,所以我们没有办法动态传入构造函数参数,只能在类的内部指定这个参数,但是有时候我们需要动态的更新这个参数,那么这样的形式显然就没有办法实现。
2、通过属性的方式,来动态的设置属性的内容来完成输出参数的改变,但是这样的方式可能太过自由,无法满足单例模式的初衷。
3、接口方式,因为接口必须要靠类来实现,所以更不靠谱,可以不考虑这样的方式。
4、通过Attribute的方式来将信息动态的注入到构造函数中,但是怎么说这样的方式是不是太兴师动众了呢?毕竟单例模式本来就是很简单的。
5、通过配置文件,通过config文件配置节点的形式来动态的配置相关信息,实现更新实例对象内容的情况。
通过上面的5种情况的分析,那么通过2、4、5可以实现这个要求,但是对比相应的代价来说,5的方式是最灵活也是最符合单例模式本来的规范要求,相对来说
成本和代价也可以接收。
1 |
<? xml version = "1.0" encoding = "utf-8" ?> |
2 |
< configuration > |
3 |
< system.Web > |
4 |
< add key = "ssss" >value</ add > |
5 |
|
6 |
</ system.Web > |
7 |
</ configuration > |
那么我们上面的单力模型中的代码只需要稍微的变化下即可,请看如下代码:
01 |
public class UpdateHelper |
02 |
{ |
03 |
private string type = string .Empty; |
04 |
private static object _lock = new object (); |
05 |
private static UpdateHelper instance; |
06 |
private UpdateHelper( string valueType) |
07 |
{ |
08 |
type = valueType; |
09 |
} |
10 |
11 |
public static UpdateHelper Instance |
12 |
{ |
13 |
get |
14 |
{ |
15 |
lock (_lock) |
16 |
{ |
17 |
if (instance == null ) |
18 |
{ |
19 |
//如果这里有多个条件需求的话,可能写起来会比较复杂,那么有更好的方式来处理吗? |
20 |
instance = new UpdateHelper(System.Configuration.ConfigurationManager.AppSettings[ "ssss" ].ToString()); |
21 |
} |
22 |
23 |
return instance; |
24 |
} |
25 |
} |
26 |
} |
27 |
} |
我想到这里大家都对单例模式有个简单的认识了,本文的内容就讲到这里。我们来回顾下我们讲述的内容:
本文主要讲述了创建型模式中的单例模式,单例模式主要是用来控制系统中的某个类的实例的数量及全局的访问入口点。我们主要讲述了实现单例模式的方式,分为外部方式及内部方式,当然我们现在采用的方式都是内部方式,还讲述了线程安全的单例模式及带有参数的构造函数的情况,根据配置文件来实现参数值的动态配置的情况。希望本文的讲解能对不熟悉设计模式的同仁能够了解知道单例模式的应用,而对已熟知单例模式的同仁可以温故而知新,我会努力写好这个系列,当然我这里可能在大牛的面前可能是班门弄斧吧,不过我会继续努力,争取写出让大家一看就明白的设计模式系列。本文错误之处再所难免,还请大家批评之处,我会继续改进。
1、系统架构技能之设计模式-单件模式
2、系统架构技能之设计模式-工厂模式
3、系统架构技能之设计模式-抽象工厂模式
4、系统架构技能之设计模式-创建者模式
5、系统架构技能之设计模式-原型模式
1、系统架构技能之设计模式-组合模式
2、系统架构技能之设计模式-外观模式
3、系统架构技能之设计模式-适配器模式
4、系统架构技能之设计模式-桥模式
5、系统架构技能之设计模式-装饰模式
6、系统架构技能之设计模式-享元模式
7、系统架构技能之设计模式-代理模式
1、系统架构技能之设计模式-命令模式
2、系统架构技能之设计模式-观察者模式
3、系统架构技能之设计模式-策略模式
4、系统架构技能之设计模式-职责模式
5、系统架构技能之设计模式-模板模式
6、系统架构技能之设计模式-中介者模式
7、系统架构技能之设计模式-解释器模式
下篇我们将会介绍我们大家最熟知的工程模式,当然我会更多的结合实例来讲解每个设计模式的应用场景及具体的实例,来更清晰的描述什么情况下用什么模式,及每个模式之间的区别。大家的支持就是我书写的动力,希望大家多多支持我吧!