0%

在软件开发过程中,应用程序中的有些对象可能会根据不同的情况做出不同的行为,我们把这种对象称为有状态的对象,而把影响对象行为的一个或多个动态变化的属性称为状态。当有状态的对象与外部事件产生互动时,其内部状态会发生改变,从而使得其行为也随之发生改变。

对这种有状态的对象编程,传统的解决方案是:将这些所有可能发生的情况全都考虑到,然后使用 if-else 语句来做状态判断,再进行不同情况的处理。但当对象的状态很多时,程序会变得很复杂。而且增加新的状态要添加新的 if-else 语句,这违背了“开闭原则”,不利于程序的扩展。

以上问题如果采用“状态模式”就能很好地得到解决。状态模式的解决思想是:当控制一个对象状态转换的条件表达式过于复杂时,把相关“判断逻辑”提取出来,放到一系列的状态类当中,这样可以把原来复杂的逻辑判断简单化

状态(State)模式的定义:对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。

状态模式是一种对象行为型模式,其主要优点如下:

  • 状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”
  • 减少对象间的相互依赖,将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。
  • 有利于程序的扩展,通过定义新的子类很容易地增加新的状态和转换。

状态模式的主要缺点如下:

  • 状态模式的使用必然会增加系统的类与对象的个数
  • 状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。

状态模式把受环境改变的对象行为包装在不同的状态对象里,其意图是让一个对象在其内部状态改变的时候,其行为也随之改变。

模式的结构#

状态模式包含以下主要角色:

  • 环境(Context)角色:也称为上下文,它定义了客户感兴趣的接口,维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理
  • 抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为
  • 具体状态(Concrete State)角色:实现抽象状态所对应的行为

其结构图如图所示:

模式的实现#

状态模式的实现代码如下:

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
class Program
{
static void Main(string[] args)
{
Context context=new Context();
context.Handle();
context.Handle();
context.Handle();
context.Handle();
Console.Read();
}
}


public class Context
{
private State state;

public Context()
{
this.state=new ConcreteStateA();
}

public void SetState(State state)
{
this.state=state;
}

public State GetState()
{
return(state);
}

public void Handle()
{
state.Handle(this);
}
}


public abstract class State
{
public abstract void Handle(Context context);
}


public class ConcreteStateA : State
{
public override void Handle(Context context)
{
Console.WriteLine("当前状态是 A.");
context.SetState(new ConcreteStateB());
}
}


public class ConcreteStateB : State
{
public override void Handle(Context context)
{
Console.WriteLine("当前状态是 B.");
context.SetState(new ConcreteStateA());
}
}

程序运行结果如下:

1
2
3
4
当前状态是 A.
当前状态是 B.
当前状态是 A.
当前状态是 B.

通常在以下情况下可以考虑使用状态模式:

  • 一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式。
  • 一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时

在有些情况下,可能有多个环境对象需要共享一组状态,这时需要引入享元模式,将这些具体状态对象放在集合中供程序共享,其结构图如图所示:

分析:共享状态模式的不同之处是在环境类中增加了一个 HashMap 来保存相关状态,当需要某种状态时可以从中获取,其程序代码如下:

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
class Program
{
static void Main(string[] args)
{
ShareContext context=new ShareContext();
context.Handle();
context.Handle();
context.Handle();
context.Handle();
Console.Read();
}
}


public class ShareContext
{
private ShareState state;
private Dictionary<String, ShareState> stateDic=new Dictionary<String, ShareState>();
public ShareContext()
{
state=new ConcreteState1();
stateDic.Add("1", state);
state=new ConcreteState2();
stateDic.Add("2", state);
state=GetState("1");
}

public void SetState(ShareState state)
{
this.state=state;
}

public ShareState GetState(String key)
{
ShareState s=stateDic[key];
return s;
}

public void Handle()
{
state.Handle(this);
}
}


public abstract class ShareState
{
public abstract void Handle(ShareContext context);
}


public class ConcreteState1 : ShareState
{
public override void Handle(ShareContext context)
{
Console.WriteLine("当前状态是: 状态1");
context.SetState(context.GetState("2"));
}
}


public class ConcreteState2 : ShareState
{
public override void Handle(ShareContext context)
{
Console.WriteLine("当前状态是: 状态2");
context.SetState(context.GetState("1"));
}
}

在软件开发中常常遇到实现某种目标存在多种策略可供选择的情况,当实现某一个功能存在多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能,如数据排序策略有冒泡排序、选择排序、插入排序、二叉树排序等。如果使用多重条件转移语句实现(即硬编码),不但使条件语句变得很复杂,而且增加、删除或更换算法要修改原代码,不易维护,违背开闭原则。如果采用策略模式就能很好解决该问题。

策略(Strategy)模式的定义:该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。

策略模式的主要优点如下:

  • 多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句
  • 策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码
  • 策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的。
  • 策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。
  • 策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离

其主要缺点如下:

  • 客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类。
  • 策略模式造成很多的策略类

策略模式是准备一组算法,并将这组算法封装到一系列的策略类里面,作为一个抽象策略类的子类。策略模式的重心不是如何实现算法,而是如何组织这些算法,从而让程序结构更加灵活,具有更好的维护性和扩展性

模式的结构#

策略模式的主要角色如下:

  • 抽象策略(Strategy)类:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现
  • 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现
  • 环境(Context)类:持有一个策略类的引用,最终给客户端调用

其结构图如图所示:

模式的实现#

策略模式的实现代码如下:

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
class Program
{
static void Main(string[] args)
{
Context c=new Context();
IStrategy s=new ConcreteStrategyA();
c.SetStrategy(s);
c.StrategyMethod();
Console.WriteLine("-----------------");
s=new ConcreteStrategyB();
c.SetStrategy(s);
c.StrategyMethod();
Console.Read();
}
}


public interface IStrategy
{
void StrategyMethod();
}


public class ConcreteStrategyA : IStrategy
{
public void StrategyMethod()
{
Console.WriteLine("具体策略A的策略方法被访问!");
}
}


public class ConcreteStrategyB : IStrategy
{
public void StrategyMethod()
{
Console.WriteLine("具体策略B的策略方法被访问!");
}
}


public class Context
{
private IStrategy strategy;
public IStrategy GetStrategy()
{
return strategy;
}
public void SetStrategy(IStrategy strategy)
{
this.strategy=strategy;
}
public void StrategyMethod()
{
strategy.StrategyMethod();
}
}

程序运行结果如下:

1
2
3
具体策略A的策略方法被访问!
-----------------
具体策略B的策略方法被访问!

策略模式在很多地方用到,如 Java SE 中的容器布局管理就是一个典型的实例,Java SE 中的每个容器都存在多种布局供用户选择。在程序设计中,通常在以下几种情况中使用策略模式较多:

  • 一个系统需要动态地在几种算法中选择一种时,可将每个算法封装到策略类中。
  • 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,可将每个条件分支移入它们各自的策略类中以代替这些条件语句。
  • 系统中各算法彼此完全独立,且要求对客户隐藏具体算法的实现细节时。
  • 系统要求使用算法的客户不应该知道其操作的数据时,可使用策略模式来隐藏与算法相关的数据结构。
  • 多个类只区别在表现行为不同,可以使用策略模式,在运行时动态选择具体要执行的行为。

在一个使用策略模式的系统中,当存在的策略很多时,客户端管理所有策略算法将变得很复杂,如果在环境类中使用策略工厂模式来管理这些策略类将大大减少客户端的工作复杂度,其结构图如图所示:

在现实世界中,许多对象并不是独立存在的,其中一个对象的行为发生改变可能会导致一个或者多个其他对象的行为也发生改变。例如,某种商品的物价上涨时会导致部分商家高兴,而消费者伤心。

在软件世界也是这样,例如,事件模型中的事件源与事件处理者。所有这些,如果用观察者模式来实现就非常方便。

观察者(Observer)模式的定义:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。

观察者模式是一种对象行为型模式,其主要优点如下:

  • 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系
  • 目标与观察者之间建立了一套触发机制

它的主要缺点如下:

  • 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用
  • 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。

实现观察者模式时要注意具体目标对象和具体观察者对象之间不能直接调用,否则将使两者之间紧密耦合起来,这违反了面向对象的设计原则。

模式的结构#

观察者模式的主要角色如下:

  • 抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法
  • 具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
  • 抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
  • 具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。

观察者模式的结构图如图所示:

模式的实现#

观察者模式的实现代码如下:

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
class Program
{
static void Main(string[] args)
{
Subject subject=new ConcreteSubject();
IObserver obs1=new ConcreteObserver1();
IObserver obs2=new ConcreteObserver2();
subject.Add(obs1);
subject.Add(obs2);
subject.NotifyObserver();
Console.Read();
}
}


public abstract class Subject
{
protected List<IObserver> observers=new List<IObserver>();

public void Add(IObserver observer)
{
observers.Add(observer);
}

public void Remove(IObserver observer)
{
observers.Remove(observer);
}
public abstract void NotifyObserver();
}


public class ConcreteSubject : Subject
{
public override void NotifyObserver()
{
Console.WriteLine("具体目标发生改变...");
Console.WriteLine("--------------");

foreach (var obs in observers)
{
obs.Response();
}
}
}


public interface IObserver
{
void Response();
}


public class ConcreteObserver1 : IObserver
{
public void Response()
{
Console.WriteLine("具体观察者1作出反应!");
}
}


public class ConcreteObserver2 : IObserver
{
public void Response()
{
Console.WriteLine("具体观察者2作出反应!");
}
}

程序运行结果如下:

1
2
3
4
具体目标发生改变...
--------------
具体观察者1作出反应!
具体观察者2作出反应!

通过前面的分析与应用实例可知观察者模式适合以下几种情形:

  • 对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。
  • 当一个抽象模型有两个方面,其中一个方面依赖于另一方面时,可将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。

在.net环境下,其运行时库为开发者提供了IObservable和 IObserver接口,用于实现观察者模式软件设计。另外,ObservableCollection 类表示一个动态数据集合,它可在添加、删除项目或刷新整个列表时提供通知。

  • 提供者或主题,是将通知发送给观察者的对象。 提供程序是实现IObservable 接口的类或结构。 提供者必须实现单个方法IObservable .Subscribe,该方法由希望从提供者接收通知的观察者调用
  • 观察者,即从提供程序接收通知的对象。 观察者是实现 IObserver 接口的类或结构。 观察者必须实现以下三个方法,这三个方法均由提供程序调用
    IObserver.OnNext,它向观察者提供新信息或当前信息。
    IObserver.OnError,它通知观察者已发生错误。
    IObserver.OnCompleted,它指示提供程序已完成发送通知。
  • 允许提供程序跟踪观察者的一种机制。 通常情况下,提供程序使用容器对象(如 List 对象)来保存对已订阅通知的 IObserver 实现的引用。 将存储容器用于此目的使提供程序能够处理零到无限数量的观察者。 未定义观察者接收通知的顺序;提供程序可以随意使用任何方法来确定顺序
  • IDisposable 实现,它使提供程序在能够通知完成时删除观察者。 观察者从 Subscribe 方法接收对 IDisposable 实现的引用,因此它们还可以调用 IDisposable.Dispose 方法,以便在提供程序已完成发送通知之前取消订阅
  • 包含提供程序发送到其观察者的数据的对象。 此对象的类型对应 IObservable 和 IObserver 接口的泛型类型参数。 尽管此对象可与 IObservable 实现相同,但通常情况下,它是一个单独的类型

注:在 Java 中,通过 java.util.Observable 类和 java.util.Observer 接口定义了观察者模式,只要实现它们的子类就可以编写观察者模式实例。

下面的示例演示观察者设计模式,实现定位系统实时通知当前经纬度坐标,代码如下:

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
class Program
{
static void Main(string[] args)
{

LocationTracker provider = new LocationTracker();
LocationReporter reporter1 = new LocationReporter("FixedGPS");
reporter1.Subscribe(provider);
LocationReporter reporter2 = new LocationReporter("MobileGPS");
reporter2.Subscribe(provider);

provider.TrackLocation(new Location(47.6456, -122.1312));
reporter1.Unsubscribe();
provider.TrackLocation(new Location(47.6677, -122.1199));
provider.TrackLocation(null);
provider.EndTransmission();

Console.Read();
}
}




public struct Location
{
double lat, lon;

public Location(double latitude, double longitude)
{
this.lat = latitude;
this.lon = longitude;
}




public double Latitude
{ get { return this.lat; } }



public double Longitude
{ get { return this.lon; } }
}




public class LocationReporter : IObserver<Location>
{
private IDisposable unsubscriber;
private string instName;

public LocationReporter(string name)
{
this.instName = name;
}

public string Name
{ get { return this.instName; } }





public virtual void Subscribe(IObservable<Location> provider)
{
if (provider != null)
unsubscriber = provider.Subscribe(this);
}

public virtual void OnCompleted()
{
Console.WriteLine("位置跟踪器已将数据传输到 {0}", this.Name);
this.Unsubscribe();
}

public virtual void OnError(Exception e)
{
Console.WriteLine("{0}: 无法确定位置", this.Name);
}

public virtual void OnNext(Location value)
{
Console.WriteLine("{2}: 当前位置是 {0}, {1}", value.Latitude, value.Longitude, this.Name);
}




public virtual void Unsubscribe()
{
unsubscriber.Dispose();
}
}




public class LocationTracker : IObservable<Location>
{
public LocationTracker()
{
observers = new List<IObserver<Location>>();
}

private List<IObserver<Location>> observers;






public IDisposable Subscribe(IObserver<Location> observer)
{
if (!observers.Contains(observer))
observers.Add(observer);
return new Unsubscriber(observers, observer);
}




private class Unsubscriber : IDisposable
{
private List<IObserver<Location>> _observers;
private IObserver<Location> _observer;

public Unsubscriber(List<IObserver<Location>> observers, IObserver<Location> observer)
{
this._observers = observers;
this._observer = observer;
}

public void Dispose()
{
if (_observer != null && _observers.Contains(_observer))
_observers.Remove(_observer);
}
}

public void TrackLocation(Nullable<Location> loc)
{
foreach (var observer in observers)
{
if (!loc.HasValue)
observer.OnError(new LocationUnknownException());
else
observer.OnNext(loc.Value);
}
}

public void EndTransmission()
{
foreach (var observer in observers.ToArray())
{
if (observers.Contains(observer))
observer.OnCompleted();
}
observers.Clear();
}
}




public class LocationUnknownException : Exception
{
internal LocationUnknownException()
{ }
}

程序运行结果如下:

1
2
3
4
5
FixedGPS:当前位置是47.6456,-122.1312
MobileGPS:当前位置是47.6456,-122.1312
MobileGPS:当前位置是47.6677,-122.1199
MobileGPS:无法确定位置位置
跟踪器已将数据传输到MobileGPS

参考资料:
观察者设计模式——MSDN
ObservableCollection 类——MSDN
IObservable 接口——MSDN
IObserver 接口——MSDN

在软件开发中,会遇到有些问题多次重复出现,而且有一定的相似性和规律性。如果将它们归纳成一种简单的语言,那么这些问题实例将是该语言的一些句子,这样就可以用“编译原理”中的解释器模式来实现了。

虽然使用解释器模式的实例不是很多,但对于满足以上特点,且对运行效率要求不是很高的应用实例,如果用解释器模式来实现,其效果是非常好的,本文将介绍其工作原理与使用方法。

解释器(Interpreter)模式的定义:给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。

这里提到的文法和句子的概念同编译原理中的描述相同,“文法”指语言的语法规则,而“句子”是语言集中的元素。例如,汉语中的句子有很多,“我是中国人”是其中的一个句子,可以用一棵语法树来直观地描述语言中的句子。

解释器模式是一种类行为型模式,其主要优点如下:

  • 扩展性好:由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
  • 容易实现:在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。

解释器模式的主要缺点如下:

  • 执行效率较低:解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,且代码的调试过程也比较麻烦。
  • 会引起类膨胀:解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护。
  • 可应用的场景比较少:在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。

解释器模式常用于对简单语言的编译或分析实例中,为了掌握好它的结构与实现,必须先了解编译原理中的“文法、句子、语法树”等相关概念。

文法:文法是用于描述语言的语法结构的形式规则。没有规矩不成方圆,任何事情都要有规则,语言也一样,不管它是机器语言还是自然语言,都有它自己的文法规则。例如,中文中的“句子”的文法如下:

1
2
3
4
5
6
7
〈句子〉::=〈主语〉〈谓语〉〈宾语〉
〈主语〉::=〈代词〉|〈名词〉
〈谓语〉::=〈动词〉
〈宾语〉::=〈代词〉|〈名词〉
〈代词〉你|我|他
〈名词〉7大学生I筱霞I英语
〈动词〉::=是|学习

注:这里的符号“::=”表示“定义为”的意思,用“〈”和“〉”括住的是非终结符,没有括住的是终结符。

句子:句子是语言的基本单位,是语言集中的一个元素,它由终结符构成,能由“文法”推导出。例如,上述文法可以推出“我是大学生”,所以它是句子。

语法树:语法树是句子结构的一种树型表示,它代表了句子的推导结果,它有利于理解句子语法结构的层次。下图所示是“我是大学生”的语法树:

有了以上基础知识,现在来介绍解释器模式的结构就简单了。解释器模式的结构与组合模式相似,不过其包含的组成元素比组合模式多,而且组合模式是对象结构型模式,而解释器模式是类行为型模式。

模式的结构#

解释器模式包含以下主要角色:

  • 抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 Interpret()。
  • 终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
  • 非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
  • 环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
  • 客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。

解释器模式的结构图如图所示:

模式的实现#

解释器模式实现的关键是定义文法规则、设计终结符类与非终结符类、画出结构图,必要时构建语法树,其代码结构如下:

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
38
39

public interface IAbstractExpression
{
Object Interpret(String info);
}


public class TerminalExpression : IAbstractExpression
{
public Object Interpret(String info)
{

}
}


public class NonterminalExpression : IAbstractExpression
{
private IAbstractExpression exp1;
private IAbstractExpression exp2;
public Object Interpret(String info)
{

}
}


public class Context
{
private IAbstractExpression exp;
public Context()
{

}
public void Operation(String info)
{

}
}

前面介绍了解释器模式的结构与特点,下面分析它的应用场景:

  • 当语言的文法较为简单,且执行效率不是关键问题时。
  • 当问题重复出现,且可以用一种简单的语言来进行表达时。
  • 当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候,如 XML 文档解释。

注意:解释器模式在实际的软件开发中使用比较少,因为它会引起效率、性能以及维护等问题。如果碰到对表达式的解释,在 C# 中可以用 Expression类 或 Flee 等来设计

在项目开发中,如果要对数据表达式进行分析与计算,无须再用解释器模式进行设计了,C# 提供了 Expression 表达式树,也可以使用 Flee 等开源类库,它们可以解释一些复杂的文法,功能强大,使用简单。

Flee是.NET框架的表达式解析器和评估器,它使用自定义编译器,强类型表达式语言和轻量级代码生成器将表达式直接编译为IL。
github链接:Flee
使用说明:https://github.com/mparlak/Flee/wiki

使用NuGet安装Flee,创建和评估表达式的示例代码如下:

1
2
3
4
5
6
7
8
9
10

CultureInfo ci = new CultureInfo("fr-FR");

ExpressionContext context = new ExpressionContext();
context.ParseCulture = ci
context.Imports.AddType(typeof(Math));


IDynamicExpression e = context.CompileDynamic("round(100,75; 1)");
object result = e.Evaluate();

在现实生活中,有些集合对象中存在多种不同的元素,且每种元素也存在多种不同的访问者和处理方式。例如,公园中存在多个景点,也存在多个游客,不同的游客对同一个景点的评价可能不同。

这些被处理的数据元素相对稳定而访问方式多种多样的数据结构,如果用“访问者模式”来处理比较方便。

访问者模式能把处理方法从数据结构中分离出来,并可以根据需要增加新的处理方法,且不用修改原来的程序代码与数据结构,这提高了程序的扩展性和灵活性。

访问者(Visitor)模式的定义:将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式

访问者(Visitor)模式是一种对象行为型模式,其主要优点如下:

  • 扩展性好:能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
  • 复用性好:可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。
  • 灵活性好:访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构。
  • 符合单一职责原则:访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一。

访问者(Visitor)模式的主要缺点如下:

  • 增加新的元素类很困难:在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。
  • 破坏封装:访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性。
  • 违反了依赖倒置原则:访问者模式依赖了具体类,而没有依赖抽象类。

访问者(Visitor)模式实现的关键是如何将作用于元素的操作分离出来封装成独立的类,其基本结构与实现方法如下。

模式的结构#

访问者模式包含以下主要角色:

  • 抽象访问者(Visitor)角色:定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 Visit() ,该操作中的参数类型标识了被访问的具体元素。
  • 具体访问者(ConcreteVisitor)角色:实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。
  • 抽象元素(Element)角色:声明一个包含接受操作 Accept() 的接口,被接受的访问者对象作为 Accept() 方法的参数。
  • 具体元素(ConcreteElement)角色:实现抽象元素角色提供的 Accept() 操作,其方法体通常都是 visitor.Visit(this) ,另外具体元素中可能还包含本身业务逻辑的相关操作。
  • 对象结构(Object Structure)角色:是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。

其结构图如图所示:

模式的实现#

访问者模式的实现代码如下:

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
class Program
{
static void Main(string[] args)
{
ObjectStructure os=new ObjectStructure();
os.Add(new ConcreteElementA());
os.Add(new ConcreteElementB());
IVisitor visitor=new ConcreteVisitorA();
os.Accept(visitor);
Console.WriteLine("------------------------");
visitor=new ConcreteVisitorB();
os.Accept(visitor);

Console.Read();
}
}


public interface IVisitor
{
void Visit(ConcreteElementA element);
void Visit(ConcreteElementB element);
}


public class ConcreteVisitorA : IVisitor
{
public void Visit(ConcreteElementA element)
{
Console.WriteLine("具体访问者A访问-->"+element.OperationA());
}
public void Visit(ConcreteElementB element)
{
Console.WriteLine("具体访问者A访问-->"+element.OperationB());
}
}


public class ConcreteVisitorB : IVisitor
{
public void Visit(ConcreteElementA element)
{
Console.WriteLine("具体访问者B访问-->"+element.OperationA());
}
public void Visit(ConcreteElementB element)
{
Console.WriteLine("具体访问者B访问-->"+element.OperationB());
}
}


public interface IElement
{
void Accept(IVisitor visitor);
}


public class ConcreteElementA : IElement
{
public void Accept(IVisitor visitor)
{
visitor.Visit(this);
}
public String OperationA()
{
return "具体元素A的操作。";
}
}


public class ConcreteElementB : IElement
{
public void Accept(IVisitor visitor)
{
visitor.Visit(this);
}
public String OperationB()
{
return "具体元素B的操作。";
}
}


public class ObjectStructure
{
private List<IElement> list=new List<IElement>();
public void Accept(IVisitor visitor)
{
foreach (var item in list)
{
item.Accept(visitor);
}
}
public void Add(IElement element)
{
list.Add(element);
}
public void Remove(IElement element)
{
list.Remove(element);
}
}

程序的运行结果如下:

1
2
3
4
5
具体访问者A访问
具体访问者A访问

具体访问者B访问
具体访问者B访问

通常在以下情况可以考虑使用访问者(Visitor)模式:

  • 对象结构相对稳定,但其操作算法经常变化的程序。
  • 对象结构中的对象需要提供多种不同且不相关的操作,而且要避免让这些操作的变化影响对象的结构。
  • 对象结构包含很多类型的对象,希望对这些对象实施一些依赖于其具体类型的操作。

访问者(Visitor)模式是使用频率较高的一种设计模式,它常常同以下两种设计模式联用:

  • 与“迭代器模式”联用:因为访问者模式中的“对象结构”是一个包含元素角色的容器,当访问者遍历容器中的所有元素时,常常要用迭代器。
    注:现在一般的高级语言都自带的有迭代器,不需要自己实现。

  • 访问者(Visitor)模式同“组合模式”联用:因为访问者(Visitor)模式中的“元素对象”可能是叶子对象或者是容器对象,如果元素对象包含容器对象,就必须用到组合模式,其结构图如图所示:

在现实生活中,常常会出现这样的事例:一个请求有多个对象可以处理,但每个对象的处理条件或权限不同,如找领导出差报销等。
在计算机软硬件中也有相关例子,如总线网中数据报传送、异常处理。所有这些,如果用责任链模式都能很好解决。

责任链(Chain of Responsibility)模式的定义:为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链。当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。

注意:责任链模式也叫职责链模式

在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,所以责任链将请求的发送者和请求的处理者解耦了。

责任链模式是一种对象行为型模式,其主要优点如下:

  • 降低了对象之间的耦合度:该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
  • 增强了系统的可扩展性:可以根据需要增加新的请求处理类,满足开闭原则。
  • 增强了给对象指派职责的灵活性:当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
  • 责任链简化了对象之间的连接:每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
  • 责任分担:每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。

其主要缺点如下:

  • 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
  • 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
  • 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。

通常情况下,可以通过数据链表来实现职责链模式的数据结构。

模式的结构#

职责链模式主要包含以下角色:

  • 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
  • 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
  • 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。

其结构图如图所示:

客户端可按图所示设置责任链:

模式的实现#

职责链模式的实现代码如下:

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
class Program
{
static void Main(string[] args)
{

Handler handler1=new ConcreteHandler1();
Handler handler2=new ConcreteHandler2();
handler1.SetNext(handler2);

handler1.HandleRequest("two");
Console.Read();
}
}


public abstract class Handler
{
private Handler next;
public void SetNext(Handler next)
{
this.next=next;
}
public Handler GetNext()
{
return next;
}

public abstract void HandleRequest(String request);
}


public class ConcreteHandler1 : Handler
{
public override void HandleRequest(String request)
{
if(request.Equals("one"))
{
Console.WriteLine("具体处理者1负责处理该请求!");
}
else
{
if(GetNext()!=null)
{
GetNext().HandleRequest(request);
}
else
{
Console.WriteLine("没有人处理该请求!");
}
}
}
}


public class ConcreteHandler2 : Handler
{
public override void HandleRequest(String request)
{
if(request.Equals("two"))
{
Console.WriteLine("具体处理者2负责处理该请求!");
}
else
{
if(GetNext()!=null)
{
GetNext().HandleRequest(request);
}
else
{
Console.WriteLine("没有人处理该请求!");
}
}
}
}

程序运行结果如下:

1
具体处理者2负责处理该请求!

前边已经讲述了关于责任链模式的结构与特点,下面介绍其应用场景,责任链模式通常在以下几种情况使用:

  • 有多个对象可以处理一个请求,哪个对象处理该请求由运行时刻自动确定。
  • 可动态指定一组对象处理请求,或添加新的处理者。
  • 在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。

职责链模式存在以下两种情况:

  • 纯的职责链模式:一个请求必须被某一个处理者对象所接收,且一个具体处理者对某个请求的处理只能采用以下两种行为之一:自己处理(承担责任);把责任推给下家处理。
  • 不纯的职责链模式:允许出现某一个具体处理者对象在承担了请求的一部分责任后又将剩余的责任传给下家的情况,且一个请求可以最终不被任何接收端对象所接收。

在现实生活以及程序设计中,经常要访问一个聚合对象中的各个元素,如“数据结构”中的链表遍历,通常的做法是将链表的创建和遍历都放在同一个类中,但这种方式不利于程序的扩展,如果要更换遍历方法就必须修改程序源代码,这违背了 “开闭原则”。

既然将遍历方法封装在聚合类中不可取,那么聚合类中不提供遍历方法,将遍历方法由用户自己实现是否可行呢?答案是同样不可取,因为这种方式会存在两个缺点:

  • 暴露了聚合类的内部表示,使其数据不安全。
  • 增加了客户的负担。

“迭代器模式”能较好地克服以上缺点,它在客户访问类与聚合类之间插入一个迭代器,这分离了聚合对象与其遍历行为,对客户也隐藏了其内部细节,且满足“单一职责原则”和“开闭原则”。

迭代器(Iterator)模式的定义:提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
迭代器模式是一种对象行为型模式,其主要优点如下:

  • 访问一个聚合对象的内容而无须暴露它的内部表示。
  • 遍历任务交由迭代器完成,这简化了聚合类。
  • 它支持以不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历。
  • 增加新的聚合类和迭代器类都很方便,无须修改原有代码。
  • 封装性良好,为遍历不同的聚合结构提供一个统一的接口。

其主要缺点是:增加了类的个数,这在一定程度上增加了系统的复杂性。

迭代器模式是通过将聚合对象的遍历行为分离出来,抽象成迭代器类来实现的,其目的是在不暴露聚合对象的内部结构的情况下,让外部代码透明地访问聚合的内部数据。

模式的结构#

迭代器模式主要包含以下角色:

  • 抽象聚合(Aggregate)角色:定义存储、添加、删除聚合对象以及创建迭代器对象的接口。
  • 具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。
  • 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 HasNext()、First()、Next() 等方法。
  • 具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。

其结构图如图所示:

模式的实现#

迭代器模式的实现代码如下:

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
class Program
{
static void Main(string[] args)
{
IAggregate ag = new ConcreteAggregate();
ag.Add("中山大学");
ag.Add("华南理工");
ag.Add("韶关学院");
Console.WriteLine("聚合的内容有:");
IIterator it = ag.GetIterator();
while (it.HasNext())
{
Object ob = it.Next();
Console.WriteLine(ob.ToString());
}
Console.WriteLine("First:" + it.First().ToString());

Console.Read();
}
}


public interface IAggregate
{
void Add(Object obj);
void Remove(Object obj);
IIterator GetIterator();
}


public class ConcreteAggregate : IAggregate
{
private List<Object> list=new List<Object>();
public void Add(Object obj)
{
list.Add(obj);
}
public void Remove(Object obj)
{
list.Remove(obj);
}
public IIterator GetIterator()
{
return(new ConcreteIterator(list));
}
}


public interface IIterator
{
Object First();
Object Next();
bool HasNext();
}


public class ConcreteIterator : IIterator
{
private List<Object> list=null;
private int index=-1;
public ConcreteIterator(List<Object> list)
{
this.list=list;
}
public bool HasNext()
{
if(index<list.Count-1)
{
return true;
}
else
{
return false;
}
}
public Object First()
{
index=0;
Object obj=list[index];;
return obj;
}
public Object Next()
{
Object obj=null;
if(this.HasNext())
{
obj=list[++index];
}
return obj;
}
}

程序运行结果如下:

1
2
3
4
5
聚合的内容有:
中山大学
华南理工
韶关学院
First:中山大学

注:在C#中,推荐使用泛型代替Object以降低性能消耗。

前面介绍了关于迭代器模式的结构与特点,下面介绍其应用场景,迭代器模式通常在以下几种情况使用:

  • 当需要为聚合对象提供多种遍历方式时。
  • 当需要为遍历不同的聚合结构提供一个统一的接口时。
  • 当访问一个聚合对象的内容而无须暴露其内部细节的表示时。

由于聚合与迭代器的关系非常密切,所以大多数语言在实现聚合类时都提供了迭代器类,因此大数情况下使用语言中已有的聚合类的迭代器就已经够了。

迭代器模式常常与组合模式结合起来使用,在对组合模式中的容器构件进行访问时,经常将迭代器潜藏在组合模式的容器构成类中。当然,也可以构造一个外部迭代器来对容器构件进行访问,其结构图如图所示:

《大话设计模式》中提到了 24种设计模式:

简单工厂模式,策略模式、装饰模式、代理模式、工厂方法模式、原型模式、模板方法模式、外观模式、建造者模式、观察者模式、抽象工厂模式、状态模式、适配器模式、备忘录模式、组合模式、迭代器模式、单例模式、桥接模式、命令模式、职责链模式、中介者模式、享元模式、解释器模式、访问者模式。

按照类型,可分为3类:

1、 创建型模式:抽象工厂、建造者模式、工厂方法、原型模式、单例模式;

创建型模式抽象了实例化的过程。创建性模式隐藏了这些类的实例是如何被创建和放在一起,整个系统关于这些对象所知道的是由抽象类所定义的接口。这样,创建性模式在创建了什么、谁创建它、她是怎么被创建的、以及何时创建方面提供了灵活性。创建相应数目的原型并克隆她们通常比每次用适合的状态手工实例化该类更方便。

2、 结构型模式:适配器模式、桥接模式、组合模式、装饰者模式、外观模式、享元模式、代理模式;

3、 行为型模式:观察者模式、模板方法、命令模式、状态模式、职责链模式、解释器模式、中介者模式、访问者模式、策略模式、备忘录模式、迭代器模式。

4、 MVC模式:集观察者、组合、策略为一体,是多种模式的综合应用,算是一种架构模式。

下面按照【概念】+【原则】+【场景】+【优点】+【缺点】+【应用】分别简述一下24种设计模式:

抽象工厂模式(Abstract Factory) 提供一个创建一系列相关或互相依赖对象的接口,而无需指定它们具体的类。

原则:LSP 里氏替换原则

场景:创建不同的产品对象,客户端应使用不同的具体工厂。

优点

a)   改变具体工厂即可使用不同的产品配置,使改变一个应用的具体工厂变得很容易。

b)   让具体的创建实例过程与客户端分离,客户端通过抽象接口操作实例,产品的具体类名也被具体工厂的实现分离。

缺点:如果要新增方法,改动极大。

应用

      a)jdk中连接数据库的代码是典型的抽象工厂模式,每一种数据库只需提供一个统一的接口:Driver(工厂类),并实现其中的方法即可。不管是jdbc还是odbc都能够通过扩展产品线来达到连接自身数据库的方法。

      b)java.util.Collection 接口中定义了一个抽象的 iterator() 方法,该方法就是一个工厂方法。对于 iterator() 方法来说 Collection 就是一个抽象工厂

建造者模式(Builder) 【又名,生成器模式】:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

原则:依赖倒转原则

场景:如果需要将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式是当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时适用的模式。

优点:使得建造代码与表示代码分离。

缺点:1、增加代码量;2、Builder只是一个替代构造器的选择,不能直接用于降低非构造函数方法的参数数量。

应用:StringBuilder和StringBuffer的append()方法

工厂方法模式(Factory Method) 定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。

原则:开放封闭原则

场景:不改变工厂和产品体系,只是要扩展产品(变化)。

优点:是简单工厂模式的进一步抽象和推广,既保持了简单工厂模式的优点(工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类。对于客户端来说,去除了与具体产品的依赖),而且克服了简单工厂的缺点(违背了开放封闭原则)。

缺点:每增加一个产品,就需要增加一个产品工厂的类,增加了额外的开发。(用反射可以解决)。

应用

1.       Collection中的iterator方法;

2.       java.lang.Proxy#newProxyInstance()

3.       java.lang.Object#toString()

4.       java.lang.Class#newInstance()

5.       java.lang.reflect.Array#newInstance()

6.       java.lang.reflect.Constructor#newInstance()

7.       java.lang.Boolean#valueOf(String)

8.       java.lang.Class#forName()

原型模式(prototype) 【又名,生成器模式】:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

原则

场景:在初始化信息不发生变化的情况,用克隆进行拷贝。

优点:隐藏了对象创建的细节,大大提升了性能。不用重新初始化对象,而是动态的获得对象运行时的状态。

缺点:深复制 or 浅复制 。

应用:JDK中的Date类。

单例模式(Singleton) 保证一个类仅有一个实例,并提供一个访问它的全局访问点。

原则:封装

场景:通常,我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象,一个最好的办法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,而且它可以提供一个访问该实例的方法。

优点:对唯一实例的受控访问。

缺点:饿汉式/懒汉式  多线程同时访问时可能造成多个实例。

应用:java.lang.Runtime; GUI中也有一些(java.awt.Toolkit#getDefaultToolkit() java.awt.Desktop#getDesktop())

适配器模式(Adapter) 将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

在GoF的设计模式中,适配器有两种类型,类适配器模式和对象适配器模式。

a)   类适配器模式:通过多重继承对一个接口与另一个接口进行匹配,而C#,Java等语言都不支持多重继承,也就是一个类只有一个父类。

b)   Java一般都指的是 对象适配器模式

场景:适配器是为了复用一些现有的类。系统的数据和行为都正确,但是接口不符,这时采用适配器模式,使原有对象和新接口匹配。

优点:能够复用现存的类,客户端统一调用同一接口,更简单、直接、紧凑。

缺点:适配器模式有点儿“亡羊补牢”的感觉,设计阶段要避免使用。

应用:在Java jdk中,适配器模式使用场景很多,如集合包中Java.util.Arrays#asList()、IO包中java.io.InputStreamReader(InputStream)、java.io.OutputStreamWriter(OutputStream) 等

桥接模式(Bridge) 将抽象部分与它的实现部分分离,使它们都可以独立的变化。

原则:合成/聚合复用原则

场景:实现系统可能有多角度分类,每一种分类都有可能变化,那么就把这种多角度分离出来让它们独立变化,减少它们之间的耦合。

优点:减少各部分的耦合。 分离抽象和实现部分,更好的扩展性,可动态地切换实现、可减少子类的个数。

缺点:1、桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。 2、桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性

应用:Collections类中的sort()方法;AWT;JDBC数据库访问接口API;

组合模式(Composite) 将对象组合成树形结构以表示“部分-整体”的层次结构。

场景:需求中体现部分与整体层次结构时,以及希望用户可以忽略组合对象与单个对象的不同,统一使用组合结构中的所有对象时,就应该考虑使用组合模式了。

优点:组合模式让客户可以一致的使用组合结构和单个对象。

缺点:使设计变得更加抽象,对象的业务规则如果很复杂,则实现组合模式具有很大挑战性,而且不是所有的方法都与叶子对象子类都有关联。

应用:JDK中AWT包和Swing包的设计是基于组合模式,在这些界面包中为用户提供了大量的容器构件(如Container)和成员构件(如Checkbox、Button和TextComponent等),他们都是继承、关联自抽象组件类Component。

装饰模式(Decorator) 动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更灵活。

场景:装饰模式是为了已有功能动态地添加更多功能的一种方式,当系统需要新功能的时候,是向旧类中添加新的代码,这些新的代码通常装饰了原有类的核心职责或主要行为。装饰着模式把每个要装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象,当需要执行特殊行为时,客户代码就可以在运行时根据需要有选择的、按顺序地使用装饰功能包装对象。

优点:把类中的装饰功能从类中搬移出去,简化原有的类。有效的把类的核心职责和装饰功能区分开,去除相关类中重复的装饰逻辑。

缺点:利用装饰器模式,常常造成设计中有大量的小类,数量实在太多,可能会造成使用此API程序员的困扰。

应用:Java I/O使用装饰模式设计,JDK中还有很多类是使用装饰模式设计的,如:Reader类、Writer类、OutputStream类等。

外观模式(facade) 为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

原则:完美的体现了依赖倒转原则和迪米特法则。

场景

a)   设计阶段:需有意识的将不同的两个层分离。

b)   开发阶段:增加外观façade提供一个简单的接口,应对子类的重演和演化。

c)   维护期间:使用façade类,为遗留代码提供清晰简单的接口,让新系统与façade交互,façade与遗留代码交互所有复杂的工作。

优点:1、客户对子系统的使用变得简单了,减少了与子系统的关联对象,实现了子系统与客户之间的松耦合关系。 2、只是提供了一个访问子系统的统一入口,并不影响用户直接使用子系统类 3、降低了大型软件系统中的编译依赖性,并简化了系统在不同平台之间的移植过程。

缺点:1、不能很好地限制客户使用子系统类,如果对客户访问子系统类做太多的限制则减少了可变性和灵活性   2、在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。

享元模式(Flyweight) 运用共享技术有效的支持大量细粒度的对象。

场景:如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大存储开销时就应该考虑使用享元模式;还有就是对象大多数状态都可为外部状态,如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象,此时可以考虑使用享元模式。

优点:享元模式可以避免大量非常相似类的开销。程序中,大量细粒度的类实例来表示数据,如果它们除了几个参数外基本相同,那么把它们转移到类实例的外面,在方法调用时将它们传递进来,就可以通过共享大幅度减少单个实例的数目。

缺点:1、由于享元模式需要区分外部状态和内部状态,使得应用程序在某种程度上来说更加复杂化了。2、为了使对象可以共享,享元模式需要将享元对象的状态外部化,而读取外部状态使得运行时间变长。

应用:String 类。

代理模式(proxy) 为其他对象提供一种代理以控制对这个对象的访问。

原则:代理模式就是在访问对象时引入一定程度的间接性。(迪米特法则?)

场景

a)   远程代理:为一个对象在不同的地址空间提供局部代表,这样可以隐藏一个对象存在于不同地址空间的事实。【WebService,客户端可以调用代理解决远程访问问题】

b)   虚拟代理:根据需要创建开销很大的对象,通过它来存放实例化需要很长时间地真实对象。【比如Html网页的图片,代理存储的是真实图片的路径和尺寸】

c)   安全代理:用来控制真实对象的访问权限。

d)   智能指引:当调用真实的对象时,代理处理另一些事。【如计算机真实对象的引用次数,代理在访问一个对象的时候回附加一些内务处理,检查对象是否被锁定、是否该释放、是否该装入内存等等】

优点:1)代理模式能将代理对象与真正被调用的对象分离,在一定程度上降低了系统的耦合度。2)代理模式在客户端和目标对象之间起到一个中介作用,这样可以起到保护目标对象的作用。代理对象也可以对目标对象调用之前进行其他操作。

缺点:1)在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢。2)增加了系统的复杂度。

应用:java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。

观察者模式(Publish/Subscribe) 【又名 发布-订阅模式】:定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,让它们能够自动更新自己。

场景:将一个系统分割成一系列互相协作的类,有一个缺点:需要维护相关对象间的一致性。紧密的耦合会给维护和扩展带来不便。观察者模式就是为了解耦而诞生的,让原本一个对象依赖另一个对象的关系,变成了两方都依赖于抽象,而不再依赖于具体,从而使得各自的变化都不会影响另一边的变化。

优点:解耦。

缺点:如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃。在使用观察者模式是要特别注意这一点。

应用:java.util.Observer ,  java类库实现观察着(Observer)模式的类和接口。

模板方法模式(Template Method) 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。

原则:代码复用平台。

场景:遇到由一系列步骤构成的过程需要执行,这个过程从高层次上看是相同的,但是有些步骤的实现可能不同,这个时候就需要考虑用模板方法模式了。

优点:模板方法模式是通过把不变行为搬移到超类,去除子类中重复代码来实现它的优势,提供了一个代码复用平台,帮助子类摆脱重复的不变行为的纠缠。

缺点:如果父类中可变的基本方法太多,将会导致类的个数增加,系统更加庞大。

应用:AbstractClass抽象类里面的TemplateMethod()就是模板方法。

命令模式(command) 将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。

原则:敏捷开发原则

场景:对请求排队或记录请求日志,以及支持可撤销的操作等行为。

优点

a)      命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分割开。

b)      它能较容易的设计一个命令队列。

c)       在需要的情况下,可以较容易的将命令记入日志。

d)      允许接收请求的一方决定是否要否决请求。

e)      可以容易的实现对请求的撤销和重做。

f)        由于加进新的具体命令类不影响其他类,因此增加新的具体命令类很容易。

缺点:会增加系统的复杂性,这里的复杂性应该主要指的是类的数量。

应用

1.       java.util.Timer类中scheduleXXX()方法

2.       java Concurrency Executor execute()方法

3.       java.lang.reflect.Methodinvoke()方法

状态模式(state) 当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类。

原则:单一职责原则

场景:当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,可以考虑使用状态模式了。

优点:状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。【消除庞大的条件分支语句】。

缺点:违背开放-封闭原则

应用

1.       java.util.Iterator

2.       javax.faces.lifecycle.LifeCycle#execute()

职责链模式(chain of responsibility) 使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

场景:当客户提交一个请求时,请求是沿链传递直至有一个对象负责处理它。

优点:使得接收者和发送者都没有对方的明确信息,且链中对象自己也不知道链结构,结果是职责链可以简化对象的相互连接,它们只需要保持一个指向其后继者的引用,而不需要保持它所有的候选接收者的引用。开发者可以随时的增加或者修改处理一个请求的结构,增强了给对象指派职责的灵活性

缺点:一个请求极有可能到了链的末端都得不到处理,或者因为没有正确配置而得不到处理。

解释器模式(interpreter) 给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

原则:依赖倒转原则

场景:如果一种特定类型问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语句中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。当一个语言需要执行,并且你可将该语言中的句子表示为一个抽象语法树时,可以用解释器模式。

优点:解释器很容易改变和扩展文法,因为该模式使用类来表示文法规则,可以使用继承来改变或扩展文法,也比较容易实现文法。因为定义抽象语法树中各个节点的类的实现大体类似,这些类都易于直接编写。

缺点:解释器模式为文法中的每一条规则至少定义了一个类,因此包含许多规则的文法可能难以管理和维护,建议当文法非常复杂时,使用其他技术(语法分析程序、编译器生成器)。

应用

1.       java.util.Pattern

2.       java.text.Normalizer

3.       java.text.Format

4.       javax.el.ELResolver

中介者模式(mediator) 用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显示的相互引用,从而使其耦合松散,而且可以独立的改变它们之间的交互。

场景:一般应用于一组对象以定义良好但是复杂的方式进行通信的场合,以及想定制一个分布在多个类的行为,而又不想生成太多子类的场合。【例如,Form窗体,或者aspx页面】。

优点

a)   抽象中介者类(Mediator)减少了抽象同事类(colleague)之间的耦合,是的可以独立的改变和复用各个类。

b)   由于把对象如何协作进行了抽象,将中介作为一个独立的概念并将其封装在一个对象中,这样关注的对象就从对象各自本身的行为转移到它们之间的交互上来,也就是站在一个更宏观的角度去看待系统。

缺点:控制集中化导致了中介者的复杂化。

应用

1.       java.util.Timer

2.       java.util.concurrent.Executor#execute()

3.       java.util.concurrent.ExecutorService#submit()

4.       java.lang.reflect.Method#invoke()

访问者模式 (Vistor) 生成器模式】:(GoF中最复杂的一个模式)表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

场景:访问者模式适合有稳定的数据结构、又有易于变化的算法】访问者模式适用于数据结构相对稳定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,是的操作集合可以相对自由的演化。访问者模式的目的是要把处理从数据结构中分离出来。

优点:增加新的操作很容易。新的操作就是新的访问者。

缺点:很难增加新的数据结构。

应用

1.       javax.lang.model.element.AnnotationValue和AnnotationValueVisitor

2.       javax.lang.model.element.Element和ElementVisitor

3.       javax.lang.model.type.TypeMirror和TypeVisitor

策略模式(strategy) 它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化不会影响到使用算法的用户。

场景:策略模式不仅可以用来封装算法,几乎可以封装缝合类型的规则,不同的业务逻辑都可以考虑用策略模式处理变化。

优点:策略模式的策略类为上下文定义了一系列可供重用的算法或行为,继承有助于析取出这些算法中的公共功能。另外,策略模式简化了单元测试,因为每一个算法都有自己的类,可以通过自己的接口单独测试。当不同的行为堆砌在一个类中,很难避免使用switch语句。但是将这些行为封装在一个一个独立的策略类中,可以在使用这些行为的类中消除条件语句

缺点:基本的策略模式,选择权在客户端,具体实现转给策略模式的上下文对象。这并不好。使用策略模式和工厂类结合,可以减轻客户端的职责。但是还是不够完美,使用反射才能真正快乐。

应用

1.       java.util.Comparator#compare()

2.       javax.servlet.http.HttpServlet

3.       javax.servlet.Filter#doFilter()

备忘录模式(Memento) 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可将该对象恢复到原先保存的状态。

场景:Memento封装要保存的细节,适合功能负责但需要维护或记录属性历史的类,或者是需要保存的属性只是众多属性中的一个小部分。

优点:使用备忘录模式可以把复杂的发起人内部信息对其他的对象屏蔽起来,从而可以恰当地保持封装的边界。

缺点:如果发起人角色的状态需要完整地存储到备忘录对象中,那么在资源消耗上面备忘录对象会很昂贵。

应用

1.       java.util.Date

2.       java.io.Serializable

迭代器模式(Iterator) 提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。

场景:当需要对聚集有多种方式遍历时,可以考虑使用迭代器。

优点迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器来负责,这样既可以做到不暴露集合的内部结构,又可以让外部代码透明的访问集合内部的数据。

缺点:由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。

应用:collection容器使用了迭代器模式

设计模式在JDK的应用:http://blog.csdn.net/u013782203/article/details/52214393

一、引言

     C#版本的23种设计模式已经写完了,现在也到了一个该总结的时候了。说起设计模式,我的话就比较多了。刚开始写代码的时候,有需求就写代码来解决需求,如果有新的需求,或者需求变了,我就想当然的修改自己的代码来满足新的需求,这样做感觉是理所当然的,也没感觉有什么不妥的地方。写了两年多代码,偶尔一次,听说了设计模式,据听说设计模式就是软件界的“独孤九剑”,学会之后就可以天下无敌,于是我就开始了我的进修之路。

       想当初,我就像很多的初学编程的人一样,在面试官面前很容易说出面向对象语言的三大特征:继承,多态和封装,其实,我根本不理解这三个词语的意思,或者说理解的很浅薄。当然也有很多概念是不清楚的,比如:OOPL(面向对象语言)是不是就是OO的全部,面向对象的设计模式和设计模式的到底有什么区别,等等相关问题。自从自己学了设计模式,很多概念变得清晰明了,做事有原则了,或者说有准则了,不像以前只是解决了问题就好,根本不管代码写的是否合理。写的代码可以很优美了,结构更合理了,自己开始重新思考代码这个东西。

     学习设计模式并不是一帆风顺的,尤其是刚开始的时候,由于自己很多概念不是很清楚,也走了很多弯路,但是通过自己的坚持,通过自己不断的看,不断的写,对模式的理解也越来越准确了。写代码不是一个很简单的事情,做任何事都是有原则的,写代码也一样,如果自己心中对做的事情没有准则,那就和无头的苍蝇一样,做与不做是一样的。写代码和写好代码是不一样的,如果要写好的代码,考虑的问题更多了,考虑稳定性,扩展性和耦合性,当然也要考虑上游和下游关联的问题,让你做事的时候知道怎么做,也知道为什么这么做,这就是学习设计模式带来的好处。

     好了,说了也不少了,我把我写的模式都罗列出来,每个模式都有链接,可以直接点击进入查看和阅读,希望对大家阅读有益。

系列导航:

创建型:

    C#设计模式(1)——单例模式(Singleton Pattern)

    C#设计模式(2)——工厂方法模式(Factory Pattern)

    C#设计模式(3)——抽象工厂模式(Abstract Pattern)

    C#设计模式(4)——建造模式(Builder Pattern)

    C#设计模式(5)——原型模式(Prototype Pattern)

结构型:

    C#设计模式(6)——适配器模式(Adapter Pattern)

    C#设计模式(7)——桥接模式(Bridge Pattern)

    C#设计模式(8)——装饰模式(Decorator Pattern)

    C#设计模式(9)——组合模式(Composite Pattern)

    C#设计模式(10)——外观模式(Facade Pattern)

    C#设计模式(11)——享元模式(Flyweight Pattern)

    C#设计模式(12)——代理模式(Proxy Pattern)

行为型:

    C#设计模式(13)——模板方法模式(Template Method)

    C#设计模式(14)——命令模式(Command Pattern)

    C#设计模式(15)——迭代器模式(Iterator Pattern)

    C#设计模式(16)——观察者模式(Observer Pattern)

    C#设计模式(17)——中介者模式(Mediator Pattern)

    C#设计模式(18)——状态模式(State Pattern)

    C#设计模式(19)——策略模式(Stragety Pattern)

    C#设计模式(20)——责任链模式(Chain of Responsibility Pattern)

    C#设计模式(21)——访问者模式(Vistor Pattern)

    C#设计模式(22)——备忘录模式(Memento Pattern)

    C#设计模式(23)——解释器模式(Interpreter Pattern)

二、 面向对象的设计原则

       写代码也是有原则的,我们之所以使用设计模式,主要是为了适应变化,提高代码复用率,使软件更具有可维护性和可扩展性。如果我们能更好的理解这些设计原则,对我们理解面向对象的设计模式也是有帮助的,因为这些模式的产生是基于这些原则的。这些规则是:单一职责原则(SRP)、开放封闭原则(OCP)、里氏代替原则(LSP)、依赖倒置原则(DIP)、接口隔离原则(ISP)、合成复用原则(CRP)和迪米特原则(LoD)。下面我们就分别介绍这几种设计原则。

   2.1、单一职责原则(SRP):

  (1)、SRP(Single Responsibilities Principle)的定义:就一个类而言,应该仅有一个引起它变化的原因。简而言之,就是功能要单一。

  (2)、如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其它职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏。(敏捷软件开发)

  (3)、软件设计真正要做的许多内容,就是发现职责并把那些职责相互分离。

     小结:单一职责原则(SRP)可以看做是低耦合、高内聚在面向对象原则上的引申,将职责定义为引起变化的原因,以提高内聚性来减少引起变化的原因。责任过多,引起它变化的原因就越多,这样就会导致职责依赖,大大损伤其内聚性和耦合度。

   2.2、开放关闭原则(OCP)

  (1)、OCP(Open-Close Principle)的定义:就是说软件实体(类,方法等等)应该可以扩展(扩展可以理解为增加),但是不能在原来的方法或者类上修改,也可以这样说,对增加代码开放,对修改代码关闭。

  (2)、OCP的两个特征: 对于扩展(增加)是开放的,因为它不影响原来的,这是新增加的。对于修改是封闭的,如果总是修改,逻辑会越来越复杂。

     小结:开放封闭原则(OCP)是面向对象设计的核心思想。遵循这个原则可以为我们面向对象的设计带来巨大的好处:可维护(维护成本小,做管理简单,影响最小)、可扩展(有新需求,增加就好)、可复用(不耦合,可以使用以前代码)、灵活性好(维护方便、简单)。开发人员应该仅对程序中出现频繁变化的那些部分做出抽象,但是不能过激,对应用程序中的每个部分都刻意地进行抽象同样也不是一个好主意。拒绝不成熟的抽象和抽象本身一样重要。

   2.3、里氏代替原则(LSP)

      (1)、LSP(Liskov Substitution Principle)的定义:子类型必须能够替换掉它们的父类型。更直白的说,LSP是实现面向接口编程的基础。

      小结:任何基类可以出现的地方,子类一定可以出现,所以我们可以实现面向接口编程。 LSP是继承复用的基石,只有当子类可以替换掉基类,软件的功能不受到影响时,基类才能真正被复用,而子类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

   2.4、依赖倒置原则(DIP)

     (1)、DIP(Dependence Inversion Principle)的定义:抽象不应该依赖细节,细节应该依赖于抽象。简单说就是,我们要针对接口编程,而不要针对实现编程。

     (2)、高层模块不应该依赖低层模块,两个都应该依赖抽象,因为抽象是稳定的。抽象不应该依赖具体(细节),具体(细节)应该依赖抽象。

      小结:依赖倒置原则其实可以说是面向对象设计的标志,如果在我们编码的时候考虑的是面向接口编程,而不是简单的功能实现,体现了抽象的稳定性,只有这样才符合面向对象的设计。

   2.5 接口隔离原则(ISP)

     (1)、接口隔离原则(Interface Segregation Principle, ISP)指的是使用多个专门的接口比使用单一的总接口要好。也就是说不要让一个单一的接口承担过多的职责,而应把每个职责分离到多个专门的接口中,进行接口分离。过于臃肿的接口是对接口的一种污染。

     (2)、使用多个专门的接口比使用单一的总接口要好。

     (3)、一个类对另外一个类的依赖性应当是建立在最小的接口上的。

     (4)、一个接口代表一个角色,不应当将不同的角色都交给一个接口。没有关系的接口合并在一起,形成一个臃肿的大接口,这是对角色和接口的污染。

     (5)、“不应该强迫客户依赖于它们不用的方法。接口属于客户,不属于它所在的类层次结构。”这个说得很明白了,再通俗点说,不要强迫客户使用它们不用的方法,如果强迫用户使用它们不使用的方法,那么这些客户就会面临由于这些不使用的方法的改变所带来的改变。

       小结:接口隔离原则(ISP)告诉我们,在做接口设计的时候,要尽量设计的接口功能单一,功能单一,使它变化的因素就少,这样就更稳定,其实这体现了高内聚,低耦合的原则,这样做也避免接口的污染。

   2.6 组合复用原则(CRP)

     (1)、组合复用原则(Composite Reuse Principle, CRP)就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分。新对象通过向这些对象的委派达到复用已用功能的目的。简单地说,就是要尽量使用合成/聚合,尽量不要使用继承。

     (2)、要使用好组合复用原则,首先需要区分”Has—A”和“Is—A”的关系。 “Is—A”是指一个类是另一个类的“一种”,是属于的关系,而“Has—A”则不同,它表示某一个角色具有某一项责任。导致错误的使用继承而不是聚合的常见的原因是错误地把“Has—A”当成“Is—A”.例如:鸡是动物,这就是“Is-A”的表现,某人有一个手枪,People类型里面包含一个Gun类型,这就是“Has-A”的表现。

     小结:组合/聚合复用原则可以使系统更加灵活,类与类之间的耦合度降低,一个类的变化对其他类造成的影响相对较少,因此一般首选使用组合/聚合来实现复用;其次才考虑继承,在使用继承时,需要严格遵循里氏替换原则,有效使用继承会有助于对问题的理解,降低复杂度,而滥用继承反而会增加系统构建和维护的难度以及系统的复杂度,因此需要慎重使用继承复用。

   2.7 迪米特法则(Law of Demeter)

      (1)、迪米特法则(Law of Demeter,LoD)又叫最少知识原则(Least Knowledge Principle,LKP),指的是一个对象应当对其他对象有尽可能少的了解。也就是说,一个模块或对象应尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立,这样当一个模块修改时,影响的模块就会越少,扩展起来更加容易。

      (2)、关于迪米特法则其他的一些表述有:只与你直接的朋友们通信;不要跟“陌生人”说话。

      (3)、外观模式(Facade Pattern)和中介者模式(Mediator Pattern)就使用了迪米特法则。

       小结:迪米特法则的初衷是降低类之间的耦合,实现类型之间的高内聚,低耦合,这样可以解耦。但是凡事都有度,过分的使用迪米特原则,会产生大量这样的中介和传递类,导致系统复杂度变大。所以在采用迪米特法则时要反复权衡,既做到结构清晰,又要高内聚低耦合。

三、创建型模式

    创建型模式就是用来解决对象实例化和使用的客户端耦合的模式,可以让客户端和对象实例化都独立变化,做到相互不影响。创建型模式包括单例模式、工厂方法模式、抽象工厂模式、建造者模式和原型模式。

    3.1、单例模式(Singleton Pattern):解决的是实例化对象的个数的问题,该模式是把对象的数量控制为一个,该模式可以扩展,可以把实例对象扩展为N个对象,N>=2。比如对象池的实现。

       动机(Motivate):在软件系统构建的过程中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确性、以及良好的效率。如何绕过常规的构造器,提供一种机制来保证一个类只有一个实例?如果指望使用者不使用构造器来重复创建对象,这是不对的。这应该是类设计者的责任,而不是使用者的责任。

       意图(Intent):保证一个类仅有一个实例,并提供一个该实例的全局访问点。

       具体结构图如下所示
      

       示例代码:

复制代码

1 ///


2 /// 单例模式的实现 3 ///

4 public sealed class Singleton 5 {
6 // 定义一个静态变量来保存类的实例
7 private static volatile Singleton uniqueInstance; 8
9 // 定义一个标识确保线程同步
10 private static readonly object locker = new object(); 11
12 // 定义私有构造函数,使外界不能创建该类实例
13 private Singleton() 14 { 15 } 16
17 ///
18 /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点 19 ///

20 ///
21 public static Singleton GetInstance() 22 { 23 // 当第一个线程运行到这里时,此时会对locker对象 “加锁”, 24 // 当第二个线程运行该方法时,首先检测到locker对象为”加锁”状态,该线程就会挂起等待第一个线程解锁 25 // lock语句运行完之后(即线程运行完之后)会对该对象”解锁” 26 // 双重锁定只需要一句判断就可以了
27 if (uniqueInstance == null) 28 { 29 lock (locker) 30 { 31 // 如果类的实例不存在则创建,否则直接返回
32 if (uniqueInstance == null) 33 { 34 uniqueInstance = new Singleton(); 35 } 36 } 37 } 38 return uniqueInstance; 39 } 40 }

复制代码

   

3.2、工厂方法模式(Factory Method Pattern):一种工厂生产一种产品,工厂类和产品类是一一对应的,他们是平行的等级结构,强调的是“单个对象”的变化。

       动机(Motivate):在软件系统的构建过程中,经常面临着“某个对象”的创建工作:由于需求的变化,这个对象(的具体实现)经常面临着剧烈的变化,但是它却拥有比较稳定的接口。如何应对这种变化?如何提供一种“封装机制”来隔离出“这个易变对象”的变化,从而保持系统中“其他依赖对象的对象”不随着需求改变而改变?

       意图(Intent):定义一个创建对象的工厂接口,由其子类决定要实例化的类,将实际创建工作推迟到子类中。

       具体结构图如下所示:

    3.3、抽象工厂模式(Abstract Factory Pattern):该模式关注的是多批多系列相互依赖的产品的变化,比如:SQLConnection,SQLCommand,SqlDataReader,SqlDataAdapter,就是一批相互依赖的对象,他们变化可以产生OledbConnection,OledbCommand,OledbDataReader,OledbDataAdapter

       动机(Motivate):在软件系统中,经常面临着”一系统相互依赖的对象”的创建工作:同时,由于需求的变化,往往存在更多系列对象的创建工作。如何应对这种变化?如何绕过常规的对象创建方法(new),提供一种”封装机制”来避免客户程序和这种”多系列具体对象创建工作”的紧耦合?

       意图(Intent):提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

       具体结构图如下所示

           

       代码实例

复制代码

1 ///


2 /// 下面以不同系列房屋的建造为例子演示抽象工厂模式 3 /// 因为每个人的喜好不一样,我喜欢欧式的,我弟弟就喜欢现代的 4 /// 客户端调用 5 ///

6 class Client 7 {
8 static void Main(string[] args)
9 {
10 // 哥哥的欧式风格的房子
11 AbstractFactory europeanFactory= new EuropeanFactory(); 12 europeanFactory.CreateRoof().Create();
13 europeanFactory.CreateFloor().Create();
14 europeanFactory.CreateWindow().Create();
15 europeanFactory.CreateDoor().Create();
16
17
18 //弟弟的现代风格的房子
19 AbstractFactory modernizationFactory = new ModernizationFactory(); 20 modernizationFactory.CreateRoof().Create();
21 modernizationFactory.CreateFloor().Create();
22 modernizationFactory.CreateWindow().Create();
23 modernizationFactory.CreateDoor().Create();
24 Console.Read();
25 }
26 }
27
28 ///
29 /// 抽象工厂类,提供创建不同类型房子的接口 30 ///

31 public abstract class AbstractFactory 32 {
33 // 抽象工厂提供创建一系列产品的接口,这里作为例子,只给出了房顶、地板、窗户和房门创建接口
34 public abstract Roof CreateRoof(); 35 public abstract Floor CreateFloor(); 36 public abstract Window CreateWindow(); 37 public abstract Door CreateDoor(); 38 }
39
40 ///
41 /// 欧式风格房子的工厂,负责创建欧式风格的房子 42 ///

43 public class EuropeanFactory : AbstractFactory 44 {
45 // 制作欧式房顶
46 public override Roof CreateRoof() 47 {
48 return new EuropeanRoof(); 49 }
50
51 // 制作欧式地板
52 public override Floor CreateFloor() 53 {
54 return new EuropeanFloor(); 55 }
56
57 // 制作欧式窗户
58 public override Window CreateWindow() 59 {
60 return new EuropeanWindow(); 61 }
62
63 // 制作欧式房门
64 public override Door CreateDoor() 65 {
66 return new EuropeanDoor(); 67 }
68 }
69
70 ///
71 /// 现在风格房子的工厂,负责创建现代风格的房子 72 ///

73 public class ModernizationFactory : AbstractFactory 74 {
75 // 制作现代房顶
76 public override Roof CreateRoof() 77 {
78 return new ModernizationRoof(); 79 }
80
81 // 制作现代地板
82 public override Floor CreateFloor() 83 {
84 return new ModernizationFloor(); 85 }
86
87 // 制作现代窗户
88 public override Window CreateWindow() 89 {
90 return new ModernizationWindow(); 91 }
92
93 // 制作现代房门
94 public override Door CreateDoor() 95 {
96 return new ModernizationDoor(); 97 }
98 }
99
100 ///
101 /// 房顶抽象类,子类的房顶必须继承该类 102 ///

103 public abstract class Roof 104 { 105 ///
106 /// 创建房顶 107 ///

108 public abstract void Create(); 109 } 110
111 ///
112 /// 地板抽象类,子类的地板必须继承该类 113 ///

114 public abstract class Floor 115 { 116 ///
117 /// 创建地板 118 ///

119 public abstract void Create(); 120 } 121
122 ///
123 /// 窗户抽象类,子类的窗户必须继承该类 124 ///

125 public abstract class Window 126 { 127 ///
128 /// 创建窗户 129 ///

130 public abstract void Create(); 131 } 132
133 ///
134 /// 房门抽象类,子类的房门必须继承该类 135 ///

136 public abstract class Door 137 { 138 ///
139 /// 创建房门 140 ///

141 public abstract void Create(); 142 } 143
144 ///
145 /// 欧式地板类 146 ///

147 public class EuropeanFloor : Floor 148 { 149 public override void Create() 150 { 151 Console.WriteLine(“创建欧式的地板”); 152 } 153 } 154
155
156 ///
157 /// 欧式的房顶 158 ///

159 public class EuropeanRoof : Roof 160 { 161 public override void Create() 162 { 163 Console.WriteLine(“创建欧式的房顶”); 164 } 165 } 166
167
168 ///
169 ///欧式的窗户 170 ///

171 public class EuropeanWindow : Window 172 { 173 public override void Create() 174 { 175 Console.WriteLine(“创建欧式的窗户”); 176 } 177 } 178
179
180 ///
181 /// 欧式的房门 182 ///

183 public class EuropeanDoor : Door 184 { 185 public override void Create() 186 { 187 Console.WriteLine(“创建欧式的房门”); 188 } 189 } 190
191 ///
192 /// 现代的房顶 193 ///

194 public class ModernizationRoof : Roof 195 { 196 public override void Create() 197 { 198 Console.WriteLine(“创建现代的房顶”); 199 } 200 } 201
202 ///
203 /// 现代的地板 204 ///

205 public class ModernizationFloor : Floor 206 { 207 public override void Create() 208 { 209 Console.WriteLine(“创建现代的地板”); 210 } 211 } 212
213 ///
214 /// 现代的窗户 215 ///

216 public class ModernizationWindow : Window 217 { 218 public override void Create() 219 { 220 Console.WriteLine(“创建现代的窗户”); 221 } 222 } 223
224 ///
225 /// 现代的房门 226 ///

227 public class ModernizationDoor : Door 228 { 229 public override void Create() 230 { 231 Console.WriteLine(“创建现代的房门”); 232 } 233 }

复制代码

   

3.4、建造者模式(Builder Pattern):该模式要解决的是由多个子部分对象构成的一个复杂对象的创建的问题,该复杂对象构成算法稳定,各个子部分对象易变化的情况。强调组装过程的稳定。

       动机(Motivate):在软件系统中,有时候面临着“一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。如何应对这种变化?如何提供一种“封装机制”来隔离出“复杂对象的各个部分”的变化,从而保持系统中的“稳定构建算法”不随着需求改变而改变?

       意图(Intent):将一个复杂对象的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。

                                  将一个产品的表示形式与产品的组装过程分割开来,从而可以使同一个组装过程(这个构建过程是稳定的,也就是算法)生成具体不同的表现的产品对象。

       具体结构图如下所示

                

    3.5、原型模式(Prototype Pattern):通过制定实例类型来复制对象

       动机(Motivate):在软件系统中,经常面临着“某些结构复杂的对象”的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化,但是它们却拥有比较稳定一致的接口。如何应对这种变化?如何向“客户程序(使用这些对象的程序)”隔离出“这些易变对象”,从而使得“依赖这些易变对象的客户程序”不随着需求改变而改变?

       意图(Intent):使用原型实例指定创建对象的种类,然后通过拷贝这些原型来创建新的对象。

       具体结构图如下所示:

四、结构型模式

    结构型模式主要研究的是类和对象的组合的问题。它包括两种类型,一是类结构型模式:指的是采用继承机制来组合实现功能;二是对象结构型模式:指的是通过组合对象的方式来实现新的功能。该系列模式包括:适配器模式、桥接模式、装饰者模式、组合模式、外观模式、享元模式和代理模式。

    4.1、适配器模式(Adapter Pattern):该模式主要关注的是接口转换的问题,将匹配的接口通过适配对接工作。

       动机(Motivate):在软件系统中,由于应用环境的变化,常常需要将“一些现存的对象”放在新的环境中应用,但是新环境要求的接口是这些现存对象所不满足的。如何应对这种“迁移的变化”?如何既能利用现有对象的良好实现,同时又能满足新的应用环境所要求的接口?

       意图(Intent):将一个类的接口转换成客户希望的另一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

                                  适配器模式意在转换接口,它能够使原本不能再一起工作的两个类一起工作,所以经常用来在类库的复用、代码迁移等方面。 适配器模式包括类适配器模式和对象适配器模式,

       具体结构图如下所示:

       类适配器模式:

       对象适配器模式:

    4.2、桥接模式(Bridge Pattern):该模式注重分离接口与其实现,接口是针对客户的,接口的内在实现是通过“实现层次”来完成的,支持多维度变化。

       动机(Motivate):在很多游戏场景中,会有这样的情况:【装备】本身会有的自己固有的逻辑,比如枪支,会有型号的问题,同时现在很多的游戏又在不同的介质平台上运行和使用,这样就使得游戏的【装备】具有了两个变化的维度——一个变化的维度为“平台的变化”,另一个变化的维度为“型号的变化”。如果我们要写代码实现这款游戏,难道我们针对每种平台都实现一套独立的【装备】吗?复用在哪里?如何应对这种“多维度的变化”?如何利用面向对象技术来使得【装备】可以轻松地沿着“平台”和“型号”两个方向变化,而不引入额外的复杂度?

       意图(Intent):将抽象部分与实现部分分离,使它们都可以独立地变化。

                       比如:就拿游戏装备来说,“手枪”,抽象部分是指手枪的型号,这个型号可以是G50,G55,针对不同平台,这些型号有不同的实现,针对这些不同平台的不同型号的实现,重新抽象,抽象的结构就是“实现的层次”,其实,到此,就形成了两个抽象层次,第一个层次,是枪支的型号的层次,另一个层次就是针对其实现的一个层次,这样就做到了抽象和实现的分离。

       具体结构图如下所示

          

       示例代码:

复制代码

1 namespace 桥接模式的实现 2 {
3 ///


4 /// 该抽象类就是抽象接口的定义,该类型就相当于是Abstraction类型 5 ///

6 public abstract class Database 7 {
8 //通过组合方式引用平台接口,此处就是桥梁,该类型相当于Implementor类型
9 protected PlatformImplementor _implementor; 10
11 //通过构造器注入,初始化平台实现
12 protected Database(PlatformImplementor implementor) 13 { 14 this._implementor = implementor; 15 } 16
17 //创建数据库–该操作相当于Abstraction类型的Operation方法
18 public abstract void Create(); 19 } 20
21 ///
22 /// 该抽象类就是实现接口的定义,该类型就相当于是Implementor类型 23 ///

24 public abstract class PlatformImplementor 25 { 26 //该方法就相当于Implementor类型的OperationImpl方法
27 public abstract void Process(); 28 } 29
30 ///
31 /// SqlServer2000版本的数据库,相当于RefinedAbstraction类型 32 ///

33 public class SqlServer2000 : Database 34 { 35 //构造函数初始化
36 public SqlServer2000(PlatformImplementor implementor) : base(implementor) { } 37
38 public override void Create() 39 { 40 this._implementor.Process(); 41 } 42 } 43
44 ///
45 /// SqlServer2005版本的数据库,相当于RefinedAbstraction类型 46 ///

47 public class SqlServer2005 : Database 48 { 49 //构造函数初始化
50 public SqlServer2005(PlatformImplementor implementor) : base(implementor) { } 51
52 public override void Create() 53 { 54 this._implementor.Process(); 55 } 56 } 57
58 ///
59 /// SqlServer2000版本的数据库针对Unix操作系统具体的实现,相当于ConcreteImplementorA类型 60 ///

61 public class SqlServer2000UnixImplementor : PlatformImplementor 62 { 63 public override void Process() 64 { 65 Console.WriteLine(“SqlServer2000针对Unix的具体实现”); 66 } 67 } 68
69 ///
70 /// SqlServer2005版本的数据库针对Unix操作系统的具体实现,相当于ConcreteImplementorB类型 71 ///

72 public sealed class SqlServer2005UnixImplementor : PlatformImplementor 73 { 74 public override void Process() 75 { 76 Console.WriteLine(“SqlServer2005针对Unix的具体实现”); 77 } 78 } 79
80 public class Program 81 { 82 static void Main() 83 { 84 PlatformImplementor SqlServer2000UnixImp = new SqlServer2000UnixImplementor(); 85 //还可以针对不同平台进行扩展,也就是子类化,这个是独立变化的
86
87 Database SqlServer2000Unix = new SqlServer2000(SqlServer2000UnixImp); 88 //数据库版本也可以进行扩展和升级,也进行独立的变化。 89
90 //以上就是两个维度的变化。 91
92 //就可以针对Unix执行操作了
93 SqlServer2000Unix.Create(); 94 } 95 } 96 }

复制代码

   

4.3、装饰模式(Decorator Pattern):该模式注重稳定接口,让接口不变,在此前提下为对象动态(关键点)的扩展功能。如果通过继承,各个功能点的组合就会形成过多的子类,维护起来就是麻烦。

       动机(Motivate):在房子装修的过程中,各种功能可以相互组合,来增加房子的功用。类似的,如果我们在软件系统中,要给某个类型或者对象增加功能,如果使用“继承”的方案来写代码,就会出现子类暴涨的情况。比如:IMarbleStyle是大理石风格的一个功能,IKeepWarm是保温的一个接口定义,IHouseSecurity是房子安全的一个接口,就三个接口来说,House是我们房子,我们的房子要什么功能就实现什么接口,如果房子要的是复合功能,接口不同的组合就有不同的结果,这样就导致我们子类膨胀严重,如果需要在增加功能,子类会成指数增长。这个问题的根源在于我们“过度地使用了继承来扩展对象的功能”,由于继承为类型引入的静态特质(所谓静态特质,就是说如果想要某种功能,我们必须在编译的时候就要定义这个类,这也是强类型语言的特点。静态,就是指在编译的时候要确定的东西;动态,是指运行时确定的东西),使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀(多继承)。如何使“对象功能的扩展”能够根据需要来动态(即运行时)地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?从而使得任何“功能扩展变化”所导致的影响降为最低?

       意图(Intent):动态地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类更为灵活。

       具体结构图如下所示

              

       示例代码:

复制代码

1 namespace 装饰模式的实现 2 {
3 ///


4 /// 该抽象类就是房子抽象接口的定义,该类型就相当于是Component类型,是饺子馅,需要装饰的,需要包装的 5 ///

6 public abstract class House 7 {
8 //房子的装修方法–该操作相当于Component类型的Operation方法
9 public abstract void Renovation(); 10 } 11
12 ///
13 /// 该抽象类就是装饰接口的定义,该类型就相当于是Decorator类型,如果需要具体的功能,可以子类化该类型 14 ///

15 public abstract class DecorationStrategy:House //关键点之二,体现关系为Is-a,有这这个关系,装饰的类也可以继续装饰了
16 { 17 //通过组合方式引用Decorator类型,该类型实施具体功能的增加 18 //这是关键点之一,包含关系,体现为Has-a
19 protected House _house; 20
21 //通过构造器注入,初始化平台实现
22 protected DecorationStrategy(House house) 23 { 24 this._house=house; 25 } 26
27 //该方法就相当于Decorator类型的Operation方法
28 public override void Renovation() 29 { 30 if(this._house!=null) 31 { 32 this._house.Renovation(); 33 } 34 } 35 } 36
37 ///
38 /// PatrickLiu的房子,我要按我的要求做房子,相当于ConcreteComponent类型,这就是我们具体的饺子馅,我个人比较喜欢韭菜馅 39 ///

40 public sealed class PatrickLiuHouse:House 41 { 42 public override void Renovation() 43 { 44 Console.WriteLine(“装修PatrickLiu的房子”); 45 } 46 } 47
48
49 ///
50 /// 具有安全功能的设备,可以提供监视和报警功能,相当于ConcreteDecoratorA类型 51 ///

52 public sealed class HouseSecurityDecorator:DecorationStrategy 53 { 54 public HouseSecurityDecorator(House house):base(house){} 55
56 public override void Renovation() 57 { 58 base.Renovation(); 59 Console.WriteLine(“增加安全系统”); 60 } 61 } 62
63 ///
64 /// 具有保温接口的材料,提供保温功能,相当于ConcreteDecoratorB类型 65 ///

66 public sealed class KeepWarmDecorator:DecorationStrategy 67 { 68 public KeepWarmDecorator(House house):base(house){} 69
70 public override void Renovation() 71 { 72 base.Renovation(); 73 Console.WriteLine(“增加保温的功能”); 74 } 75 } 76
77 public class Program 78 { 79 static void Main() 80 { 81 //这就是我们的饺子馅,需要装饰的房子
82 House myselfHouse=new PatrickLiuHouse(); 83
84 DecorationStrategy securityHouse=new HouseSecurityDecorator(myselfHouse); 85 securityHouse.Renovation(); 86 //房子就有了安全系统了 87
88 //如果我既要安全系统又要保暖呢,继续装饰就行
89 DecorationStrategy securityAndWarmHouse=new HouseSecurityDecorator(securityHouse); 90 securityAndWarmHouse.Renovation(); 91 } 92 } 93 }

复制代码

复制代码

1 namespace 命令模式的实现 2 {
3 ///


4 /// 俗话说:“好吃不如饺子,舒服不如倒着”。今天奶奶发话要吃他大孙子和孙媳妇包的饺子。今天还拿吃饺子这件事来说说命令模式的实现吧。 5 ///

6 class Client 7 {
8 static void Main(string[] args)
9 { 10 //奶奶想吃猪肉大葱馅的饺子
11 PatrickLiuAndWife liuAndLai = new PatrickLiuAndWife();//命令接受者
12 Command command = new MakeDumplingsCommand(liuAndLai);//命令
13 PaPaInvoker papa = new PaPaInvoker(command); //命令请求者 14
15 //奶奶发布命令
16 papa.ExecuteCommand(); 17
18
19 Console.Read(); 20 } 21 } 22
23 //这个类型就是请求者角色–也就是我爸爸的角色,告诉奶奶要吃饺子
24 public sealed class PaPaInvoker 25 { 26 //我爸爸从奶奶那里接受到的命令
27 private Command _command; 28
29 //爸爸开始接受具体的命令
30 public PaPaInvoker(Command command) 31 { 32 this._command = command; 33 } 34
35 //爸爸给我们下达命令
36 public void ExecuteCommand() 37 { 38 _command.MakeDumplings(); 39 } 40 } 41
42 //该类型就是抽象命令角色–Commmand,定义了命令的抽象接口,任务是包饺子
43 public abstract class Command 44 { 45 //真正任务的接受者
46 protected PatrickLiuAndWife _worker; 47
48 protected Command(PatrickLiuAndWife worker) 49 { 50 _worker = worker; 51 } 52
53 //该方法就是抽象命令对象Command的Execute方法
54 public abstract void MakeDumplings(); 55 } 56
57 //该类型是具体命令角色–ConcreteCommand,这个命令完成制作“猪肉大葱馅”的饺子
58 public sealed class MakeDumplingsCommand : Command 59 { 60 public MakeDumplingsCommand(PatrickLiuAndWife worker) : base(worker) { } 61
62 //执行命令–包饺子
63 public override void MakeDumplings() 64 { 65 //执行命令—包饺子
66 _worker.Execute(“今天包的是农家猪肉和农家大葱馅的饺子”); 67 } 68 } 69
70 //该类型是具体命令接受角色Receiver,具体包饺子的行为是我们夫妻俩来完成的
71 public sealed class PatrickLiuAndWife 72 { 73 //这个方法相当于Receiver类型的Action方法
74 public void Execute(string job) 75 { 76 Console.WriteLine(job); 77 } 78 } 79 }

复制代码

   

4.4、组合模式(Composite Pattern):该模式着重解决统一接口的问题,将“一对多”的关系转化为“一对一”的关系,让使用叶子节点和树干节点保持接口一致。

       动机(Motivate):客户代码过多地依赖于对象容器(对象容器是对象的容器,细细评味)复杂的内部实现结构,对象容器内部实现结构(而非抽象接口)的变化将引起客户代码的频繁变化,带来了代码的维护性、扩展性等方面的弊端。如何将“客户代码与复杂的对象容器内部结构”解耦?如何让对象容器自己来实现自身的复杂结构,从而使得客户代码就像处理简单对象一样来处理复杂的对象容器?

       意图(Intent):将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。

       具体结构图如下所示:

    4.5、外观模式(Facade Pattern):该模式注重简化接口,简化组件系统与外部客户程序的依赖关系,是Lod(迪米特原则,也叫:最少知识原则)原则的最好的体现,

       动机(Motivate):在软件系统开发的过程中,当组件的客户(即外部接口,或客户程序)和组件中各种复杂的子系统有了过多的耦合,随着外部客户程序和各子系统的演化,这种过多的耦合面临很多变化的挑战。如何简化外部客户程序和系统间的交互接口?如何将外部客户程序的演化和内部子系统的变化之间的依赖相互解耦?

       意图(Intent):为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

       具体结构图如下所示:

    4.6、享元模式(Flyweight Pattern):该模式注重保留接口,在内部使用共享技术对对象存储进行优化

       动机(Motivate):在软件系统中,采用纯粹对象方案的问题在于大量细粒度的对象会很快充斥在系统中,从而带来很高的运行时代价——主要指内存需求方面的代价。如何在避免大量细粒度对象问题的同时,让外部客户程序仍然能够透明地使用面向对象的方式来进行操作?

       意图(Intent):运用共享技术有效地支持大量细粒度的对象。

                                 在.NET类库中,String类的实现就使用了享元模式,String类采用字符串驻留池的来使字符串进行共享。

       具体结构图如下所示

           

    4.7、代理模式(Proxy Pattern):该模式注重假借代理接口,控制对真实对象的访问,通过增加间接层来实现灵活控制,比如可以在访问正真对象之前进行权限验证,生活中的明星的代理就是很好的例子,要想接触明星,先要和他的经纪人打交道。

       动机(Motivate):在面向对象系统中,有些对象由于某种原因(比如对象创建的开销很大,或者某些操作需要安全控制,或者需要进程外的访问等),直接访问会给使用者、或者系统结构带来很多麻烦。如何在不失去透明操作对象的同时来管理/控制这些对象特有的复杂性?增加一层间接层是软件开发中常见的解决方式。

       意图(Intent):为其他对象提供一种代理以控制对这个对象的访问。

       具体结构图如下所示:

             

五、行为型模式

    行为型模式主要讨论的是在不同对象之间划分责任和算法的抽象化的问题。行为型模式又分为类的行为模式和对象的行为模式两种。

    类的行为模式——使用继承关系在几个类之间分配行为。

对象的行为模式——使用对象聚合的方式来分配行为。

    行为型模式包括11种模式:模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、状态模式、策略模式、责任链模式、访问者模式、解释器模式和备忘录模式。

    5.1、模板方法模式(Template Method Pattern):该模式注重对算法结构的封装,定义算法骨架,并且稳定,但是支持算法子步骤的变化。

       动机(Motivate):在软件构建过程中,对于某一项任务,它常常有稳定的整体操作结构,但各个子步骤却有很多改变的需求,或者由于固有的原因(比如框架与应用之间的关系)而无法和任务的整体结构同时实现。如何在确定稳定操作结构的前提下,来灵活应对各个子步骤的变化或者晚期实现需求?

       意图(Intent):定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

       具体结构图如下所示

             

    5.2、命令模式(Command Pattern):该模式注重将请求封装为对象,通过将一组行为抽象为对象,实现行为请求者和行为实现者之间的解耦。也可以实现“撤销/重做”的功能,类似“宏”的功能实现也很容易。

       动机(Motivate):在软件构建过程中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合——比如需要对行为进行“记录、撤销/重做(undo/redo)、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。

       意图(Intent):将一个请求封装为一个对象,从而使你可用不同的请求对客户(客户程序,也是行为的请求者)进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。命令模式的实现可以提供命令的撤销和恢复功能。

       具体结构图如下所示

            

       代码实例

复制代码

1 namespace 命令模式的实现 2 {
3 ///


4 /// 俗话说:“好吃不如饺子,舒服不如倒着”。今天奶奶发话要吃他大孙子和孙媳妇包的饺子。今天还拿吃饺子这件事来说说命令模式的实现吧。 5 ///

6 class Client 7 {
8 static void Main(string[] args)
9 { 10 //奶奶想吃猪肉大葱馅的饺子
11 PatrickLiuAndWife liuAndLai = new PatrickLiuAndWife();//命令接受者
12 Command command = new MakeDumplingsCommand(liuAndLai);//命令
13 PaPaInvoker papa = new PaPaInvoker(command); //命令请求者 14
15 //奶奶发布命令
16 papa.ExecuteCommand(); 17
18
19 Console.Read(); 20 } 21 } 22
23 //这个类型就是请求者角色–也就是我爸爸的角色,告诉奶奶要吃饺子
24 public sealed class PaPaInvoker 25 { 26 //我爸爸从奶奶那里接受到的命令
27 private Command _command; 28
29 //爸爸开始接受具体的命令
30 public PaPaInvoker(Command command) 31 { 32 this._command = command; 33 } 34
35 //爸爸给我们下达命令
36 public void ExecuteCommand() 37 { 38 _command.MakeDumplings(); 39 } 40 } 41
42 //该类型就是抽象命令角色–Commmand,定义了命令的抽象接口,任务是包饺子
43 public abstract class Command 44 { 45 //真正任务的接受者
46 protected PatrickLiuAndWife _worker; 47
48 protected Command(PatrickLiuAndWife worker) 49 { 50 _worker = worker; 51 } 52
53 //该方法就是抽象命令对象Command的Execute方法
54 public abstract void MakeDumplings(); 55 } 56
57 //该类型是具体命令角色–ConcreteCommand,这个命令完成制作“猪肉大葱馅”的饺子
58 public sealed class MakeDumplingsCommand : Command 59 { 60 public MakeDumplingsCommand(PatrickLiuAndWife worker) : base(worker) { } 61
62 //执行命令–包饺子
63 public override void MakeDumplings() 64 { 65 //执行命令—包饺子
66 _worker.Execute(“今天包的是农家猪肉和农家大葱馅的饺子”); 67 } 68 } 69
70 //该类型是具体命令接受角色Receiver,具体包饺子的行为是我们夫妻俩来完成的
71 public sealed class PatrickLiuAndWife 72 { 73 //这个方法相当于Receiver类型的Action方法
74 public void Execute(string job) 75 { 76 Console.WriteLine(job); 77 } 78 } 79 }

复制代码

   

5.3、迭代器模式(Iterator Pattern):该模式注重封装对集合的操作,支持集合实例的变化,屏蔽集合对象内部复杂结构,提供客户程序对它的透明遍历。

       动机(Motivate):在软件构建过程中,集合对象内部结构常常变化各异。但对于这些集合对象,我们希望在不暴露其内部结构的同时,可以让外部客户代码透明地访问其中包含的元素;同时这种“透明遍历”也为“同一种算法在多种集合对象上进行操作”提供了可能。使用面向对象技术将这种遍历机制抽象为“迭代器对象”为“应对变化中的集合对象”提供了一种优雅的方式。

       意图(Intent): 提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。

       具体结构图如下所示

            

    5.4、观察者模式(Observer Pattern):该模式注重的是变化通知,变化通知指目标对象发生变化,依赖的对象就能获得通知并进行相应操作,这是一种“一对多”的关系。

       动机(Motivate):在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系”——一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。

       意图(Intent):定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。

       具体结构图如下所示

              

    5.5、中介者模式(Mediator Pattern):该模式注重封装对象间的交互,通过封装一系列对象之间的复杂交互,使他们不需要显式相互引用,实现解耦。

       动机(Motivate):在软件构建过程中,经常会出现多个对象互相关联交互的情况,对象之间常常会维持一种复杂的引用关系,如果遇到一些需求的更改,这种直接的引用关系将面临不断地变化。在这种情况下,我们可使用一个“中介对象”来管理对象间的关联关系,避免相互交互的对象之间的紧耦合引用关系,从而更好地抵御变化。

       意图(Intent):定义了一个中介对象来封装一系列对象之间的交互关系。中介者使各个对象之间不需要显式地相互引用,从而使耦合性降低,而且可以独立地改变它们之间的交互行为。

       具体的结构图如下所示

           

       代码实例

复制代码

1 namespace 中介者模式的实现 2 {
3 //抽象中介者角色
4 public interface Mediator 5 {
6 void Command(Department department); 7 }
8
9 //总经理–相当于具体中介者角色
10 public sealed class President : Mediator 11 {
12 //总经理有各个部门的管理权限
13 private Financial _financial; 14 private Market _market; 15 private Development _development; 16
17 public void SetFinancial(Financial financial) 18 {
19 this._financial = financial; 20 }
21 public void SetDevelopment(Development development) 22 {
23 this._development = development; 24 }
25 public void SetMarket(Market market) 26 {
27 this._market = market; 28 }
29
30 public void Command(Department department) 31 {
32 if (department.GetType() == typeof(Market))
33 {
34 _financial.Process();
35 }
36 }
37 }
38
39 //同事类的接口
40 public abstract class Department 41 {
42 //持有中介者(总经理)的引用
43 private Mediator mediator; 44
45 protected Department(Mediator mediator) 46 {
47 this.mediator = mediator; 48 }
49
50 public Mediator GetMediator 51 {
52 get { return mediator; } 53 private set { this.mediator = value; } 54 }
55
56 //做本部门的事情
57 public abstract void Process(); 58
59 //向总经理发出申请
60 public abstract void Apply(); 61 }
62
63 //开发部门
64 public sealed class Development : Department 65 {
66 public Development(Mediator m) : base(m) { }
67
68 public override void Process() 69 {
70 Console.WriteLine(“我们是开发部门,要进行项目开发,没钱了,需要资金支持!”);
71 }
72
73 public override void Apply() 74 {
75 Console.WriteLine(“专心科研,开发项目!”);
76 }
77 }
78
79 //财务部门
80 public sealed class Financial : Department 81 {
82 public Financial(Mediator m) : base(m) { }
83
84 public override void Process() 85 {
86 Console.WriteLine(“汇报工作!没钱了,钱太多了!怎么花?”);
87 }
88
89 public override void Apply() 90 {
91 Console.WriteLine(“数钱!”);
92 }
93 }
94
95 //市场部门
96 public sealed class Market : Department 97 {
98 public Market(Mediator mediator) : base(mediator) { }
99
100 public override void Process() 101 { 102 Console.WriteLine(“汇报工作!项目承接的进度,需要资金支持!”); 103 GetMediator.Command(this); 104 } 105
106 public override void Apply() 107 { 108 Console.WriteLine(“跑去接项目!”); 109 } 110 } 111
112
113 class Program 114 { 115 static void Main(String[] args) 116 { 117 President mediator = new President(); 118 Market market = new Market(mediator); 119 Development development = new Development(mediator); 120 Financial financial = new Financial(mediator); 121
122 mediator.SetFinancial(financial); 123 mediator.SetDevelopment(development); 124 mediator.SetMarket(market); 125
126 market.Process(); 127 market.Apply(); 128
129 Console.Read(); 130 } 131 } 132 }

复制代码

   

5.6、状态模式(State Pattern):该模式注重封装与状态相关的行为(定义状态类,会把和一个状态相关的操作都放到这个类里面),支持状态的变化,从而在其内部状态改变时改变它的行为。

       动机(Motivate):在软件构建过程中,某些对象的状态如果改变,其行为也会随之而发生变化,比如文档处于只读状态,其支持的行为和读写状态支持的行为就可能完全不同。如何在运行时根据对象的状态来透明地更改对象的行为?而不会为对象操作和状态转化之间引入紧耦合?

       意图(Intent):允许一个对象在其内部状态改变时改变它的行为。从而使对象看起来似乎修改了其行为。

       具体结构图如下所示

              

       代码实例

复制代码

1 namespace 状态模式的实现 2 {
3 //环境角色—相当于Context类型
4 public sealed class Order 5 {
6 private State current; 7
8 public Order() 9 {
10 //工作状态初始化为上午工作状态
11 current = new WaitForAcceptance(); 12 IsCancel = false;
13 }
14 private double minute; 15 public double Minute 16 {
17 get { return minute; } 18 set { minute = value; } 19 }
20
21 public bool IsCancel { get; set; }
22
23 private bool finish; 24 public bool TaskFinished 25 {
26 get { return finish; } 27 set { finish = value; } 28 }
29 public void SetState(State s) 30 {
31 current = s; 32 }
33 public void Action() 34 {
35 current.Process(this);
36 }
37 }
38
39 //抽象状态角色—相当于State类型
40 public interface State 41 {
42 //处理订单
43 void Process(Order order); 44 }
45
46 //等待受理–相当于具体状态角色
47 public sealed class WaitForAcceptance : State 48 {
49 public void Process(Order order) 50 {
51 System.Console.WriteLine(“我们开始受理,准备备货!”);
52 if (order.Minute < 30 && order.IsCancel) 53 {
54 System.Console.WriteLine(“接受半个小时之内,可以取消订单!”);
55 order.SetState(new CancelOrder()); 56 order.TaskFinished = true;
57 order.Action();
58 }
59 order.SetState(new AcceptAndDeliver()); 60 order.TaskFinished = false;
61 order.Action();
62 }
63 }
64
65 //受理发货—相当于具体状态角色
66 public sealed class AcceptAndDeliver : State 67 {
68 public void Process(Order order) 69 {
70 System.Console.WriteLine(“我们货物已经准备好,可以发货了,不可以撤销订单!”);
71 if (order.Minute < 30 && order.IsCancel) 72 {
73 System.Console.WriteLine(“接受半个小时之内,可以取消订单!”);
74 order.SetState(new CancelOrder()); 75 order.TaskFinished = true;
76 order.Action();
77 }
78 if (order.TaskFinished==false)
79 {
80 order.SetState(new Success()); 81 order.Action();
82 }
83 }
84 }
85
86 //交易成功—相当于具体状态角色
87 public sealed class Success : State 88 {
89 public void Process(Order order) 90 {
91 System.Console.WriteLine(“订单结算”);
92 order.SetState(new ConfirmationReceipt()); 93 order.TaskFinished = true;
94 order.Action();
95 }
96 }
97
98 //确认收货—相当于具体状态角色
99 public sealed class ConfirmationReceipt : State 100 { 101 public void Process(Order order) 102 { 103 System.Console.WriteLine(“检查货物,没问题可以就可以签收!”); 104 order.SetState(new ConfirmationReceipt()); 105 order.TaskFinished = true; 106 order.Action(); 107 } 108 } 109
110 //取消订单—相当于具体状态角色
111 public sealed class CancelOrder : State 112 { 113 public void Process(Order order) 114 { 115 System.Console.WriteLine(“检查货物,有问题,取消订单!”); 116 order.SetState(new CancelOrder()); 117 order.TaskFinished = true; 118 order.Action(); 119 } 120 } 121
122
123 public class Client 124 { 125 public static void Main(String[] args) 126 { 127 //订单
128 Order order = new Order(); 129 order.Minute = 9; 130 order.Action(); 131 //可以取消订单
132 order.IsCancel = true; 133 order.Minute = 20; 134 order.Action(); 135 order.Minute = 33; 136 order.Action(); 137 order.Minute = 43; 138 order.Action(); 139
140 Console.Read(); 141 } 142 } 143 }

复制代码

   

5.7、策略模式(Stragety Pattern):该模式注重封装算法,这里面没有算法骨架,一种算法就是一种解决方案,一种方法策略。支持算法的变化,通过封装一系列算法,可以做到算法的替换来满足客户的需求。

       动机(Motivate): 在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂;而且有时候支持不使用的算法也是一个性能负担。如何在运行时根据需要透明地更改对象的算法?将算法与对象本身解耦,从而避免上述问题?

       意图(Intent):定义一系列算法,把它们一个个封装起来,并且使它们可互相替换。该模式使得算法可独立于使用它的客户而变化。

       具体结构图如下所示

          

    5.8、责任链模式(Chain of Responsibility Pattern):该模式注重封装对象责任,支持责任的变化,通过动态构建职责链,实现业务处理。在现实生活中,请假流程,采购流程等都是职责链模式很好的例子。

       动机(Motivate):在软件构建过程中,一个请求可能被多个对象处理,但是每个请求在运行时只能有一个接受者,如果显示指定,将必不可少地带来请求发送者与接受者的紧耦合。如何使请求的发送者不需要指定具体的接受者,让请求的接受者自己在运行时决定来处理请求,从而使两者解耦。

       意图(Intent):避免请求发送者与接收者耦合在一起,让多个对象都有可能接受请求,将这些对象连接成一条链,并且沿着这条链传递请求,知道有对象处理它为止。

       具体结构图如下所示

            

    5.9、访问者模式(Visitor Pattern):该模式注重封装对象操作变化,支持在运行时为类结构添加新的操作,在类层次结构中,在不改变各类的前提下定义作用于这些类实例的新的操作。

       动机(Motivate):在软件构建过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法),如果直接在基类中做这样的更改,将会给子类带来很繁重的变更负担,甚至破坏原有设计。如何在不更改类层次结构的前提下,在运行时根据需要透明地为类层次结构上的各个类动态添加新的操作,从而避免上述问题?

       意图(Intent):表示一个作用于某对象结构中的各个元素的操作。它可以在不改变各元素的类的前提下定义作用于这些元素的新的操作。

       具体结构图如下所示

            

       代码实例

复制代码

1 namespace Vistor 2 {
3 //抽象图形定义—相当于“抽象节点角色”Element
4 public abstract class Shape 5 {
6 //画图形
7 public abstract void Draw(); 8 //外界注入具体访问者
9 public abstract void Accept(ShapeVisitor visitor); 10 }
11
12 //抽象访问者 Visitor
13 public abstract class ShapeVisitor 14 {
15 public abstract void Visit(Rectangle shape); 16
17 public abstract void Visit(Circle shape); 18
19 public abstract void Visit(Line shape); 20
21 //这里有一点要说:Visit方法的参数可以写成Shape吗?就是这样 Visit(Shape shape),当然可以,但是ShapeVisitor子类Visit方法就需要判断当前的Shape是什么类型,是Rectangle类型,是Circle类型,或者是Line类型。
22 }
23
24 //具体访问者 ConcreteVisitor
25 public sealed class CustomVisitor : ShapeVisitor 26 {
27 //针对Rectangle对象
28 public override void Visit(Rectangle shape) 29 {
30 Console.WriteLine(“针对Rectangle新的操作!”);
31 }
32 //针对Circle对象
33 public override void Visit(Circle shape) 34 {
35 Console.WriteLine(“针对Circle新的操作!”);
36 }
37 //针对Line对象
38 public override void Visit(Line shape) 39 {
40 Console.WriteLine(“针对Line新的操作!”);
41 }
42 }
43
44 //矩形—-相当于“具体节点角色” ConcreteElement
45 public sealed class Rectangle : Shape 46 {
47 public override void Draw() 48 {
49 Console.WriteLine(“矩形我已经画好!”);
50 }
51
52 public override void Accept(ShapeVisitor visitor) 53 {
54 visitor.Visit(this);
55 }
56 }
57
58 //圆形—相当于“具体节点角色”ConcreteElement
59 public sealed class Circle : Shape 60 {
61 public override void Draw() 62 {
63 Console.WriteLine(“圆形我已经画好!”);
64 }
65
66 public override void Accept(ShapeVisitor visitor) 67 {
68 visitor.Visit(this);
69 }
70 }
71
72 //直线—相当于“具体节点角色” ConcreteElement
73 public sealed class Line : Shape 74 {
75 public override void Draw() 76 {
77 Console.WriteLine(“直线我已经画好!”);
78 }
79
80 public override void Accept(ShapeVisitor visitor) 81 {
82 visitor.Visit(this);
83 }
84 }
85
86 //结构对象角色
87 internal class AppStructure 88 {
89 private ShapeVisitor _visitor; 90
91 public AppStructure(ShapeVisitor visitor) 92 {
93 this._visitor = visitor; 94 }
95
96 public void Process(Shape shape) 97 {
98 shape.Accept(_visitor);
99 } 100 } 101
102 class Program 103 { 104 static void Main(string[] args) 105 { 106 //如果想执行新增加的操作
107 ShapeVisitor visitor = new CustomVisitor(); 108 AppStructure app = new AppStructure(visitor); 109
110 Shape shape = new Rectangle(); 111 shape.Draw();//执行自己的操作
112 app.Process(shape);//执行新的操作
113
114
115 shape = new Circle(); 116 shape.Draw();//执行自己的操作
117 app.Process(shape);//执行新的操作
118
119
120 shape = new Line(); 121 shape.Draw();//执行自己的操作
122 app.Process(shape);//执行新的操作
123
124
125 Console.ReadLine(); 126 } 127 } 128 }

复制代码

   

5.10、备忘录模式(Memento Pattern):该模式注重封装对象状态变化,支持状态保存、恢复。现实生活中的手机通讯录备忘录,操作系统备份,数据库备份等都是备忘录模式的应用。

       动机(Motivate):在软件构建过程中,某些对象的状态在转换的过程中,可能由于某种需要,要求程序能够回溯到对象之前处于某个点时的状态。如果使用一些公有接口来让其他对象得到对象的状态,便会暴露对象的细节实现。如何实现对象状态的良好保存与恢复,但同时又不会因此而破坏对象本身的封装性?

       意图(Intent):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态(如果没有这个关键点,其实深拷贝就可以解决问题)。这样以后就可以将该对象恢复到原先保存的状态。

       具体的结构图如下所示

          

       代码实例

复制代码

1 namespace MementoPattern 2 {
3 // 联系人–需要备份的数据,是状态数据,没有操作
4 public sealed class ContactPerson 5 {
6 //姓名
7 public string Name { get; set; }
8
9 //电话号码
10 public string MobileNumber { get; set; }
11 }
12
13 // 发起人–相当于【发起人角色】Originator
14 public sealed class MobileBackOriginator 15 {
16 // 发起人需要保存的内部状态
17 private List _personList; 18
19
20 public List ContactPersonList 21 {
22 get
23 {
24 return this._personList;
25 }
26
27 set
28 {
29 this._personList = value; 30 }
31 }
32 //初始化需要备份的电话名单
33 public MobileBackOriginator(List personList) 34 {
35 if (personList != null)
36 {
37 this._personList = personList; 38 }
39 else
40 {
41 throw new ArgumentNullException(“参数不能为空!”);
42 }
43 }
44
45 // 创建备忘录对象实例,将当期要保存的联系人列表保存到备忘录对象中
46 public ContactPersonMemento CreateMemento() 47 {
48 return new ContactPersonMemento(new List(this._personList));
49 }
50
51 // 将备忘录中的数据备份还原到联系人列表中
52 public void RestoreMemento(ContactPersonMemento memento) 53 {
54 this.ContactPersonList = memento.ContactPersonListBack; 55 }
56
57 public void Show() 58 {
59 Console.WriteLine(“联系人列表中共有{0}个人,他们是:”, ContactPersonList.Count);
60 foreach (ContactPerson p in ContactPersonList) 61 {
62 Console.WriteLine(“姓名: {0} 号码: {1}”, p.Name, p.MobileNumber);
63 }
64 }
65 }
66
67 // 备忘录对象,用于保存状态数据,保存的是当时对象具体状态数据–相当于【备忘录角色】Memeto
68 public sealed class ContactPersonMemento 69 {
70 // 保存发起人创建的电话名单数据,就是所谓的状态
71 public List ContactPersonListBack { get; private set; }
72
73 public ContactPersonMemento(List personList) 74 {
75 ContactPersonListBack = personList; 76 }
77 }
78
79 // 管理角色,它可以管理【备忘录】对象,如果是保存多个【备忘录】对象,当然可以对保存的对象进行增、删等管理处理—相当于【管理者角色】Caretaker
80 public sealed class MementoManager 81 {
82 //如果想保存多个【备忘录】对象,可以通过字典或者堆栈来保存,堆栈对象可以反映保存对象的先后顺序
83 //比如:public Dictionary<string, ContactPersonMemento> ContactPersonMementoDictionary { get; set; }
84 public ContactPersonMemento ContactPersonMemento { get; set; }
85 }
86
87 class Program 88 {
89 static void Main(string[] args)
90 {
91 List persons = new List()
92 {
93 new ContactPerson() { Name=”黄飞鸿”, MobileNumber = “13533332222”},
94 new ContactPerson() { Name=”方世玉”, MobileNumber = “13966554433”},
95 new ContactPerson() { Name=”洪熙官”, MobileNumber = “13198765544”}
96 };
97
98 //手机名单发起人
99 MobileBackOriginator mobileOriginator = new MobileBackOriginator(persons); 100 mobileOriginator.Show(); 101
102 // 创建备忘录并保存备忘录对象
103 MementoManager manager = new MementoManager(); 104 manager.ContactPersonMemento = mobileOriginator.CreateMemento(); 105
106 // 更改发起人联系人列表
107 Console.WriteLine(“-—移除最后一个联系人——–”); 108 mobileOriginator.ContactPersonList.RemoveAt(2); 109 mobileOriginator.Show(); 110
111 // 恢复到原始状态
112 Console.WriteLine(“-——恢复联系人列表——“); 113 mobileOriginator.RestoreMemento(manager.ContactPersonMemento); 114 mobileOriginator.Show(); 115
116 Console.Read(); 117 } 118 } 119 }

复制代码

   

5.11、解释器模式(Interpreter Pattern):该模式注重封装特定领域变化,将特定领域的问题表达为某种语法规则下的句子,然后构建一个解释器来解释这样的句子,从而达到解决问题的目的。C#的编译器,中英翻译工具,正则表达式都是解释器应用的很好例子。

       动机(Motivate):在软件构建过程中,如果某一特定领域的问题比较复杂,类似的模式不断重复出现,如果使用普通的编程方式来实现将面临非常频繁的变化。在这种情况下,将特定领域的问题表达为某种语法规则下的句子,然后构建一个解释器来解释这样的句子,从而达到解决问题的目的。

       意图(Intent):给定一个语言,定义它的文法的一种表示,并定义一种解释器,这个解释器使用该表示来解释语言中的句子。

       具体的结构图如下所示

                           

六、总结

    C#版本23种面向对象的设计模式,终于写完了,今天是一个总结性的文章。各个模式都列了出来,每个模式的【动机】、【意图】和【结构图】也写了出来,可以方便大家查看。重新自己写了一遍,感觉很多都不一样了。理解更深刻了,学无止境,继续前进吧。

之前写过Python的设计模式,由于经常不使用Python,回过头来再看Python的设计模式,有时候感觉并不是那么的显而易见,所以使用c#重新将代码编写一遍,更加清晰明了。

这里借用原来的介绍,对模式做简要说明,模式简易说明和类图,请查看 http://www.cnblogs.com/cotton/category/606629.html

设计模式分为三种类型

  • 创建型模式:简单工厂、工厂方法模式、抽象工厂模式、建造者模式、原型模式、单例模式

  • 结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。

  • 行为型模式:模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。


创建型模式

一、简单工厂模式

模式说明

简单工厂模式又称之为静态工厂方法,属于创建型模式。在简单工厂模式中,可以根据传递的参数不同,返回不同类的实例。简单工厂模式定义了一个类,这个类专门用于创建其他类的实例,这些被创建的类都有一个共同的父类。

模式结构图

代码示例

namespace DesignPattern
{ public class SimpleFactory
{ public static Operation GetOperation(op op, double a, double b)
{ switch (op)
{ case op.add: return new Add(a, b); case op.sub: return new Sub(a, b); case op.mul: return new Mul(a, b); case op.div: return new Div(a, b); default: return new undef(a, b);
}
}
} public enum op
{
add = ‘+’,
sub = ‘-‘,
mul = ‘*‘,
div = ‘/‘ } public abstract class Operation
{ public double a, b; public Operation(double a, double b)
{ this.a = a; this.b = b;
} public abstract double GetResult();
} public class Add : Operation
{ public Add(double a, double b) : base(a, b) { } public override double GetResult()
{ return a + b;
}
} public class Sub : Operation
{ public Sub(double a, double b) : base(a, b) { } public override double GetResult()
{ return a - b;
}
} public class Mul : Operation
{ public Mul(double a, double b) : base(a, b) { } public override double GetResult()
{ return a * b;
}
} public class Div : Operation
{ public Div(double a, double b) : base(a, b) { } public override double GetResult()
{ try { return a / b;
} catch (DivideByZeroException e)
{ throw e;
}
}
} public class undef : Operation
{ public undef(double a, double b) : base(a, b) { } public override double GetResult()
{ throw new NotImplementedException();
}

}

}

View Code

二、工厂方法模式

模式说明

工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法模式让实例化推迟到子类。

和简单工厂区别在于,每个工厂只管生产自己对应的产品,而简单工厂是一个工厂生产各种产品。

模式结构图

代码示例

namespace DesignPattern
{ public interface ILogger
{ void write(string log);
} public class EventLogger : ILogger
{ public void write(string log)
{
Console.WriteLine(“EventLog:” + log);
}
} public class FileLogger : ILogger
{ public void write(string log)
{
Console.WriteLine(“FileLog:” + log);
}
} public interface ILoggerFactory
{
ILogger CreateLogger();
} public class EventLoggerFactory : ILoggerFactory
{ public ILogger CreateLogger()
{ return new EventLogger();
}
} public class FileLoggerFactory : ILoggerFactory
{ public ILogger CreateLogger()
{ return new FileLogger();
}
}
}

View Code

三、抽象工厂模式

模式说明

抽象工厂模式提供一个接口,用于创建相关或者依赖对象的家族,而不需要明确指定具体类。

抽象工厂允许客户端使用抽象的接口来创建一组相关的产品,而不需要关系实际产出的具体产品是什么。这样一来,客户就可以从具体的产品中被解耦。

和工厂方法主要区别于,抽象工厂内要像像定义中说的一样,‘创建一组相关的产品’。

感觉像是(不知道这样理解对否):简单工厂是一个工厂生产多个产品;工厂方法是拆分成子工厂,分别生产各自产品;抽象工厂整合工厂方法和简单工厂,随着子工厂规模变大,也可以生产多个类似产品。

模式结构图

代码示例

namespace DesignPattern
{ //抽象实体
public abstract class absSalary
{ protected double salary; protected double bonus; protected double tax; public absSalary(double sal, double bns, double t)
{ this.salary = sal; this.bonus = bns; this.tax = t;
} public abstract double CalculateTax();
} public class ChineseSalary : absSalary
{ public ChineseSalary(double sal, double bns, double t)
: base(sal, bns, t)
{
} public override double CalculateTax()
{ return (base.salary + base.bonus - 3500) * base.tax;
}
} public class ForeignerSalary : absSalary
{ public ForeignerSalary(double sal, double bonus, double tax)
: base(sal, bonus, tax)
{
} public override double CalculateTax()
{ return (base.salary + base.bonus - 4000) * base.tax;
}
} public abstract class absSocialSecurity
{ protected double SocialSecurity; public absSocialSecurity()
{ this.SocialSecurity = 0;
} public virtual double GetSocialSecurity()
{ return this.SocialSecurity;
}
} public class ChineseSocialSecurity : absSocialSecurity
{ public ChineseSocialSecurity(double socialsecurity)
: base()
{ base.SocialSecurity = socialsecurity < 1000 ? 1000 : socialsecurity;
}
} public class ForeignerSocialSecurity : absSocialSecurity
{ public ForeignerSocialSecurity(double socialsecurity)
: base()
{ base.SocialSecurity = socialsecurity < 1500 ? 1500 : socialsecurity;
}
} //抽象工厂,生产一系列产品(多个Create方法,分别对应不同产品)
public interface AbstractFactory
{
absSalary CreateSalary(double sal, double bonus, double tax);
absSocialSecurity CreateSocialSecurity(double socialsecurity);
} public class ChineseFactory : AbstractFactory
{ public absSalary CreateSalary(double sal, double bonus, double tax)
{ return new ChineseSalary(sal, bonus, tax);
} public absSocialSecurity CreateSocialSecurity(double socialsecurity)
{ return new ChineseSocialSecurity(socialsecurity);
}
} public class ForeignerFactory : AbstractFactory
{ public absSalary CreateSalary(double sal, double bonus, double tax)
{ return new ForeignerSalary(sal, bonus, tax);
} public absSocialSecurity CreateSocialSecurity(double socialsecurity)
{ return new ForeignerSocialSecurity(socialsecurity);
}
}
}

View Code

四、创建者模式

模式说明

建造者模式将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。

建造者模式构建复杂对象就像造汽车一样,是一个一个组件一个一个步骤创建出来的,它允许用户通过制定的对象类型和内容来创建他们,但是用户并不需要知道这个复杂对象是如何构建的,它只需要明白通过这样做我可以得到一个完整的复杂对象实例。

和工厂方法很像,创造者是一个builder内每个方法分别创建产品零部件,而工厂方法是每个factory生产一个产品。如果把builder的零部件当做一个完整产品呢?是不是就像 builder又再一次封装了factory~ 

模式结构图

代码示例

namespace DesignPattern
{ public class Meal
{ private string food; private string drink; public Meal() { } public void setFood(string food)
{ this.food = food;
} public void setDrink(string drink)
{ this.drink = drink;
} public string getFood()
{ return this.food;
} public string getDrink()
{ return this.drink;
}
} //建造者,分别建造不同部件,然后返回整体
public abstract class Builder
{ protected Meal meal = new Meal(); public abstract void buildFood(); public abstract void buildDrink(); public Meal GetMeal()
{ return meal;
}
} public class MealABuilder : Builder
{ public override void buildFood()
{
meal.setFood(“A food”);
} public override void buildDrink()
{
meal.setDrink(“A drink”);
}
} public class MealBBuilder : Builder
{ public override void buildFood()
{
meal.setFood(“B food”);
} public override void buildDrink()
{
meal.setDrink(“B drink”);
}
} public class Waitor
{ public void PrepareMeal(Builder builder)
{
builder.buildDrink();
builder.buildFood();
}
}
}

View Code

五、原型模式

模式说明

所谓原型模式就是用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。

说到复制,就会有深/浅两种复制,这是面向对象的值类型和引用类型的差异,具体不作说明

模式结构图

代码示例

namespace DesignPattern
{
[Serializable] public class other
{ public int value { get; set; } public other()
{
value = 10;
}
}

\[Serializable\] public abstract class ColorPrototype
{ public int red { get; set; } public int green { get; set; } public int blue { get; set; } public other o = new other(); //浅拷贝
    public virtual ColorPrototype Clone()
    { return (ColorPrototype)this.MemberwiseClone();
    }
} public class Red : ColorPrototype
{ public override ColorPrototype Clone()
    { return base.Clone();
    }
}

\[Serializable\] public class Green : ColorPrototype
{ public override ColorPrototype Clone()
    {
        BinaryFormatter formatter \= new BinaryFormatter();
        MemoryStream stream \= new MemoryStream();
        formatter.Serialize(stream, this);
        stream.Position \= 0;
        ColorPrototype obj \= (ColorPrototype)formatter.Deserialize(stream); return obj;
    }
}

}

View Code

六、单例模式

代码示例

namespace DesignPattern
{ public class Singleton
{ private int cnt = 0; private static Singleton instance = null; private volatile static Singleton safeInstance = null; private static readonly object lockedobj = new object(); private Singleton()
{
} public static Singleton GetInstance()
{ if (instance == null) instance = new Singleton(); return instance;
} public static Singleton GetSafeInstance()
{ if (safeInstance == null)
{ lock (lockedobj)
{ if (safeInstance == null)
{
safeInstance = new Singleton();
}
}
} return safeInstance;
} public void count()
{
cnt += 1;
} public int getCnt()
{ return cnt;
}
}

}

View Code


结构型模式

七、适配器模式

模式说明

适配器模式就是将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。

在适配器模式中,我们可以定义一个包装类,包装不兼容接口的对象,这个包装类就是适配器,它所包装的对象就是适配者。

适配器提供给客户需要的接口,适配器的实现就是将客户的请求转换成对适配者的相应的接口的引用。也就是说,当客户调用适配器的方法时,适配器方法内部将调用 适配者的方法,客户并不是直接访问适配者的,而是通过调用适配器方法访问适配者。因为适配器可以使互不兼容的类能够“合作愉快”。

模式结构图

代码示例

注:此处ILogger接口使用了【工厂方法模式】定义的接口

namespace DesignPattern
{ public interface IAdaptor
{ void writelog(string log);
} public class LogAdaptor : IAdaptor
{
ILogger logger; public LogAdaptor(ILogger logger)
{ this.logger = logger;
} public void writelog(string log)
{ this.logger.write(log);
}
}
}

View Code

八、桥接模式

模式说明

桥接模式即将抽象部分与它的实现部分分离开来,使他们都可以独立变化。

桥接模式将继承关系转化成关联关系,它降低了类与类之间的耦合度,减少了系统中类的数量,也减少了代码量。

个人感觉,代理模式、适配器模式和桥接模式相类似,代理模式是一个代理对外表示一个特定的类,适配器模式相当于一个适配器代理多个类,而桥接模式则更加适用于多个对多个的时候

模式结构图

代码示例

namespace DesignPattern
{ public abstract class Color
{ public string name { get; set; }
} public abstract class Shape
{ private Color color; public string name { get; set; } public void SetColor(Color c)
{
color = c;
} public void Draw()
{
Console.WriteLine(“draw shape {0} with color {1}”, this.name, this.color.name);
}
} public class White : Color
{ public White()
{ this.name = “white”;
}
} public class Blue : Color
{ public Blue()
{ this.name = “blue”;
}
} public class Squre : Shape
{ public Squre()
{ this.name = “squre”;
}
} public class Circle : Shape
{ public Circle()
{ this.name = “circle”;
}
}
}

View Code

九、装饰者模式

模式说明

装饰者模式装饰者模式可以动态地给一个对象增加一些额外的职责。就增加功能来说,装饰者模式相比生成子类更为灵活。

模式结构图

代码示例

namespace DesignPattern
{ public abstract class Car
{ public string color { get; set; } public int compartment { get; set; } public void run()
{
Console.WriteLine(color + “ “ + compartment + “ compartment “ + this.GetType().Name + “ is running!”);
}
} public class Benz:Car
{ public Benz()
{ base.color = “black”; base.compartment = 1;
}
} public class QQ:Car
{ public QQ()
{ base.color = “black”; base.compartment = 1;
}
} public abstract class Decorator : Car
{ public Car car; public Decorator(Car car)
{ this.car = car;
}
} public class ColorDecorator:Decorator
{ //一般在构造函数内完成属性的修改(装饰),这里单独加了一个decorate方法
public ColorDecorator(Car car):base(car)
{
} public Car decorate(string color)
{ base.car.color = color; return base.car;
}
} public class CompartmentDecorator : Decorator
{ public CompartmentDecorator(Car car)
: base(car)
{
} public Car decorate(int compartment)
{ base.car.compartment = compartment; return base.car;
}
}
}

View Code

十、组合模式

模式说明

组合模式组合多个对象形成树形结构以表示“整体-部分”的结构层次。

组合模式对单个对象(叶子对象)和组合对象(组合对象)具有一致性,它将对象组织到树结构中,可以用来描述整体与部分的关系。同时它也模糊了简单元素(叶 子对象)和复杂元素(容器对象)的概念,使得客户能够像处理简单元素一样来处理复杂元素,从而使客户程序能够与复杂元素的内部结构解耦。

模式结构图

代码示例

namespace DesignPattern
{ public abstract class File
{ protected string name; public File(string name)
{ this.name = name;
} public abstract void Display();
} public class Folder : File
{
IList list; public Folder(string name)
: base(name)
{
list = new List();
} public void AddFile(File file)
{
list.Add(file);
} public void RemoveFile(File file)
{
list.Remove(file);
} public override void Display()
{
Console.WriteLine(“folder:” + this.name); foreach (File f in list)
{
f.Display();
}
}
} public class ImageFile : File
{ public ImageFile(string name)
: base(name)
{
} public override void Display()
{
Console.WriteLine(“ImageFile:” + this.name);
}
}
}

View Code

十一、外观模式

模式说明

所谓外观模式就是提供一个统一的接口,用来访问子系统中的一群接口。

模式结构图

代码示例

namespace DesignPattern
{ public class Facade
{
Light _light = new Light();
TV _tv = new TV(); public void off()
{
_light.on();
_tv.off();
} public void on()
{
_tv.on();
_light.off();
}
} class Light
{ public void on()
{
Console.WriteLine(“light on!”);
} public void off()
{
Console.WriteLine(“light off!”);
}
} class TV
{ public void on()
{
Console.WriteLine(“tv on!”);
} public void off()
{
Console.WriteLine(“tv off!”);
}
}
}

View Code

十二、享元模式

模式说明

所谓享元模式就是运行共享技术有效地支持大量细粒度对象的复用。系统使用少量对象,而且这些都比较相似,状态变化小,可以实现对象的多次复用。

FlyweightFactory内定义的实体是不变的(共享的),传入参数是状态变化。

缓存形式,传入参数已经被缓存则直接返回,否则创建参数对应实体,放入缓存并返回该新实体

模式结构图

代码示例

namespace DesignPattern
{ public class FlyweightFactory
{ static Dictionary<string, IFlyweight> pendic = new Dictionary<string, IFlyweight>(); public IFlyweight getPen(string color)
{ if (pendic.ContainsKey(color))
{ return pendic[color];
} else {
IFlyweight pen = new ConcreteFlyweight(color);
pendic.Add(color, pen); return pen;
}
} public void Display()
{ foreach (KeyValuePair<string,IFlyweight> pair in pendic)
{
Console.WriteLine(pair.Value.GetType().FullName + “:” + pair.Key);
}
}
} public interface IFlyweight
{ string GetColor();
}; public class ConcreteFlyweight : IFlyweight
{ string color; public ConcreteFlyweight(string color)
{ this.color = color;
} public string GetColor()
{ return this.color;
}
}
}

View Code

十三、代理模式

模式说明

代理模式就是给一个对象提供一个代理,并由代理对象控制对原对象的引用。

在代理模式中,“第三者”代理主要是起到一个中介的作用,它连接客户端和目标对象。

模式结构图

代码示例

namespace DesignPattern
{ public class Girl
{ public string name { get; set; } public Girl(string name)
{ this.name = name;
}
} public class Boy
{ private Girl girl; public string name { get; set; } public Boy(string name, Girl girl)
{ this.name = name; this.girl = girl;
} public void GiveFlower()
{
Console.WriteLine(“boy {0} give flower to girl {1}”, this.name, this.girl.name);
}
} public class Proxy
{ private Boy boy; public Proxy(Boy boy)
{ this.boy = boy;
} public void GiveFlower()
{ this.boy.GiveFlower();
}
}
}

View Code


行为型模式

十四、迭代器模式

代码示例

namespace DesignPattern
{ public class Persons : IEnumerable
{ string[] m_Names; public Persons(params string[] Names)
{
m_Names = new string[Names.Length];

        Names.CopyTo(m\_Names, 0);
    } public IEnumerator GetEnumerator()
    { foreach (string s in m\_Names)
        { yield return s;
        }
    } public int Length { get { return m\_Names.Length; } } public string this\[int i\]
    { get { return m\_Names\[i\];
        } set {
            m\_Names\[i\] \= value;
        }
    }
}

}

View Code

十五、解释器模式

模式说明

所谓解释器(Interpreter)就是将一系列指令转化成代码,能够执行的代码。Interpreter本来就有翻译的意思。GoF给它的定义是:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

模式结构图

代码示例

namespace DesignPattern
{ public class Context
{ private string msg; public Context(string msg)
{ this.msg = msg;
} public string GetMsg()
{ return this.msg;
}
} public interface Interpreter
{ string Interprete(Context context);
} public class UpperInterpreter : Interpreter
{ public string Interprete(Context context)
{ string msg = context.GetMsg(); return msg.ToUpperInvariant();
}
} public class LowerInterpreter : Interpreter
{ public string Interprete(Context context)
{ string msg = context.GetMsg(); return msg.ToLowerInvariant();
}
}
}

View Code

十六、命令模式

模式说明

将请求封装成对象,从而使可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。

模式结构图

代码示例

namespace DesignPattern
{ //接受命令的对象
public class CDMachine
{ public void on()
{
Console.WriteLine(“CD Machine turns on!”);
} public void off()
{
Console.WriteLine(“CD Machine turns off!”);
}
} //定义命令
public abstract class Command
{ public abstract void Execute(CDMachine cdMachine);
} public class TurnonCommand : Command
{ public override void Execute(CDMachine cdMachine)
{
cdMachine.on();
}
} public class TurnoffCommand : Command
{ public override void Execute(CDMachine cdMachine)
{
cdMachine.off();
}
} //发送命令的对象
public class Controller
{ //遥控的功能 — 可发送的命令
private TurnonCommand turnonCommand; private TurnoffCommand turnoffCommand; public Controller(TurnonCommand turnonCommand, TurnoffCommand turnoffCommand)
{ this.turnonCommand = turnonCommand; this.turnoffCommand = turnoffCommand;
} public void turnOn(CDMachine cdMachine)
{ this.turnonCommand.Execute(cdMachine);
} public void turnOff(CDMachine cdMachine)
{ this.turnoffCommand.Execute(cdMachine);
}
}
}

View Code

十七、中介者模式

模式说明

所谓中介者模式就是用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

模式结构图

代码示例

namespace DesignPattern
{ public abstract class Person
{ public string name; public Mediator mediator; public Person(string name, Mediator mediator)
{ this.name = name; this.mediator = mediator;
} public void Contact(string msg)
{ //参数 this 代表 消息来自我
this.mediator.SendMsg(msg, this);
} internal void GetMsg(string msg)
{
Console.WriteLine(this.name + “ 收到消息:” + msg);
}
} public class HouseOwner : Person
{ public HouseOwner(string name, Mediator mediator) : base(name, mediator) { }
} public class Tenant : Person
{ public Tenant(string name, Mediator mediator) : base(name, mediator) { }
} public interface Mediator
{ void SendMsg(string msg, Person p);
} public class ConcreteMediator : Mediator
{
HouseOwner houseOwner;
Tenant tenant; public ConcreteMediator()
{
} public void SetHouseOwner(HouseOwner houseOwner)
{ this.houseOwner = houseOwner;
} public void SetTenant(Tenant tenant)
{ this.tenant = tenant;
} public void SendMsg(string msg, Person p)
{ if (p.GetType() == houseOwner.GetType())
{
tenant.GetMsg(msg);
} else {
houseOwner.GetMsg(msg);
}
}
}
}

View Code

十八、备忘录模式

模式说明

所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。

模式结构图

代码示例

namespace DesignPattern
{ public class Memonto
{ public int blood { get; set; } public int magic { get; set; }
} public class Caretaker
{ private Memonto memonto; public void SetMemonto(Memonto memonto)
{ this.memonto = memonto;
} public Memonto getMemonto()
{ return this.memonto;
}
} public class Original
{ public int blood { get; set; } public int magic { get; set; } public Memonto SaveMemonto()
{ return new Memonto() { blood = this.blood, magic = this.magic };
} public void RestoreMemonto(Memonto memonto)
{ this.blood = memonto.blood; this.magic = memonto.magic;
} public void display()
{
Console.WriteLine(“blood:” + this.blood + “\tmagic:” + this.magic);
}
}
}

View Code

十九、观察者模式

模式说明

定义了一种一对多的关系,让多个观察对象同时监听一个主题对象,当主题对象状态发生变化时会通知所有观察者。

模式结构图

代码示例

public interface Observer
{ void Update(Subject subject);
} public abstract class Subject
{
List obsList = new List(); public void AddObserver(Observer observer)
{
obsList.Add(observer);
} public void RemoveObserver(Observer observer)
{
obsList.Remove(observer);
} public void notity()
{ foreach (Observer o in obsList)
{
o.Update(this);
}
} private string _state; public void SetState(string state)
{ this._state = state;
} public string GetState()
{ return this._state;
}
} public class ConcreteSubject : Subject
{
} public class ConcreteObserver1 : Observer
{ public void Update(Subject subject)
{
Console.WriteLine(“ConcreteObserver1 get notice:” + subject.GetState());
}
} public class ConcreteObserver2 : Observer
{ public void Update(Subject subject)
{
Console.WriteLine(“ConcreteObserver2 get notice:” + subject.GetState());
}
}

View Code

//事件委托的方式
public delegate void updateDelegate(Subject subject); public class EventSubjet : Subject
{ public event updateDelegate UpdateHandler; public void EventNotify()
{
OnUpdate();
} private void OnUpdate()
{ if (UpdateHandler != null)
{
UpdateHandler(this);
}
}
}

View Code

二十、状态模式

模式说明

当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。

模式结构图

代码示例

namespace DesignPattern
{ public interface IState
{ void display();
} public class WorkState:IState
{ public void display()
{
Console.WriteLine(“Working State”);
}
} public class RestState:IState
{ public void display()
{
Console.WriteLine(“Rest State”);
}
} public class Programmer
{
IState _state; public void Doing(DateTime dt)
{ if(dt.Hour<7 || dt.Hour > 22)
{
_state = new RestState();
} else {
_state = new WorkState();
}
_state.display();
}
}
}

View Code

二十一、模板模式

模式说明

定义一个操作中的算法的骨架,而将步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义算法的某些特定步骤。

模式结构图

代码示例

namespace DesignPattern
{ public abstract class Template
{ protected void boilWater()
{
Console.WriteLine(“boil water”);
} protected virtual void brew()
{
Console.WriteLine(“brew”);
} protected void pourInCup()
{
Console.WriteLine(“pour into cup”);
} protected virtual void addOther()
{
Console.WriteLine(“add other”);
} public void makeBeverage()
{
boilWater();
brew();
pourInCup();
addOther();
}
} public class Tea : Template
{ protected override void brew()
{
Console.WriteLine(“tea”);
} protected override void addOther()
{
Console.WriteLine(“add lemon”);
}
} public class Coffee : Template
{ protected override void brew()
{
Console.WriteLine(“coffee”);
} protected override void addOther()
{
Console.WriteLine(“add sugar”);
}
}
}

View Code

二十二、策略模式

模式说明

定义算法家族并且分别封装,它们之间可以相互替换而不影响客户端。

模式结构图

代码示例

namespace DesignPattern
{ public abstract class OrderStrategy
{ public List<int> orderList; public abstract void Order(); public void display()
{ foreach (int i in orderList)
{
Console.Write(i + “\t”);
}
Console.WriteLine();
}
} public class BubbleStrategy : OrderStrategy
{ public override void Order()
{ for (int i = 0; i < orderList.Count; i++)
{ for (int j = i + 1; j < orderList.Count; j++)
{ if (orderList[i] < orderList[j])//冒泡降序 小的冒上去
{ int temp = orderList[i];
orderList[i] = orderList[j];
orderList[j] = temp;
}
}
}
}
} public class SelectionStrategy : OrderStrategy
{ public override void Order()
{ for (int i = 0; i < orderList.Count; i++)
{ int smallvalue = orderList[i]; int smallposition = i; for (int j = i + 1; j < orderList.Count; j++)
{ if (orderList[j] < smallvalue)
{
smallposition = j;
smallvalue = orderList[j];
}
} //将最小值放到当前要排序的位置
orderList[smallposition] = orderList[i];
orderList[i] = smallvalue;
}
}
} public class InsertionStrategy : OrderStrategy
{ public override void Order()
{ for (int i = 1; i < orderList.Count; i++)
{ int temp = orderList[i];//当前要插入的值,相当于位置I是个空白位置,供对比进行后移
int j = i; //j之前的序列已经排序好,选一个位置把当前值插入

            while (j > 0)
            { //i从1开始,是因为这里j要比较前一个值
                if (temp < orderList\[j - 1\]) //插入过程中,每次比较的值大于当前值则向后移动

{
orderList[j] = orderList[j-1];
j--;
} else { break;
}
} //找到位置(break)或者循环正常结束(说明当前值最小)则赋值。
orderList[j] = temp;
}
}
} public class StrategyManager
{
OrderStrategy strategy; public void SetStrategy(OrderStrategy strategy)
{ this.strategy =strategy;
} public void Sort(List<int> list)
{ this.strategy.orderList = list; this.strategy.Order(); this.strategy.display();
}
}
}

View Code

二十三、访问者模式

模式说明

访问者模式即表示一个作用于某对象结构中的各元素的操作,它使我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

模式结构图

代码示例

namespace DesignPattern
{ public interface Element
{ void accept(Visitor visitor);
} public class ConcreteElementA : Element
{ string name; public void SetName(string name)
{ this.name = name;
} public string GetName()
{ return this.name;
} public void accept(Visitor visitor)
{
visitor.visitElementA(this);
}
} public class ConcreteElementB : Element
{ int ID; public void SetID(int id)
{ this.ID = id;
} public int GetID()
{ return this.ID;
} public void accept(Visitor visitor)
{
visitor.visitElementB(this);
}
} public interface Visitor
{ void visitElementA(ConcreteElementA ea); void visitElementB(ConcreteElementB eb);
} public class ConcreteVisitorA : Visitor
{ public void visitElementA(ConcreteElementA ea)
{
Console.WriteLine(“ConcreteVisitorA visit ElemantA:” + ea.GetName());
} public void visitElementB(ConcreteElementB eb)
{
Console.WriteLine(“ConcreteVisitorA visit ElemantB:” + eb.GetID());
}
} public class ConcreteVisitorB : Visitor
{ public void visitElementA(ConcreteElementA ea)
{
Console.WriteLine(“ConcreteVisitorB visit ElemantA:” + ea.GetName());
} public void visitElementB(ConcreteElementB eb)
{
Console.WriteLine(“ConcreteVisitorB visit ElemantB:” + eb.GetID());
}
} public class objectStructure
{
List elementlist = new List(); public void Attach(Element e)
{
elementlist.Add(e);
} public void Dettach(Element e)
{
elementlist.Remove(e);
} public void Accept(Visitor visit)
{ foreach(Element e in elementlist)
{
e.accept(visit);
}
}
}
}

View Code

二十四、责任链模式

模式说明

避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止,这就是职责链模式。

模式结构图

代码示例

namespace DesignPattern
{ public class Request
{ int days; string name; public Request(int days, string name)
{ this.days = days; this.name = name;
} public int GetDays()
{ return days;
} public string GetName()
{ return name;
}

} public abstract class Responsibility
{ protected Responsibility responsibility; public Responsibility(Responsibility responsibility)
    { this.responsibility = responsibility;
    } public abstract void HandleRequest(Request request);
} public class Leader : Responsibility
{ public Leader(Responsibility responsibility)
        : base(responsibility)
    { } public override void HandleRequest(Request request)
    { if (request.GetDays() < 3)
        {
            Console.WriteLine("Leader passed {0}'s {1} days request", request.GetName(), request.GetDays());
        } else { this.responsibility.HandleRequest(request);
        }
    }
} public class Department : Responsibility
{ public Department(Responsibility responsibility)
        : base(responsibility)
    { } public override void HandleRequest(Request request)
    { if (request.GetDays() < 8)
        {
            Console.WriteLine("Department passed {0}'s {1} days request", request.GetName(), request.GetDays());
        } else { this.responsibility.HandleRequest(request);
        }
    }
} //责任链终端必须处理
public class Boss : Responsibility
{ public Boss() : base(null) { } public override void HandleRequest(Request request)
    { if (request.GetDays() < 15)
        {
            Console.WriteLine("Boss passed {0}'s {1} days request", request.GetName(), request.GetDays());
        } else {
            Console.WriteLine("Boss refused {0}'s {1} days request", request.GetName(), request.GetDays());
        }
    }
}

}

View Code

主程序(注:调用顺序与该列表顺序不一致)

namespace DesignPattern
{ class Program
{ static void Main(string[] args)
{ //简单工厂
Console.WriteLine(“简单工厂”);
Console.WriteLine(SimpleFactory.GetOperation(op.add, 1.1, 2.2).GetResult()); //工厂方法
Console.WriteLine(“\n工厂方法”);
ILoggerFactory factorymethod = new EventLoggerFactory();
ILogger iLogger = factorymethod.CreateLogger();
iLogger.write(“123”);

        factorymethod \= new FileLoggerFactory();
        iLogger \= factorymethod.CreateLogger();
        iLogger.write("321"); //抽象工厂
        Console.WriteLine("\\n抽象工厂");
        AbstractFactory absFactory \= new ChineseFactory();
        absSalary chSalary \= absFactory.CreateSalary(10000, 8000, 0.12);
        absSocialSecurity chScSc \= absFactory.CreateSocialSecurity(1200);
        Console.WriteLine(chSalary.CalculateTax());
        Console.WriteLine(chScSc.GetSocialSecurity());

        absFactory \= new ForeignerFactory();
        chSalary \= absFactory.CreateSalary(10000, 8000, 0.12);
        chScSc \= absFactory.CreateSocialSecurity(1200);
        Console.WriteLine(chSalary.CalculateTax());
        Console.WriteLine(chScSc.GetSocialSecurity()); //创造者模式
        Console.WriteLine("\\n创造者模式");
        Waitor waiter \= new Waitor();
        Builder b1 \= new MealABuilder();
        Builder b2 \= new MealBBuilder();

        waiter.PrepareMeal(b1);
        Meal ma \= b1.GetMeal();
        Console.WriteLine(ma.getFood() \+ "\\t" + ma.getDrink());

        waiter.PrepareMeal(b2);
        Meal mb \= b2.GetMeal();
        Console.WriteLine(mb.getFood() \+ "\\t" + mb.getDrink()); //原型模式
        Console.WriteLine("\\n原型模式");
        Red r \= new Red();
        r.o.value \= 20;//改变引用值
        ColorPrototype RCopy = r.Clone();
        RCopy.o.value \= 30;
        Console.WriteLine(r.o.value);//30 浅拷贝,指向同一个应用对象,一处改变,都改变

Green g = new Green();
g.o.value = 20;
ColorPrototype GCopy = g.Clone();
GCopy.o.value = 30;
Console.WriteLine(g.o.value);//20 深拷贝,引用对象独立 //单例模式
Console.WriteLine(“\n单例模式”);
Task[] tArr = new Task[]{
Task.Run(() => Singleton.GetInstance().count()),
Task.Run(() => Singleton.GetInstance().count()),
Task.Run(() => Singleton.GetInstance().count()),
Task.Run(() => Singleton.GetInstance().count()),
Task.Run(() => Singleton.GetInstance().count()),
Task.Run(() => Singleton.GetInstance().count()),
Task.Run(() => Singleton.GetInstance().count()),
Task.Run(() => Singleton.GetInstance().count()),
Task.Run(() => Singleton.GetInstance().count()),
Task.Run(() => Singleton.GetInstance().count())
};
Singleton.GetInstance().count();
Task.WaitAll(tArr);
Console.WriteLine(“danger:” + Singleton.GetInstance().getCnt());

        Task\[\] tArrSafe \= new Task\[\]{
        Task.Run(() \=> Singleton.GetSafeInstance().count()),
        Task.Run(() \=> Singleton.GetSafeInstance().count()),
        Task.Run(() \=> Singleton.GetSafeInstance().count()),
        Task.Run(() \=> Singleton.GetSafeInstance().count()),
        Task.Run(() \=> Singleton.GetSafeInstance().count()),
        Task.Run(() \=> Singleton.GetSafeInstance().count()),
        Task.Run(() \=> Singleton.GetSafeInstance().count()),
        Task.Run(() \=> Singleton.GetSafeInstance().count()),
        Task.Run(() \=> Singleton.GetSafeInstance().count()),
        Task.Run(() \=> Singleton.GetSafeInstance().count())
        };
        Singleton.GetSafeInstance().count();
        Task.WaitAll(tArrSafe);
        Console.WriteLine("safe:" + Singleton.GetSafeInstance().getCnt()); //迭代器
        Console.WriteLine("\\n迭代器");
        Persons ps \= new Persons(new string\[\] { "1", "2", "3" }); foreach (string name in ps)
        {
            Console.WriteLine(name);
        } for (var i = 0; i < ps.Length; i++)
        {
            Console.WriteLine(ps\[i\]);
        } //适配器模式
        Console.WriteLine("\\n适配器模式");
        EventLogger eLog \= new EventLogger();
        FileLogger fLog \= new FileLogger();
        LogAdaptor adapter \= new LogAdaptor(eLog);
        adapter.writelog("123123");
        adapter \= new LogAdaptor(fLog);
        adapter.writelog("123123"); //代理模式
        Console.WriteLine("\\n代理模式");
        Girl girl \= new Girl("Han MeiMei");
        Boy boy \= new Boy("Li Lei", girl);
        Proxy proxy \= new Proxy(boy);
        proxy.GiveFlower(); //桥接模式
        Console.WriteLine("\\n桥接模式");
        Color blue \= new Blue();
        Color white \= new White();
        Shape squre \= new Squre();
        Shape circle \= new Circle();
        squre.SetColor(blue);
        squre.Draw();
        circle.SetColor(white);
        circle.Draw(); //组合模式
        Console.WriteLine("\\n组合模式");
        Folder folder1 \= new Folder("study");
        File img \= new ImageFile("img");
        folder1.AddFile(img);
        Folder folder2 \= new Folder("c#");
        folder2.AddFile(img);
        folder1.AddFile(folder2);
        folder1.Display(); //解释器模式
        Console.WriteLine("\\n解释器模式");
        Context context \= new Context("ABcdeFG");
        UpperInterpreter ui \= new UpperInterpreter(); string inprResut = ui.Interprete(context);
        Console.WriteLine("up:" + inprResut);

        LowerInterpreter li \= new LowerInterpreter();
        inprResut \= li.Interprete(context);
        Console.WriteLine("low:" + inprResut); //外观模式
        Console.WriteLine("\\n外观模式");
        Facade facade \= new Facade();
        Console.WriteLine("电视打开,电灯关闭!");
        facade.on();
        Console.WriteLine("3秒后电灯打开,电视关闭");
        Thread.Sleep(3000);
        facade.off(); //享元模式
        Console.WriteLine("\\n享元模式");
        FlyweightFactory flyfactory \= new FlyweightFactory();
        IFlyweight flyweidht \= flyfactory.getPen("red");
        Console.WriteLine(flyweidht.GetColor());
        flyweidht \= flyfactory.getPen("blue");
        Console.WriteLine(flyweidht.GetColor());
        flyweidht \= flyfactory.getPen("red");
        Console.WriteLine(flyweidht.GetColor());
        flyfactory.Display(); //责任链模式
        Console.WriteLine("\\n责任链模式");
        Request request \= new Request(20, "wang");
        Boss boss \= new Boss();
        Department department \= new Department(boss);
        Leader leader \= new Leader(department);
        leader.HandleRequest(request); //命令模式
        Console.WriteLine("\\n命令模式");
        CDMachine cd \= new CDMachine();
        TurnoffCommand off \= new TurnoffCommand();
        TurnonCommand on \= new TurnonCommand();
        Controller ctrl \= new Controller(on, off); //遥控器发送命令到cd机

ctrl.turnOn(cd);
ctrl.turnOff(cd); //中介者模式
Console.WriteLine(“\n中介者模式”); //中介单独存在 //Mediator mediator = new ConcreteMediator();
ConcreteMediator mediator = new ConcreteMediator(); //房主和租客寻找中介
HouseOwner houseOwner = new HouseOwner(“houseowner”, mediator);
Tenant tenant = new Tenant(“tenant”, mediator); //中介给他们搭建链接
mediator.SetHouseOwner(houseOwner);
mediator.SetTenant(tenant);

        houseOwner.Contact("出租房");
        tenant.Contact("租房"); //备忘录模式
        Console.WriteLine("\\n备忘录模式");
        Caretaker caretaker \= new Caretaker();
        Original original \= new Original();
        original.blood \= 100;
        original.magic \= 100;
        original.display();
        caretaker.SetMemonto(original.SaveMemonto());
        original.blood \= 50;
        original.magic \= 50;
        original.display();
        original.RestoreMemonto(caretaker.getMemonto());
        original.display(); //观察者模式
        Console.WriteLine("\\n观察者模式");
        Subject subject \= new ConcreteSubject();
        subject.SetState("start");
        Observer o1 \= new ConcreteObserver1();
        Observer o2 \= new ConcreteObserver2();
        subject.AddObserver(o1);
        subject.AddObserver(o2);
        subject.notity();
        subject.SetState("change");
        subject.notity(); //Subject eventSubject = new EventSubjet();
        EventSubjet eventSubject = new EventSubjet();
        eventSubject.UpdateHandler += o1.Update;
        eventSubject.UpdateHandler += o2.Update;
        eventSubject.SetState("event");
        eventSubject.EventNotify(); //状态模式
        Console.WriteLine("\\n状态模式");
        Programmer programmer \= new Programmer();
        Console.WriteLine(DateTime.Now \+ "程序员正在做什么呢?");
        programmer.Doing(DateTime.Now);
        Console.WriteLine(DateTime.Now.AddHours(\-10) + "程序员正在做什么呢?");
        programmer.Doing(DateTime.Now.AddHours(\-10)); //策略模式
        Console.WriteLine("\\n策略模式");
        BubbleStrategy bubble \= new BubbleStrategy();
        SelectionStrategy selection \= new SelectionStrategy();
        InsertionStrategy insertion \= new InsertionStrategy(); var list = new List<int\>() { 3, 1, 6, 2, 5 };
        StrategyManager manager \= new StrategyManager();
        manager.SetStrategy(bubble);
        manager.Sort(list);
        manager.SetStrategy(selection);
        manager.Sort(list);
        manager.SetStrategy(insertion);
        manager.Sort(list); //模板模式
        Console.WriteLine("\\n模板模式");
        Template tea \= new Tea();
        tea.makeBeverage();
        Template coffee \= new Coffee();
        coffee.makeBeverage(); //访问者模式
        Console.WriteLine("\\n访问者模式");
        ConcreteElementA elementA \= new ConcreteElementA();
        elementA.SetName("ea");
        ConcreteElementB elementB \= new ConcreteElementB();
        elementB.SetID(2);
        objectStructure structure \= new objectStructure();
        structure.Attach(elementA);
        structure.Attach(elementB);

        Visitor visitorA \= new ConcreteVisitorA();
        Visitor visitorB \= new ConcreteVisitorB();

        structure.Accept(visitorA);
        structure.Accept(visitorB); //装饰者模式
        Console.WriteLine("\\n装饰者模式");
        Car car \= new Benz();
        car \= new ColorDecorator(car).decorate("red");
        car.run();
        car \= new CompartmentDecorator(car).decorate(3);
        car.run();
    }
}

}

View Code


该博文出发点是使用面向对象的语言,使用简单的示例简要概括说明各个模式的应用,已备不时之需。

提交了一次竟然没通过,从首页移除

重新发送一遍,是不是能通过呢? 

 为避免某某些折叠代码不能正常打开,附源码下载地址