关于生活中的设计模式(二)

上篇文章中讨论了以“每个过程”为观察点来处理订单流程。我们定义了一个接口IAction

   1:  public interface IAction
   2:  {
   3:        void DoA();
   4:        void DoB();
   5:        void DoC();
   6:        ....
   7:  }

还定义了每个过程“拍下商品”、“付钱到支付宝”…

   1:  public class 拍下商品:IAction
   2:  {
   3:      void DoA();
   4:      void DoB();
   5:      void DoC();
   6:  }
   7:   
   8:  public class 付款到支付宝:IAction
   9:  {
  10:      void DoA();
  11:      void DoB();
  12:      void DoC();
  13:  }

以及还有一个负责将每个“过程”进行串联的管理类ActionManager

   1:  public class ActionManager
   2:  {
   3:      private IAction _action =null;
   4:      
   5:      public ActionManager(IAction action)
   6:      {
   7:           _action =action;
   8:      }
   9:      
  10:      public void DoSomething()
  11:      {
  12:          _action.DoA();
  13:          _action.DoB();
  14:          _action.DoC();
  15:          ...
  16:      }
  17:  }

最后展示了调用方式:

   1:  var actionMgr = new ActionManager(拍下商品);
   2:  actionMgr.DoSomething();

 

由于实际项目中呢?我们对于IAction每个“动作”(即DoA、DoB、DoC),都会做一些其他的与业务不怎么相关(如日志)或者说在不影响原先的业务逻辑前提下,我要增加一些业务逻辑(比如业务要求在4点之后客户不能确认收货)

 

一开始我们的想法都是比较简单:直接在“确认收货”该过程里面修改逻辑代码!!以及在“每个过程”都添加日志的模块。然后将代码编译成dll文件,做个发布包就OK了。

 

这样的做法有点违反设计模式的“开闭原则”:“说软件实体(类,模块,函数等)应该可以扩展,但是不可以修改”。

 

那么有没有比较优美的做法来改变“直接修改类”的问题呢?

 

优化版做法

在23种设计模式中就有一个符合解决上述问题的解决方案——“装饰者模式”。我的理解就是一个“包装类”,生活中有很多这种例子比如中秋节买的月饼,里面的东西都是差不多的可是在经过盒子的“包装”后让人顿时感觉就“高端 大气 上档次”了。

 

装饰者模式:自己内部拥有一个业务接口(即IAction),而且自己也实现这个业务接口(IAction)。

 

代码如下:

   1:  public void ProcedureWrapper:IAction
   2:  {
   3:      private IAction _innerAction=null;
   4:      
   5:      public ProcedureWrapper(IAction action)
   6:      {
   7:          _innerAction=action;
   8:      }
   9:      
  10:      public void DoA()
  11:      {
  12:        //做一些其他的事情;比如4点之后不执行改动作,直接return;
  13:          
  14:        try
  15:        {
  16:          _innerAction.DoA();
  17:        }catch
  18:        {
  19:          //记录些日志
  20:        }
  21:        
  22:        //改过程完成之后;做一些事情。
  23:      }
  24:      
  25:      public void DoB()
  26:      {
  27:        //做一些其他的事情;比如4点之后不执行改动作,直接return;
  28:          
  29:        try
  30:        {
  31:          _innerAction.DoB();
  32:        }catch
  33:        {
  34:          //记录些日志
  35:        }
  36:        
  37:        //改过程完成之后;做一些事情。
  38:      }
  39:      
  40:      public void DoC()
  41:      {
  42:        //做一些其他的事情;比如4点之后不执行改动作,直接return;
  43:          
  44:        try
  45:        {
  46:          _innerAction.DoC();
  47:        }catch
  48:        {
  49:          //记录些日志
  50:        }
  51:        
  52:        //改过程完成之后;做一些事情。
  53:      }
  54:  }

然后我们修改下ActionManager

   1:  public class ActionManager
   2:  {
   3:      private IAction _action = null;
   4:      
   5:      public ActionManager(IAction action)
   6:      {
   7:          if(action!=null)
   8:          {
   9:              _action=ProcedureWrapper(action);
  10:          }
  11:      }
  12:      
  13:      public void DoSomething()
  14:      {
  15:          if(action!=null)
  16:          {
  17:              _action.DoA();
  18:              _action.DoB();
  19:              _action.DoC();
  20:          }
  21:      }
  22:  }

 

这样的做法虽然满足了上述的要求,但是有个弊端就是每个方法几乎都被定死了即“DoC前”—>“DoC中”—>“DoC后”。在“DoC前和后”都已经规定死了要做什么,要是有些“过程”我们比较特殊(即我不要记日志,或者记日志的方式不一样了)。

 

面对这个问题我能想到的有两种做法:一个用接口IMethodBefore,IMethodAfter;另一种做法就是定义两个事件:OnActionBefore、OnActionAfter

事件的做法大家应该都会做的,也是比较用到的毕竟.Net就是以事件为驱动的。这里我就讲下接口的做法

 

接口定义如下:

   1:  public interface IMethodBefore
   2:  {
   3:      void DoBefore();
   4:  }
   5:   
   6:  public interface IMethodAfter
   7:  {
   8:      void DoAfter();
   9:  }

 

然后呢我定义一个默认的做法类DefaultMethodAction

   1:  public class DefaultMethodAction:IMethodBefore,IMethodAfter
   2:  {
   3:      public DoBefore()
   4:      {
   5:          //做一些通用的事情;如上述的“4点之后不执行该动作”
   6:      }
   7:      
   8:      public DoAfter()
   9:      {
  10:          //做一些通用的事情;如上述的“4点之后不执行该动作”
  11:      }
  12:  }

DefaultMethodAction用于如果没有其他的业务要求我们就做默认的的动作。

 

自定义的IMethodBefore,IMethodAfter该如何实现呢?这种实现有很多种方式,这里提供一种我的方式—反射、特性。

 

特性定义如下:

   1:  public class BeforeAttribute : Attribute
   2:  {
   3:      public IMethodBefore MethodBefore { get; private set; }
   4:      public BeforeAttribute(IMethodBefore before)
   5:      {
   6:          MethodBefore = before;
   7:      }
   8:  }
   9:   
  10:  public class AfterAttribute : Attribute
  11:  {
  12:      public IMethodAfter MethodAfter { get; private set; }
  13:      public BeforeAttribute(IMethodAfter before)
  14:      {
  15:          MethodAfter = before;
  16:      }
  17:  }

 

 

修改下ProcedureWrapper类代码如下:

   1:  public class ProcedureWrapper : IAction
   2:  {
   3:      private IMethodBefore _defaultBefore = null;
   4:      private IMethodAfter _defaultAfter = null;
   5:      private IAction _action = null;
   6:      /// <summary>
   7:      /// key:方法名称 
   8:      /// </summary>
   9:      private Dictionary<string, IMethodBefore> methodBefore = new Dictionary<string, IMethodBefore>();
  10:   
  11:      /// <summary>
  12:      ///  key:方法名称 
  13:      /// </summary>
  14:      private Dictionary<string, IMethodAfter> methodAfter = new Dictionary<string, IMethodAfter>();
  15:      public ProcedureWrapper(IAction action)
  16:      {
  17:          _defaultBefore = new DefaultMethodAction();
  18:          _defaultAfter = new DefaultMethodAction();
  19:          _action = action;
  20:   
  21:          var methods = _action.GetType().GetMethods();
  22:          foreach (var methodInfo in methods)
  23:          {
  24:              var methodName = methodInfo.Name.ToLower();
  25:              var attrBefore =methodInfo.GetCustomAttributes(typeof(BeforeAttribute),true);
  26:              if (attrBefore.Any())
  27:              {
  28:                  var before = attrBefore[0] as BeforeAttribute;
  29:                  if (before != null) methodBefore.Add(methodName,before.MethodBefore);
  30:              }
  31:   
  32:              var attrAfter =methodInfo.GetCustomAttributes(typeof(AfterAttribute),true);
  33:              if (attrAfter.Any())
  34:              {
  35:                    var after = attrBefore[0] as AfterAttribute;
  36:                  if (after != null) methodAfter.Add(methodName,after.MethodAfter);
  37:              }
  38:          }
  39:      }
  40:      public void DoA()
  41:      {
  42:          IMethodBefore before = _defaultBefore;
  43:          IMethodAfter after = _defaultAfter;
  44:          const string key = "doa";
  45:          if (methodBefore.ContainsKey(key))
  46:          {
  47:              before = methodBefore[key];
  48:          }
  49:   
  50:          before.DoBefore();
  51:   
  52:          try
  53:          {
  54:              _action.DoA();
  55:          }
  56:          catch (Exception)
  57:          {
  58:              
  59:              throw;
  60:          }
  61:   
  62:           if (methodAfter.ContainsKey(key))
  63:          {
  64:              after = methodAfter[key];
  65:          }
  66:          after.DoAfter();
  67:      }
  68:   
  69:      public void DoB()
  70:      {
  71:           IMethodBefore before = _defaultBefore;
  72:          IMethodAfter after = _defaultAfter;
  73:          const string key = "dob";
  74:          if (methodBefore.ContainsKey(key))
  75:          {
  76:              before = methodBefore[key];
  77:          }
  78:   
  79:          before.DoBefore();
  80:   
  81:           try
  82:          {
  83:              _action.DoB();
  84:          }
  85:          catch (Exception)
  86:          {
  87:              
  88:              throw;
  89:          }
  90:   
  91:           if (methodAfter.ContainsKey(key))
  92:          {
  93:              after = methodAfter[key];
  94:          }
  95:          after.DoAfter();
  96:      }
  97:   
  98:      public void DoC()
  99:      {
 100:           IMethodBefore before = _defaultBefore;
 101:          IMethodAfter after = _defaultAfter;
 102:          const string key = "doc";
 103:          if (methodBefore.ContainsKey(key))
 104:          {
 105:              before = methodBefore[key];
 106:          }
 107:   
 108:          before.DoBefore();
 109:   
 110:           try
 111:          {
 112:              _action.DoC();
 113:          }
 114:          catch (Exception)
 115:          {
 116:              
 117:              throw;
 118:          }
 119:   
 120:           if (methodAfter.ContainsKey(key))
 121:          {
 122:              after = methodAfter[key];
 123:          }
 124:          after.DoAfter();
 125:      }
 126:  }

 

然后就是我们的每个过程“用法”

 

   1:  public class 拍下商品:IAction
   2:  {
   3:      //在这里你可以打上BeforeAttribute或者AfterAttribute,也可以不打上面的两个标签,让其走默认的做法
   4:      public void DoA()
   5:      {
   6:      
   7:      }
   8:      
   9:      public void DoB()
  10:      {
  11:      
  12:      }
  13:      
  14:      public void DoC()
  15:      {
  16:      
  17:      }
  18:  }

 

我们最终的调用方式还是和原来一样,代码如下:

   1:  var actionMgr = new ActionManager(拍下商品);
   2:  actionMgr.DoSomething();

你可能感兴趣的:(设计模式)