定义:Strategy pattern defines a one-to-many dependency between objects so that when one object changes state, all its dependencies are notified and updated automatically.
背景介绍:Team foundation server中提供了一个非常适用的功能就是project alerts.在team explorer中打开一个项目,右键菜单中有一个project alerts菜单项,点击会弹出一个对话框,你可以选择自己要接受的project alert的类别(发送project alert的一些规则)和自己的email地址,当当前项目发生的变化或者发生的事件满足上述你订阅的规则时,系统就会给你发送邮件通知你TFS中你所关心的项目发生了怎样的变化。想着跟踪项目中的变化,这应该是最方便的途径了,订阅了以后TFS会自动通知你相关的信息,你再也不用自己逐个文件查看是否发生了改变了。如果不再需要跟踪项目信息,只要退订project alerts就可以了。

VS自动生成的类图:


实例代码:
查看代码
1
using System;
2
using System.Collections.Generic;
3
using System.Text;
4
using System.Collections;
5
6
namespace ObserverDemo
7

{
8
9
/**//// <summary>
10
/// Project alert type.
11
/// </summary>
12
[Flags]
13
public enum ChangeType
14
{
15
workItemChanged = 0x0001,
16
checkedIn = 0x0002,
17
qualityChanged = 0x0004,
18
buildCompleted = 0x0008
19
}
20
21
/**//// <summary>
22
/// Supply a set of rules that every project should apply.
23
/// Of course it is designed for observer pattern.
24
/// </summary>
25
public interface IProjectTemplateStoredInTFS
26
{
27
void SubscribeProjectAlerts(ReceiverBase receiver);
28
void UnsubscribeProjectAlerts(ReceiverBase receiver);
29
void SendProjectAlert();
30
}
31
32
/**//// <summary>
33
/// Base class for receivers.
34
/// </summary>
35
public abstract class ReceiverBase
36
{
37
protected string _mailContent;
38
protected string _receiverName;
39
protected ChangeType _changeType;
40
protected IProjectTemplateStoredInTFS _projectForUnsubscribe;
41
public ChangeType ProjectChangeType
42
{
43
get
{ return _changeType; }
44
set
{ _changeType = value; }
45
}
46
47
public string ReceiverName
48
{
49
get
{ return _receiverName; }
50
set
{ _receiverName = value; }
51
}
52
public abstract void Update(string content);
53
//Give user a chance to unsubscribe to a project alert.
54
public void Unsubscribe(IProjectTemplateStoredInTFS currentProject)
55
{
56
_projectForUnsubscribe.UnsubscribeProjectAlerts(this);
57
}
58
}
59
60
/**//// <summary>
61
/// Different user may have different way to display data.
62
/// Every user has to implement this interface.
63
/// </summary>
64
public interface IDisplay
65
{
66
void Display();
67
}
68
69
/**//// <summary>
70
/// Assume that users in Corpnet are always using OutLook to send and receive emails.
71
/// </summary>
72
public sealed class CorpnetUser : ReceiverBase, IDisplay
73
{
74
public override void Update(string content)
75
{
76
_mailContent = content;
77
Display();
78
}
79
80
public void Display()
81
{
82
Console.WriteLine(Environment.NewLine);
83
Console.WriteLine("Welcome to use MS OutLook");
84
Console.WriteLine(_mailContent);
85
}
86
public CorpnetUser(IProjectTemplateStoredInTFS project, string subscriberName, ChangeType changType)
87
{
88
_projectForUnsubscribe = project;
89
_receiverName = subscriberName;
90
_changeType = changType;
91
_projectForUnsubscribe.SubscribeProjectAlerts(this);
92
}
93
}
94
95
/**//// <summary>
96
/// Assume that userd off Coprnet can use free email service and may
97
/// receive annoying advertisement.
98
/// </summary>
99
public sealed class OffCorpnetUser : ReceiverBase, IDisplay
100
{
101
private string _advertisement;
102
public override void Update(string content)
103
{
104
_mailContent = content;
105
Display();
106
}
107
public OffCorpnetUser(IProjectTemplateStoredInTFS project, ChangeType changType, string subscriberName)
108
{
109
_projectForUnsubscribe = project;
110
_receiverName = subscriberName;
111
_advertisement = "There is no advertisement!";
112
_changeType = changType;
113
_projectForUnsubscribe.SubscribeProjectAlerts(this);
114
}
115
public OffCorpnetUser(IProjectTemplateStoredInTFS project, ChangeType changType, string advertisement, string subscriberName)
116
{
117
_projectForUnsubscribe = project;
118
_receiverName = subscriberName;
119
_advertisement = advertisement;
120
_changeType = changType;
121
_projectForUnsubscribe.SubscribeProjectAlerts(this);
122
}
123
public void Display()
124
{
125
Console.WriteLine(Environment.NewLine);
126
Console.WriteLine("AD: "+_advertisement);
127
Console.WriteLine(_mailContent);
128
}
129
}
130
131
/**//// <summary>
132
/// Concrete project class.
133
/// </summary>
134
public sealed class MyCurrentProject : IProjectTemplateStoredInTFS
135
{
136
private ArrayList _receivers;
137
private string _author;
138
private string _projectName;
139
ChangeType _changeType;
140
private string _changeHistory;
141
142
143
public MyCurrentProject(string projectName)
144
{
145
_projectName = projectName;
146
_receivers = new ArrayList();
147
}
148
public void CheckInSourceCode(string author, string fileList)
149
{
150
_author = author;
151
_changeHistory ="The following files :"+ fileList+" were checked in.";
152
_changeType = ChangeType.checkedIn;
153
SendProjectAlert();
154
}
155
public void ChangeWorkItem(string author, string workItem)
156
{
157
_author = author;
158
_changeHistory = "The following workitems are changed: " + workItem;
159
_changeType = ChangeType.workItemChanged;
160
SendProjectAlert();
161
}
162
public void BuildProject(string author)
163
{
164
_author = author;
165
_changeHistory = "Build " + _projectName + " successfully completed!";
166
_changeType = ChangeType.buildCompleted;
167
SendProjectAlert();
168
}
169
170
public void MakeQualityChanged()
171
{
172
throw new System.NotImplementedException("It is not implemented for now!");
173
}
174
175
public void SubscribeProjectAlerts(ReceiverBase receiver)
176
{
177
if (_receivers.Contains(receiver))
178
Console.WriteLine(receiver.ReceiverName+" has already subscribed to this project alert before!");
179
else
180
{
181
_receivers.Add(receiver);
182
Console.WriteLine("Welcome " + receiver.ReceiverName + "! Thank you for subscribing to project alert!");
183
}
184
}
185
186
public void UnsubscribeProjectAlerts(ReceiverBase receiver)
187
{
188
if (_receivers.Contains(receiver))
189
{
190
_receivers.Remove(receiver);
191
Console.WriteLine(receiver.ReceiverName+" has unsubscribed to this project alert!");
192
}
193
}
194
195
public void SendProjectAlert()
196
{
197
foreach (ReceiverBase receiver in _receivers)
198
{
199
200
StringBuilder sb = new StringBuilder("************************************************");
201
sb.Append(Environment.NewLine);
202
sb.Append("Hi " + receiver.ReceiverName + ",");
203
sb.Append(Environment.NewLine);
204
sb.Append(_changeHistory);
205
sb.Append(Environment.NewLine);
206
sb.Append("Summary:");
207
sb.Append(Environment.NewLine);
208
sb.Append("Team Project: " + _projectName);
209
sb.Append(Environment.NewLine);
210
sb.Append("Author: " + _author);
211
sb.Append(Environment.NewLine);
212
sb.Append("Time: " + DateTime.Now.ToString());
213
sb.Append(Environment.NewLine);
214
sb.Append("
..");
215
sb.Append(Environment.NewLine);
216
sb.Append("Provided by Microsoft Visual Studio® 2005 Team System");
217
sb.Append(Environment.NewLine);
218
sb.Append("************************************************");
219
sb.Append(Environment.NewLine);
220
//Who has subscribed current type of project alert?
221
if (receiver.ProjectChangeType.ToString().IndexOf(_changeType.ToString()) >= 0)
222
receiver.Update(sb.ToString());
223
}
224
225
}
226
227
228
}
229
230
class Program
231
{
232
static void Main(string[] args)
233
{
234
MyCurrentProject myproject = new MyCurrentProject("HugeProject");
235
OffCorpnetUser offCorpnetUser1 = new OffCorpnetUser(myproject, ChangeType.buildCompleted | ChangeType.workItemChanged, "Welcome to use this free email service, click here to win award!", "XXX");
236
CorpnetUser corpnetUser1 = new CorpnetUser(myproject, "MM", ChangeType.checkedIn | ChangeType.buildCompleted);
237
myproject.ChangeWorkItem("zhanqiangz", "WorkItem110 has been changed!");
238
myproject.CheckInSourceCode("zhanqiangz", "global.asmx,web.config");
239
myproject.BuildProject("zhanqiangz");
240
corpnetUser1.Unsubscribe(myproject);
241
myproject.CheckInSourceCode("zhanqiangz", "global.asmx,web.config");
242
Console.ReadKey();
243
244
}
245
}
246
}
247
运行结果图:
本例引出的面向对象原则:
尽量将交互的对象设计为松散耦合(strive for loosely coupled designs between objects that interact)
在本例中TFS完全不需要知道订阅者是谁,不管你是Manager,PM,还是只是普通的SDE,你只要使用project alert订阅了你感兴趣的东西,都会把相关的信息发送给你。
代码下载:
ObserverDemo.zip
后续:
生活中这样的例子很多,比如手机短信订阅,网上购物,订阅报纸,婚姻介绍所,通过猎头找工作等等。我写这些东西主要是为了巩固自己对模式的理解,例子中错误在所难免,虽然我写的是head first读书笔记,但是我深信自己没有能力把文章写的那么生动有趣,也没有精力去迎合别人的阅读兴趣;写出来共享资源是目的之二,希望能给像我一样对模式理解不深的朋友提供一点信息,仅此而已。