Head First Design patterns笔记-Observer Patterns (从TFS的Project alerts功能看观察者模式)
Head First Design patterns笔记-Observer Patterns (从TFS的Project alerts功能看观察者模式) . Team foundation server中提供了一个非常适用的功能就是project alerts.在team explorer中打开一个项目,右键菜单中有一个project alerts菜单项,点击会弹出一个对话框,你可以选择自己要接受的project alert的类别(发送project alert的一些规则)和自己的email地址,当当前项目发生的变化或者发生的事件满足上述你订阅的规则时,系统就会给你发送邮件通知你TFS中你所关心的项目发生了怎样的变化。想着跟踪项目中的变化,这应该是最方便的途径了,订阅了以后TFS会自动通知你相关的信息,你在也不会自己逐个文件查看是否发生了改变了。如果不在需要跟踪项目的信息,只要推订project alerts就可以了。
定义: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自动生成的类图:
实例代码:
查看代码
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.Collections;
5
6namespace 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
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.Collections;
5
6namespace 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读书笔记,但是我深信自己没有能力把文章写的那么生动有趣,也没有精力去迎合别人的阅读兴趣;写出来共享资源是目的之二,希望能给像我一样对模式理解不深的朋友提供一点信息,仅此而已。