C# 设计模式
我居然连一月一随笔都没有,啊啊啊,忙死个人
这个随笔主要是记录基于自己学习[美]James W.Cooper著的《C# Design Patterns : A Tutorial》一书中常用设计模式的整理,既是自己整理的,便避免不了理解偏差,欢迎分享宝贵见解。
Behavioral Pattern 行为型模式
行为型模式模式主要与对象间的通信有关。
Chain of Responsibility(职责链模式),职责链模式减少对象间的耦合。整理了一个接口,如下:
/// <summary> /// 职责链模式 /// 职责链减少了类之间的耦合 /// </summary> /// <typeparam name="T"></typeparam> public interface IChain<T> { /// <summary> /// 添加一条链至本链后 /// </summary> /// <param name="nextChain"></param> void AddNext(IChain<T> nextChain); /// <summary> /// 消息处理 /// </summary> /// <param name="msg"></param> void OnChain(T msg); }
适用:
p) 职责链模式允许多个类处理同一个请求,请求在类之间传递,直到其中一个类处理它为止。
p) 不想把相互作用的内容放在调用程序里。
举例:
例2:
最近想关于硬件命令和数据收发时想到了用职责链解决,结果好像出现了偏差,便有了以下代码。以下代码用于统计字符串中JMB三个字母出现的次数以及J出现的次数以及两个B字母间用时(实属瞎JB写)。
以下是相关类定义:
1 using System; 2 using System.Collections.Generic; 3 4 namespace ConcurrencySolution 5 { 6 #region base classes 7 public class Msg 8 { 9 public string ID 10 { 11 get { return _id; } 12 } 13 private string _id; 14 15 public Msg() 16 { 17 _id = Guid.NewGuid().ToString("N"); 18 } 19 } 20 21 public class MsgRep 22 { 23 public string MsgID; 24 25 public bool ExecResult; 26 27 public MsgRep() 28 : this(string.Empty, false) 29 { } 30 31 public MsgRep(string vMsgID, bool vExecResult) 32 { 33 MsgID = vMsgID; 34 ExecResult = vExecResult; 35 } 36 } 37 38 public abstract class Chain 39 { 40 public Chain Next 41 { 42 get { return _next; } 43 } 44 private Chain _next; 45 46 public void SetNext(Chain next) 47 { 48 _next = next; 49 } 50 51 public MsgRep HandleMsg(Msg msg) 52 { 53 MsgRep rep = HandleMessage(msg); 54 if (true == rep.ExecResult) 55 return rep; 56 57 if (Next != null) 58 return Next.HandleMsg(msg); 59 60 return null; 61 } 62 63 protected abstract MsgRep HandleMessage(Msg msg); 64 } 65 66 public abstract class Worker : Chain 67 { 68 public string WorkerID 69 { 70 get { return _workerID; } 71 } 72 protected string _workerID; 73 74 protected Worker() 75 : this(string.Empty) 76 { } 77 78 protected Worker(string vWorkerID) 79 { 80 _workerID = vWorkerID; 81 } 82 83 protected override MsgRep HandleMessage(Msg msg) 84 { 85 WorkerMsg wmsg = msg as WorkerMsg; 86 if (null == wmsg) 87 throw new ArgumentException("isn't a valid WorkerMsg", "msg"); 88 89 var rep = DoWork(wmsg); 90 return rep; 91 } 92 93 protected abstract WorkerMsgRep DoWork(WorkerMsg msg); 94 } 95 #endregion 96 97 public class WorkerMsg : Msg 98 { 99 public string Action 100 { 101 get { return _action; } 102 } 103 private string _action; 104 105 public WorkerMsg(string vAction) 106 : base() 107 { 108 _action = vAction; 109 } 110 } 111 112 public class WorkerMsgRep : MsgRep 113 { 114 public string WorkerID; 115 116 public WorkerMsgRep() 117 : this(string.Empty, false, string.Empty) 118 { } 119 120 public WorkerMsgRep(string vMsgID, bool vExecResult, string vWorkerID) 121 : base(vMsgID, vExecResult) 122 { 123 WorkerID = vWorkerID; 124 } 125 } 126 127 #region Workers 128 public class WorkerJ : Worker 129 { 130 public int MentionTimes 131 { 132 get { return _mentionTimes; } 133 } 134 protected int _mentionTimes; 135 136 protected WorkerJ() 137 : base() 138 { } 139 140 public WorkerJ(string vWorkerID) 141 : base(vWorkerID) 142 { } 143 144 protected override WorkerMsgRep DoWork(WorkerMsg msg) 145 { 146 WorkerMsgRep rep = new WorkerMsgRep(msg.ID, false, WorkerID); 147 rep.ExecResult = msg.Action == "J"; 148 if (true == rep.ExecResult) 149 _mentionTimes++; 150 151 return rep; 152 } 153 154 public virtual void ChangeWorkerID(string vWorkerID) 155 { 156 throw new NotImplementedException(); 157 } 158 } 159 160 public class WorkerM : Worker 161 { 162 public WorkerM(string vWorkerID) 163 : base(vWorkerID) 164 { } 165 166 protected override WorkerMsgRep DoWork(WorkerMsg msg) 167 { 168 var rep = new WorkerMsgRep(msg.ID, false, WorkerID); 169 if (msg.Action == "M") 170 rep.ExecResult = true; 171 172 return rep; 173 } 174 } 175 176 public class WorkerB : Worker 177 { 178 public List<DateTime> MentionTime 179 { 180 get { return _mentionTime; } 181 } 182 List<DateTime> _mentionTime; 183 184 public WorkerB(string vWorkerID) 185 : base(vWorkerID) 186 { 187 _mentionTime = new List<DateTime>(); 188 } 189 190 protected override WorkerMsgRep DoWork(WorkerMsg msg) 191 { 192 WorkerMsgRep rep = new WorkerMsgRep(msg.ID, false, WorkerID); 193 rep.ExecResult = msg.Action == "B"; 194 if (true == rep.ExecResult) 195 _mentionTime.Add(DateTime.Now); 196 197 return rep; 198 } 199 200 201 } 202 #endregion 203 }
以下是使用:
public void TestMethod1() { string testStr = "Bb1123lkiJMoBp"; WorkerJ wj = new WorkerJ("j"); WorkerM wm = new WorkerM("m"); WorkerB wb = new WorkerB("B"); wj.SetNext(wm); wm.SetNext(wb); int countJMB = 0; WorkerMsg ms; WorkerMsgRep mr; for (int i = 0; i < testStr.Length; i++) { ms = new WorkerMsg(testStr[i].ToString()); mr = wj.HandleMsg(ms) as WorkerMsgRep; if (mr != null && true == mr.ExecResult) countJMB++; } string time1 = wb.MentionTime[0].ToString("HH:mm:ss:ms"); string time2 = wb.MentionTime[1].ToString("HH:mm:ss:ms"); ; }
例1:
public class Sunday : IChain<DayOfWeek> // System.DayOfWeek { private IChain<DayOfWeek> _next; private DayOfWeek _token; public DayOfWeek Token { get { return _token; } } public Sunday() { _token = DayOfWeek.Sunday; } #region IChain<DayOfWeek> 成员 void IChain<DayOfWeek>.AddNext(IChain<DayOfWeek> nextChain) { _next = nextChain; } void IChain<DayOfWeek>.OnChain(DayOfWeek msg) { if (DayOfWeek.Sunday == msg) // 此处判断本类是否最适合处理此消息 { Console.WriteLine("Hello! It's {0}, u'd better study!", _token); } else if (_next != null) // 传递消息 { _next.OnChain(msg); } else { throw new Exception(string.Format("{0} can't handle this msg : {1}", _token, msg)); } } #endregion } public class DefaultDay : IChain<DayOfWeek> { private DayOfWeek _token; public DayOfWeek Token { get { return _token; } } public DefaultDay() { //_token = DayOfWeek.None; 好烦啊,想偷个懒的,意思到就好了8 } #region IChain<DayOfWeek> 成员 void IChain<DayOfWeek>.AddNext(IChain<DayOfWeek> nextChain) { throw new NotImplementedException(string.Format("Sorry but i'm at the end of the chain, {0} said.", _token)); } void IChain<DayOfWeek>.OnChain(DayOfWeek msg) { //if (msg != _token) //{ // throw new Exception(string.Format("It's none of my business about : {0}, {1} said", msg, _token)); //} //else //{ Console.WriteLine("Unbelievable! Today is {0}!", _token); //} } #endregion } public class ChainImpl { IChain<DayOfWeek> _prior, _inferior, _default; public ChainImpl() { _prior = new Sunday(); //_inferior = new Monday(); _default = new DefaultDay(); //_prior.AddNext(_inferior); //_inferior.AddNext(_default); _prior.AddNext(_default); } public void Work(DayOfWeek today) { // 不管今天具体是周几,由_prior对象优先去处理 // 如果_prior对象不能处理,自动转交给它的"下一链"处理直到_default _prior.OnChain(today); } }
Strategy Pattern(策略模式)。
适用:
p) 不同算法各自封装,可随意挑选需要的算法。
实现:
方式一,把策略封装在单独的类中。策略决策类和策略类间的耦合较低(当更换策略时,策略决策类与原策略类解耦);可提供异步获取结果的方法;
方式二,把策略作为一个委托(根据自己理解实现的,自荐)。性能(反射)可能较低;策略决策类与策略(委托,当为其他类中的方法时)所属类间的耦合较高;策略相对轻量级、灵活方便;对于同个问题的策略,可封装在单独类中,结构清晰;
举例:
方式二,具体实现。
using System; public class StrategyDecision { protected Delegate _strategy; public StrategyDecision() { } public StrategyDecision(Delegate strategy) { _strategy = strategy; } ~StrategyDecision() { _strategy = null; } public object GetResult(params object[] args) { try { return _strategy.DynamicInvoke(args); } catch (Exception e) { throw e; } } public void ChangeStrategy(Delegate strategy) { _strategy = strategy; } }
方式二,使用举例。
var obj = new StrategyDecision(
new Func<int, int, int>(
(oa, ob) =>
{
return oa + ob;
})).GetResult(7, 7); // Bad example! 图简便 >_<
Creational Pattern 创建型模式
所有创建型模式都涉及到创建对象实例的方式。将创建对象过程抽象成一个专门的“创建器”类,会使程序更灵活、更通用。
Singleton Pattern(单例模式),单例模式限制而不是改进类的创建,单例模式保证一个类有且只有一个实例,并提供一个访问它的全局访问点。如果有需要,也可以在类中保存少数几个实例。我常用单例的全局访问功能;在你只持有一个资源,而需要对外开放多个资源访问服务时,用单例是很好的选择。
举例:
public class Myself { static readonly Myself _instance; public static Myself Instance { get { return _instance; } } // 应用程序域中第一次发生以下事件时将触发静态构造函数的执行: //• 创建类类型的实例。(外部无法创建本类实例) //• 引用类类型的任何静态成员。(当Instance属性被引用时) static Myself() { _instance = new Myself(); } private Myself() // 私有构造限制外部创建实例 { } }
备注:
此单例的实现是我根据静态构造的特性实现的,以下称前者;书中的单例用lock方式实现,以下称后者。我用循环1000次调用某单例的两种实现的方法(简单返回字符串):第一次调用前者用时0毫秒,后者用时2毫秒,也就是说后者用时是在第一次调用(也就是构造函数花费的)时出现的,以后每次调用二者都是0毫秒;值得一提的点,每个1000次调用中,后者总会至少有一次返回空,也就是调用失败或者啥,但前者不会有这种情况,也就是说前者较为安全。有时间我把测试也贴出来。
Linkin Park 的《Halfway Right》好听哭了T_T,一定要听!