单例模式(Singleton Pattern)
单例模式(Singleton Pattern)
https://www.cnblogs.com/PatrickLiu/p/8250985.html
https://www.cnblogs.com/zhili/p/SingletonPatterm.html
单例模式(Singleton):保证一个类只有一个实例,并提供一个访问它的全局访问点
应用场景:一个无状态的类使用单例模式节省内存资源。
1 |
|
私有的实例构造器是屏蔽外界的调用,上面的单例模式的实现在单线程下确实是完美的,也很好的满足了我们单线程环境的需求。
单线程单例模式的几个要点:
1.Singleton模式中的实例构造器可以设置为protected以允许子类派生。
2.Singleton模式一般不要支持ICloneable接口,因为这可能会导致多个对象实例,与Singleton模式的初衷违背。
3.Singleton模式一般不要支持序列化,因为这也有可能导致多个对象实例,同样与Singleton模式的初衷违背。
4.Singletom模式只考虑到了对象创建的工作,没有考虑对象销毁的工作。为什么这样做呢,因为Net平台是支持垃圾回收的,所以我们一般没有必要对其进行销毁处理。
5.不能应对多线程环境:在多线程环境下,使用Singleton模式仍然有可能得到Singleton类的多个实例对象
原型模式
Prototype,原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型来创建新的对象。 应用场景:用new创建一个对象需要非常繁琐的数据准备或者权限
http://www.cnblogs.com/PatrickLiu/p/7640873.html
http://www.cnblogs.com/zhili/p/PrototypePattern.html
命令模式
Command,命令模式:将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化,对请求排队和记录请求日志,以及支持可撤销的操作。 应用场景:将命令者与执行者完全解耦。
http://www.cnblogs.com/zhili/p/CommandPattern.html
http://www.cnblogs.com/PatrickLiu/p/7873322.html
命令模式CSharp实现
之前一直在忙于工作上的事情,关于设计模式系列一直没更新,最近项目中发现,对于设计模式的了解是必不可少的,当然对于设计模式的应用那更是重要,可以说是否懂得应用设计模式在项目中是衡量一个程序员的技术水平,因为对于一个功能的实现,高级工程师和初级工程师一样都会实现,但是区别在于它们实现功能的可扩展和可维护性,也就是代码的是否“优美”、可读。但是,要更好地应用,首先就必须了解各种设计模式和其应用场景,所以我还是希望继续完成设计模式这个系列,希望通过这种总结的方式来加深自己设计模式的理解。
2.1 命令模式的定义
命令模式属于对象的行为型模式。命令模式是把一个操作或者行为抽象为一个对象中,通过对命令的抽象化来使得发出命令的责任和执行命令的责任分隔开。命令模式的实现可以提供命令的撤销和恢复功能。
2.2 命令模式的结构
既然,命令模式是实现把发出命令的责任和执行命令的责任分割开,然而中间必须有某个对象来帮助发出命令者来传达命令,使得执行命令的接收者可以收到命令并执行命令。例如,开学了,院领导说计算机学院要进行军训,计算机学院的学生要跑1000米,院领导的话也就相当于一个命令,他不可能直接传达给到学生,他必须让教官来发出命令,并监督学生执行该命令。在这个场景中,发出命令的责任是属于学院领导,院领导充当与命令发出者的角色,执行命令的责任是属于学生,学生充当于命令接收者的角色,而教官就充当于命令的发出者或命令请求者的角色,然而命令模式的精髓就在于把每个命令抽象为对象。从而命令模式的结构如下图所示:
从命令模式的结构图可以看出,它涉及到五个角色,它们分别是:
- 客户角色:发出一个具体的命令并确定其接受者。
- 命令角色:声明了一个给所有具体命令类实现的抽象接口
- 具体命令角色:定义了一个接受者和行为的弱耦合,负责调用接受者的相应方法。
- 请求者角色:负责调用命令对象执行命令。
- 接受者角色:负责具体行为的执行。
2.3 命令模式的实现
现在,让我们以上面的军训的例子来实现一个命令模式,在实现之前,可以参考下命令模式的结构图来分析下实现过程。
军训场景中,具体的命令即是学生跑1000米,这里学生是命令的接收者,教官是命令的请求者,院领导是命令的发出者,即客户端角色。要实现命令模式,则必须需要一个抽象命令角色来声明约定,这里以抽象类来来表示。命令的传达流程是:
命令的发出者必须知道具体的命令、接受者和传达命令的请求者,对应于程序也就是在客户端角色中需要实例化三个角色的实例对象了。
命令的请求者负责调用命令对象的方法来保证命令的执行,对应于程序也就是请求者对象需要有命令对象的成员,并在请求者对象的方法内执行命令。
具体命令就是跑1000米,这自然属于学生的责任,所以是具体命令角色的成员方法,而抽象命令类定义这个命令的抽象接口。
有了上面的分析之后,具体命令模式的实现代码如下所示:
1
2 // 教官,负责调用命令对象执行请求
3 public class Invoke 4 {
5 public Command _command; 6
7 public Invoke(Command command) 8 {
9 this._command = command; 10 } 11
12 public void ExecuteCommand() 13 { 14 _command.Action(); 15 } 16 } 17
18 // 命令抽象类
19 public abstract class Command 20 { 21 // 命令应该知道接收者是谁,所以有Receiver这个成员变量
22 protected Receiver _receiver; 23
24 public Command(Receiver receiver) 25 { 26 this._receiver = receiver; 27 } 28
29 // 命令执行方法
30 public abstract void Action(); 31 } 32
33 //
34 public class ConcreteCommand :Command 35 { 36 public ConcreteCommand(Receiver receiver) 37 : base(receiver) 38 { 39 } 40
41 public override void Action() 42 { 43 // 调用接收的方法,因为执行命令的是学生
44 _receiver.Run1000Meters(); 45 } 46 } 47
48 // 命令接收者——学生
49 public class Receiver 50 { 51 public void Run1000Meters() 52 { 53 Console.WriteLine(“跑1000米”); 54 } 55 } 56
57 // 院领导
58 class Program 59 { 60 static void Main(string[] args) 61 { 62 // 初始化Receiver、Invoke和Command
63 Receiver r = new Receiver(); 64 Command c = new ConcreteCommand(r); 65 Invoke i = new Invoke(c); 66
67 // 院领导发出命令
68 i.ExecuteCommand(); 69 } 70 }
在ASP.NET的MVC模式中,有一种叫Front Controller的模式,它分为Handler和Command树两个部分,Handler处理所有公共的逻辑,接收HTTP Post或Get请求以及相关的参数并根据输入的参数选择正确的命令对象,然后将控制权传递到Command对象,由其完成后面的操作,这里面其实就是用到了Command模式。
Front Controller 的处理程序部分结构图
Front Controller的命令部分结构图
Handler 类负责处理各个 Web 请求,并将确定正确的 Command 对象这一职责委派给 CommandFactory 类。当 CommandFactory 返回 Command 对象后,Handler 将调用 Command 上的 Execute 方法来执行请求。具体的实现如下
1 // Handler类
2 public class Handler : IHttpHandler 3
4 {
5 public void ProcessRequest(HttpContext context) 6
7 {
8
9 Command command = CommandFactory.Make(context.Request.Params); 10
11 command.Execute(context);
12
13 }
14
15 public bool IsReusable 16
17 {
18 get
19
20 {
21 return true;
22 }
23 }
24 }
25
26 Command接口:
27 ///
28 /// Command 29 ///
30 public interface Command 31
32 {
33 void Execute(HttpContext context); 34 }
35
36 CommandFactory类:
37 ///
38 /// CommandFactory 39 ///
40 public class CommandFactory 41
42 {
43 public static Command Make(NameValueCollection parms) 44
45 {
46
47 string requestParm = parms[“requestParm”];
48
49 Command command = null;
50
51 //根据输入参数得到不同的Command对象
52
53 switch (requestParm) 54
55 {
56 case “1”:
57
58 command = new FirstPortal(); 59
60 break;
61
62 case “2”:
63
64 command = new SecondPortal(); 65
66 break;
67
68 default:
69
70 command = new FirstPortal(); 71
72 break;
73 }
74
75 return command; 76
77 }
78 }
79
80 RedirectCommand类:
81 public abstract class RedirectCommand : Command 82
83 {
84 //获得Web.Config中定义的key和url键值对,UrlMap类详见下载包中的代码
85
86 private UrlMap map = UrlMap.SoleInstance; 87
88 protected abstract void OnExecute(HttpContext context); 89
90 public void Execute(HttpContext context) 91
92 {
93 OnExecute(context);
94
95 //根据key和url键值对提交到具体处理的页面
96
97 string url = String.Format(“{0}?{1}”, map.Map[context.Request.Url.AbsolutePath], context.Request.Url.Query);
98
99 context.Server.Transfer(url); 100
101 } 102 } 103
104 FirstPortal类: 105 public class FirstPortal : RedirectCommand 106
107 { 108 protected override void OnExecute(HttpContext context) 109
110 { 111 //在输入参数中加入项portalId以便页面处理
112
113 context.Items[“portalId”] = “1”; 114
115 } 116 } 117
118 SecondPortal类: 119 public class SecondPortal : RedirectCommand 120
121 { 122 protected override void OnExecute(HttpContext context) 123
124 { 125 context.Items[“portalId”] = “2”; 126 } 127 }
View Code
在下面的情况下可以考虑使用命令模式:
- 系统需要支持命令的撤销(undo)。命令对象可以把状态存储起来,等到客户端需要撤销命令所产生的效果时,可以调用undo方法吧命令所产生的效果撤销掉。命令对象还可以提供redo方法,以供客户端在需要时,再重新实现命令效果。
- 系统需要在不同的时间指定请求、将请求排队。一个命令对象和原先的请求发出者可以有不同的生命周期。意思为:原来请求的发出者可能已经不存在了,而命令对象本身可能仍是活动的。这时命令的接受者可以在本地,也可以在网络的另一个地址。命令对象可以串行地传送到接受者上去。
- 如果一个系统要将系统中所有的数据消息更新到日志里,以便在系统崩溃时,可以根据日志里读回所有数据的更新命令,重新调用方法来一条一条地执行这些命令,从而恢复系统在崩溃前所做的数据更新。
- 系统需要使用命令模式作为“CallBack(回调)”在面向对象系统中的替代。Callback即是先将一个方法注册上,然后再以后调用该方法。
命令模式使得命令发出的一个和接收的一方实现低耦合,从而有以下的优点:
- 命令模式使得新的命令很容易被加入到系统里。
- 可以设计一个命令队列来实现对请求的Undo和Redo操作。
- 可以较容易地将命令写入日志。
- 可以把命令对象聚合在一起,合成为合成命令。合成命令式合成模式的应用。
命令模式的缺点:
- 使用命令模式可能会导致系统有过多的具体命令类。这会使得命令模式在这样的系统里变得不实际。
命令模式的实现要点在于把某个具体的命令抽象化为具体的命令类,并通过加入命令请求者角色来实现将命令发送者对命令执行者的依赖分割开,在上面军训的例子中,如果不使用命令模式的话,则命令的发送者将对命令接收者是强耦合的关系,实现代码如下:
1 // 院领导
2 class Program 3 {
4 static void Main(string[] args)
5 {
6 // 行为的请求者和行为的实现者之间呈现一种紧耦合关系
7 Receiver r = new Receiver(); 8
9 r.Run1000Meters(); 10 } 11 } 12
13 public class Receiver 14 { 15 // 操作
16 public void Run1000Meters() 17 { 18 Console.WriteLine(“跑1000米”); 19 } 20 }
到这里,本章的内容就介绍结束了,在下一章将继续为大家分享下我对迭代器模式的理解。
外观模式
Facade,外观模式:为子系统中的一组接口提供一致的界面,fa?ade提供了一高层接口,这个接口使得子系统更容易使用。
http://www.cnblogs.com/zhili/p/FacadePattern.html
http://www.cnblogs.com/PatrickLiu/p/7772184.html
外观模式CSharp实现
一、引言
在软件开发过程中,客户端程序经常会与复杂系统的内部子系统进行耦合,从而导致客户端程序随着子系统的变化而变化,然而为了将复杂系统的内部子系统与客户端之间的依赖解耦,从而就有了外观模式,也称作 ”门面“模式。下面就具体介绍下外观模式。
二、外观模式的详细介绍
2.1 定义
外观模式提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。使用外观模式时,我们创建了一个统一的类,用来包装子系统中一个或多个复杂的类,客户端可以直接通过外观类来调用内部子系统中方法,从而外观模式让客户和子系统之间避免了紧耦合。
2.2 外观模式实现
介绍了外观模式的定义之后,让我们具体看看外观模式的由来以及实现,下面与学校中一个选课系统为例来解释外观模式,例如在选课系统中,有注册课程子系统和通知子系统,在不使用外观模式的情况下,客户端必须同时保存注册课程子系统和通知子系统两个引用,如果后期这两个子系统发生改变时,此时客户端的调用代码也要随之改变,这样就没有很好的可扩展性,下面看看不使用外观模式下选课系统的实现方式和客户端调用代码:
///
/// 不使用外观模式的情况 /// 此时客户端与三个子系统都发送了耦合,使得客户端程序依赖与子系统 /// 为了解决这样的问题,我们可以使用外观模式来为所有子系统设计一个统一的接口 /// 客户端只需要调用外观类中的方法就可以了,简化了客户端的操作 /// 从而让客户和子系统之间避免了紧耦合 ///
class Client
{ static void Main(string[] args)
{
SubSystemA a = new SubSystemA();
SubSystemB b = new SubSystemB();
SubSystemC c = new SubSystemC();
a.MethodA();
b.MethodB();
c.MethodC();
Console.Read();
}
} // 子系统A
public class SubSystemA
{ public void MethodA()
{
Console.WriteLine(“执行子系统A中的方法A”);
}
} // 子系统B
public class SubSystemB
{ public void MethodB()
{
Console.WriteLine(“执行子系统B中的方法B”);
}
} // 子系统C
public class SubSystemC
{ public void MethodC()
{
Console.WriteLine(“执行子系统C中的方法C”);
}
}
然而外观模式可以解决我们上面所说的问题,下面具体看看使用外观模式的实现:
///
/// 以学生选课系统为例子演示外观模式的使用 /// 学生选课模块包括功能有: /// 验证选课的人数是否已满 /// 通知用户课程选择成功与否 /// 客户端代码 ///
class Student
{ private static RegistrationFacade facade = new RegistrationFacade(); static void Main(string[] args)
{ if (facade.RegisterCourse(“设计模式”, “Learning Hard”))
{
Console.WriteLine(“选课成功”);
} else {
Console.WriteLine(“选课失败”);
}
Console.Read();
}
} // 外观类
public class RegistrationFacade
{ private RegisterCourse registerCourse; private NotifyStudent notifyStu; public RegistrationFacade()
{
registerCourse \= new RegisterCourse();
notifyStu \= new NotifyStudent();
} public bool RegisterCourse(string courseName, string studentName)
{ if (!registerCourse.CheckAvailable(courseName))
{ return false;
} return notifyStu.Notify(studentName);
}
} #region 子系统
// 相当于子系统A
public class RegisterCourse
{ public bool CheckAvailable(string courseName)
{
Console.WriteLine("正在验证课程 {0}是否人数已满", courseName); return true;
}
} // 相当于子系统B
public class NotifyStudent
{ public bool Notify(string studentName)
{
Console.WriteLine("正在向{0}发生通知", studentName); return true;
}
} #endregion
使用了外观模式之后,客户端只依赖与外观类,从而将客户端与子系统的依赖解耦了,如果子系统发生改变,此时客户端的代码并不需要去改变。外观模式的实现核心主要是——由外观类去保存各个子系统的引用,实现由一个统一的外观类去包装多个子系统类,然而客户端只需要引用这个外观类,然后由外观类来调用各个子系统中的方法。然而这样的实现方式非常类似适配器模式,然而外观模式与适配器模式不同的是:适配器模式是将一个对象包装起来以改变其接口,而外观是将一群对象 ”包装“起来以简化其接口。****它们的意图是不一样的,适配器是将接口转换为不同接口,而外观模式是提供一个统一的接口来简化接口。
2.3 外观模式的结构
看完外观模式的实现之后,为了帮助理清外观模式中类之间的关系,下面给出上面实现代码中类图:
然而对于外观模式而言,是没有一个一般化的类图描述,下面演示一个外观模式的示意性对象图来加深大家对外观模式的理解:
在上面的对象图中有两个角色:
门面(Facade)角色:客户端调用这个角色的方法。该角色知道相关的一个或多个子系统的功能和责任,该角色会将从客户端发来的请求委派带相应的子系统中去。
子系统(subsystem)角色:可以同时包含一个或多个子系统。每个子系统都不是一个单独的类,而是一个类的集合。每个子系统都可以被客户端直接调用或被门面角色调用。对于子系统而言,门面仅仅是另外一个客户端,子系统并不知道门面的存在。
三、外观的优缺点
优点:
- 外观模式对客户屏蔽了子系统组件,从而简化了接口,减少了客户处理的对象数目并使子系统的使用更加简单。
- 外观模式实现了子系统与客户之间的松耦合关系,而子系统内部的功能组件是紧耦合的。松耦合使得子系统的组件变化不会影响到它的客户。
缺点:
- 如果增加新的子系统可能需要修改外观类或客户端的源代码,这样就违背了”开——闭原则“(不过这点也是不可避免)。
四、使用场景
在以下情况下可以考虑使用外观模式:
- 外一个复杂的子系统提供一个简单的接口
- 提供子系统的独立性
- 在层次化结构中,可以使用外观模式定义系统中每一层的入口。其中三层架构就是这样的一个例子。
五、总结
到这里外观模式的介绍就结束了,外观模式,为子系统的一组接口提供一个统一的接口,该模式定义了一个高层接口,这一个高层接口使的子系统更加容易使用。并且外观模式可以解决层结构分离、降低系统耦合度和为新旧系统交互提供接口功能。
本文所有源码:设计模式之外观模式
工厂方法(Factory Method)
工厂方法模式CSharp实现
一、引言
在简单工厂模式中讲到简单工厂模式的缺点,有一点是——简单工厂模式系统难以扩展,一旦添加新产品就不得不修改简单工厂方法,这样就会造成简单工厂的实现逻辑过于复杂,然而本专题介绍的工厂方法模式可以解决简单工厂模式中存在的这个问题,下面就具体看看工厂模式是如何解决该问题的。
二、工厂方法模式的实现
工厂方法模式之所以可以解决简单工厂的模式,是因为它的实现把具体产品的创建推迟到子类中,此时工厂类不再负责所有产品的创建,而只是给出具体工厂必须实现的接口,这样工厂方法模式就可以允许系统不修改工厂类逻辑的情况下来添加新产品,这样也就克服了简单工厂模式中缺点。下面看下工厂模式的具体实现代码(这里还是以简单工厂模式中点菜的例子来实现):
namespace 设计模式之工厂方法模式
{ ///
/// 菜抽象类 ///
public abstract class Food
{ // 输出点了什么菜
public abstract void Print();
} ///
/// 西红柿炒鸡蛋这道菜 ///
public class TomatoScrambledEggs : Food
{ public override void Print()
{
Console.WriteLine(“西红柿炒蛋好了!”);
}
} ///
/// 土豆肉丝这道菜 ///
public class ShreddedPorkWithPotatoes : Food
{ public override void Print()
{
Console.WriteLine(“土豆肉丝好了”);
}
} ///
/// 抽象工厂类 ///
public abstract class Creator
{ // 工厂方法
public abstract Food CreateFoddFactory();
} ///
/// 西红柿炒蛋工厂类 ///
public class TomatoScrambledEggsFactory:Creator
{ ///
/// 负责创建西红柿炒蛋这道菜 ///
///
public override Food CreateFoddFactory()
{ return new TomatoScrambledEggs();
}
} ///
/// 土豆肉丝工厂类 ///
public class ShreddedPorkWithPotatoesFactory:Creator
{ ///
/// 负责创建土豆肉丝这道菜 ///
///
public override Food CreateFoddFactory()
{ return new ShreddedPorkWithPotatoes();
}
} ///
/// 客户端调用 ///
class Client
{ static void Main(string[] args)
{ // 初始化做菜的两个工厂()
Creator shreddedPorkWithPotatoesFactory = new ShreddedPorkWithPotatoesFactory();
Creator tomatoScrambledEggsFactory = new TomatoScrambledEggsFactory(); // 开始做西红柿炒蛋
Food tomatoScrambleEggs = tomatoScrambledEggsFactory.CreateFoddFactory();
tomatoScrambleEggs.Print(); //开始做土豆肉丝
Food shreddedPorkWithPotatoes = shreddedPorkWithPotatoesFactory.CreateFoddFactory();
shreddedPorkWithPotatoes.Print();
Console.Read();
}
}
}
使用工厂方法实现的系统,如果系统需要添加新产品时,我们可以利用多态性来完成系统的扩展,对于抽象工厂类和具体工厂中的代码都不需要做任何改动。例如,我们我们还想点一个“肉末茄子”,此时我们只需要定义一个肉末茄子具体工厂类和肉末茄子类就可以。而不用像简单工厂模式中那样去修改工厂类中的实现(具体指添加case语句)。具体代码为:
///
/// 肉末茄子这道菜 ///
public class MincedMeatEggplant : Food
{ ///
/// 重写抽象类中的方法 ///
public override void Print()
{
Console.WriteLine(“肉末茄子好了”);
}
} ///
/// 肉末茄子工厂类,负责创建肉末茄子这道菜 ///
public class MincedMeatEggplantFactory : Creator
{ ///
/// 负责创建肉末茄子这道菜 ///
///
public override Food CreateFoddFactory()
{ return new MincedMeatEggplant();
}
} ///
/// 客户端调用 ///
class Client
{ static void Main(string[] args)
{ // 如果客户又想点肉末茄子了 // 再另外初始化一个肉末茄子工厂
Creator minceMeatEggplantFactor = new MincedMeatEggplantFactory(); // 利用肉末茄子工厂来创建肉末茄子这道菜
Food minceMeatEggplant = minceMeatEggplantFactor.CreateFoddFactory();
minceMeatEggplant.Print();
Console.Read();
}
}
三、工厂方法模式的UML图
讲解完工厂模式的具体实现之后,让我们看下工厂模式中各类之间的UML图:
从UML图可以看出,在工厂方法模式中,工厂类与具体产品类具有平行的等级结构,它们之间是一一对应的。针对UML图的解释如下:
Creator类:充当抽象工厂角色,任何具体工厂都必须继承该抽象类
TomatoScrambledEggsFactory和ShreddedPorkWithPotatoesFactory类:充当具体工厂角色,用来创建具体产品
Food类:充当抽象产品角色,具体产品的抽象类。任何具体产品都应该继承该类
TomatoScrambledEggs和ShreddedPorkWithPotatoes类:充当具体产品角色,实现抽象产品类对定义的抽象方法,由具体工厂类创建,它们之间有一一对应的关系。
四、.NET中实现了工厂方法的类
.NET 类库中也有很多实现了工厂方法的类,例如Asp.net中,处理程序对象是具体用来处理请求,当我们请求一个*.aspx的文件时,此时会映射到System.Web.UI.PageHandlerFactory类上进行处理,而对*.ashx的请求将映射到System.Web.UI.SimpleHandlerFactory类中(这两个类都是继承于IHttpHandlerFactory接口的),关于这点说明我们可以在“C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\Web.Config”文件中找到相关定义,具体定义如下:
下面我们就具体看下工厂方法模式在Asp.net中是如何实现的,如果对一个Index.aspx页面发出请求时,将会调用PageHandlerFactory中GetHandler方法来创建一个Index.aspx对象,它们之间的类图关系如下:
五、总结
工厂方法模式通过面向对象编程中的多态性来将对象的创建延迟到具体工厂中,从而解决了简单工厂模式中存在的问题,也很好地符合了开放封闭原则(即对扩展开发,对修改封闭)。