您的位置:首页 > 其它

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

2013-11-05 12:18 393 查看
原文地址:http://www.cnblogs.com/hankskfc/p/3408148.html

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

publicinterfaceIAction

[code]{
voidDoA();

voidDoB();

voidDoC();

....

}

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

publicclass拍下商品:IAction

[code]{
voidDoA();

voidDoB();

voidDoC();

}


publicclass付款到支付宝:IAction

{

voidDoA();

voidDoB();

voidDoC();

}

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


publicclassActionManager

[code]{
privateIAction_action=null;


publicActionManager(IActionaction)

{

_action=action;

}


publicvoidDoSomething()

{

_action.DoA();

_action.DoB();

_action.DoC();

...

}

}

[/code]
最后展示了调用方式:


varactionMgr=newActionManager(拍下商品);

[code]actionMgr.DoSomething();
[/code]

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

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

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

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

优化版做法

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

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

代码如下:


publicvoidProcedureWrapper:IAction

[code]{
privateIAction_innerAction=null;


publicProcedureWrapper(IActionaction)

{

_innerAction=action;

}


publicvoidDoA()

{

//做一些其他的事情;比如4点之后不执行改动作,直接return;


try

{

_innerAction.DoA();

}catch

{

//记录些日志

}


//改过程完成之后;做一些事情。

}


publicvoidDoB()

{

//做一些其他的事情;比如4点之后不执行改动作,直接return;


try

{

_innerAction.DoB();

}catch

{

//记录些日志

}


//改过程完成之后;做一些事情。

}


publicvoidDoC()

{

//做一些其他的事情;比如4点之后不执行改动作,直接return;


try

{

_innerAction.DoC();

}catch

{

//记录些日志

}


//改过程完成之后;做一些事情。

}

}

[/code]
然后我们修改下ActionManager


publicclassActionManager

[code]{
privateIAction_action=null;


publicActionManager(IActionaction)

{

if(action!=null)

{

_action=ProcedureWrapper(action);

}

}


publicvoidDoSomething()

{

if(action!=null)

{

_action.DoA();

_action.DoB();

_action.DoC();

}

}

}

[/code]

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

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

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

接口定义如下:


publicinterfaceIMethodBefore

[code]{
voidDoBefore();

}


publicinterfaceIMethodAfter

{

voidDoAfter();

}

[/code]

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


publicclassDefaultMethodAction:IMethodBefore,IMethodAfter

[code]{
publicDoBefore()

{

//做一些通用的事情;如上述的“4点之后不执行该动作”

}


publicDoAfter()

{

//做一些通用的事情;如上述的“4点之后不执行该动作”

}

}

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

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

特性定义如下:


publicclassBeforeAttribute:Attribute

[code]{
publicIMethodBeforeMethodBefore{get;privateset;}

publicBeforeAttribute(IMethodBeforebefore)

{

MethodBefore=before;

}

}


publicclassAfterAttribute:Attribute

{

publicIMethodAfterMethodAfter{get;privateset;}

publicBeforeAttribute(IMethodAfterbefore)

{

MethodAfter=before;

}

}

[/code]

修改下ProcedureWrapper类代码如下:


publicclassProcedureWrapper:IAction

[code]{
privateIMethodBefore_defaultBefore=null;

privateIMethodAfter_defaultAfter=null;

privateIAction_action=null;

///<summary>

///key:方法名称

///</summary>

privateDictionary<string,IMethodBefore>methodBefore=newDictionary<string,IMethodBefore>();


///<summary>

///key:方法名称

///</summary>

privateDictionary<string,IMethodAfter>methodAfter=newDictionary<string,IMethodAfter>();

publicProcedureWrapper(IActionaction)

{

_defaultBefore=newDefaultMethodAction();

_defaultAfter=newDefaultMethodAction();

_action=action;


varmethods=_action.GetType().GetMethods();

foreach(varmethodInfoinmethods)

{

varmethodName=methodInfo.Name.ToLower();

varattrBefore=methodInfo.GetCustomAttributes(typeof(BeforeAttribute),true);

if(attrBefore.Any())

{

varbefore=attrBefore[0]asBeforeAttribute;

if(before!=null)methodBefore.Add(methodName,before.MethodBefore);

}


varattrAfter=methodInfo.GetCustomAttributes(typeof(AfterAttribute),true);

if(attrAfter.Any())

{

varafter=attrBefore[0]asAfterAttribute;

if(after!=null)methodAfter.Add(methodName,after.MethodAfter);

}

}

}

publicvoidDoA()

{

IMethodBeforebefore=_defaultBefore;

IMethodAfterafter=_defaultAfter;

conststringkey="doa";

if(methodBefore.ContainsKey(key))

{

before=methodBefore[key];

}


before.DoBefore();


try

{

_action.DoA();

}

catch(Exception)

{


throw;

}


if(methodAfter.ContainsKey(key))

{

after=methodAfter[key];

}

after.DoAfter();

}


publicvoidDoB()

{

IMethodBeforebefore=_defaultBefore;

IMethodAfterafter=_defaultAfter;

conststringkey="dob";

if(methodBefore.ContainsKey(key))

{

before=methodBefore[key];

}


before.DoBefore();


try

{

_action.DoB();

}

catch(Exception)

{


throw;

}


if(methodAfter.ContainsKey(key))

{

after=methodAfter[key];

}

after.DoAfter();

}


publicvoidDoC()

{

IMethodBeforebefore=_defaultBefore;

IMethodAfterafter=_defaultAfter;

conststringkey="doc";

if(methodBefore.ContainsKey(key))

{

before=methodBefore[key];

}


before.DoBefore();


try

{

_action.DoC();

}

catch(Exception)

{


throw;

}


if(methodAfter.ContainsKey(key))

{

after=methodAfter[key];

}

after.DoAfter();

}

}

[/code]

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

publicclass拍下商品:IAction

[code]{
//在这里你可以打上BeforeAttribute或者AfterAttribute,也可以不打上面的两个标签,让其走默认的做法

publicvoidDoA()

{


}


publicvoidDoB()

{


}


publicvoidDoC()

{


}

}

[/code]

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


varactionMgr=newActionManager(拍下商品);

[code]actionMgr.DoSomething();
[/code]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: