DH
请教下大家一个设计模式的问题
业务:
有多种短信指令,系统会根据接收到的指令进行相应的业务处理,处理完成后,系统会回复一条短信
这种业务处理,可以用哪个设计模式来做?
QX
状态模式
DH
小雪,短信指令可能会增加,也可能会减少
QB
命令模式
DH
用命令模式更合适?
QB
命令模式看起来挺奇怪的。
QB
interface ICommand{
ICommandResult Execute();
}
abstract class CommandResult{
virtual ToString();
}
DH
如何理解
QB
public class Invoker
{
void Invoke(ICommand cmd)
{
CommandResult ret = cmd.Execute();
MessageSender.Send(ret)
}
}
我觉得 如果是同步模式话 用这种方式就可以了。不好说是什么设计模式。
interface ICommand{
CommandResult Execute();
}
敲错了,qq敲代码蛋疼。
DH
我再说下业务, 系统接收到上行短信后,会把短信push入消息队列,
有一个线程在监听消息队列,每pop出一条消息,都会判断这条消息是什么指令,不同指令处理不同业务
QB
可以用个工厂模式来创建消息。HandlerFactory,webserver里面基本上是这样处理的。
.aspx .ashx .asmx 不同的扩展名通过HttpHandlerFactory 创建不同的Handler
DH
消息类型的判断是放在工厂里做还是放在监听消息队列的线程来做?
QB
Factory里面来做
if(aspx)
return aspxHandler;
else if
....
STST
这种方式,如果分支的数量本身就不稳定,那么维护起来不方便
QB
如果性能要求不高的话 可以用反射。
但是如果要求性能的话也只是修改一个类。可以接受。
DH
指令一般都是固定那些
应该不会怎么变
STST
尽量避免修改一个类
QB
这个还是看项目取舍吧。用反射来处理可以做到只修改配置文件。
DH
目前有11个指令类型,在想着怎么优雅的不需要写分支
STST
具体到这个例子,应该是工厂加职责链处理起来比较合适,个人认为
QB
用map
HandlerMap(string cmdString, Type handlerType)
QB
如果对多线程比较擅长,handler 做成单例的也可以。
QB
但是我觉得11个指令 你就if else 简单有效。性能也无可挑剔
DH
STST
为什么会想到职责链的?
STST
18:47:35
这里的每个职责就是每个相应的业务处理
然后就只要把这些职责串起来,最好把业务量最多的职责放前面
QB
如果需要对命令进行一些过滤操作的话 可以考虑责任链。如果只是单纯处理就不需要了。还是看业务需要。
STST
然后用一个工厂,负责创建职责链
这样处理满足了OCP原则
如果以后增加了业务处理,那么我们需要增加一个 职责
然后增加一个新的Factory20150405,负责创建新的职责链
不用去修改现有的类
QB
也是要改代码的
STST
这只是我的看法,不用改代码阿,只增加代码
QB
你新增了一个Factory 应该在程序里面去调用他啊
STST
使用那个Factory可以在配制文件里处理了
IOC的核心就是这个
选择哪个具体的工厂,可以放配置文件里处理的
这里也同时满足了DIP原则,底层依赖了高层
高层不依赖底层
QB
那你相当于要弄一个抽象工厂,我觉得不需要。简单工厂就可以了。抽象工厂的话要多不少类哦
STST
可以这么理解吧
@大模式-广州 ,这图画出来理解了吗?
DH
谢谢各位
我先看下
STST
针对一条短信X
不再关心是什么命令,直接丢给职责链表来处理
职责链表.Do(X)
每个职责里的处理方法象下面这样
Do(X)
{
if(需要自己处理(X)){具体处理;......}
else
Next.Do(X);
}
QB
修改一下
STST
这样处理无法满足OCP原则
QB
如果用反射的话是可以的。
STST
来了新的短信类型必定要修改
QB
不需要,我先写几句伪代码
DH
英界尔,
你的图是用什么工具画的,感觉挺漂亮的
STST
VS
DH
VISIO?
STST
visual studio
DH
哦哦
STST
QB
public interface IHandler
{
void Handle();
}
public class ConcreteHandlerA : IHandler
{
public void Handle()
{
// handle commandA
}
}
public class ConcreteHandlerB : IHandler
{
public void Handle()
{
// handle commandB
}
}
public static class HandlerFactory
{
public static IHandler CreateHandler(string commandType)
{
switch (commandType)
{
case "A":
return new ConcreteHandlerA();
case "B":
return new ConcreteHandlerB();
default:
throw new NotSupportedException();
}
}
}
public class CommandParser
{
public void Receive(string msg)
{
Command cmd = this.ParseCommand(msg);
IHandler handler = HandlerFactory.CreateHandler(cmd.CommandType);
handler.Handle();
}
}
STST
问题就在这里,来了C,D,E,F呢
QB
罩门在HandlerFactory 增加新的command 只需要修改HandlerFactory。
但是如果在HandlerFactory中用反射的话 就可以做到不修改代码。
性能与优雅的取舍。
要优雅就用反射。要性能就用switch case
你的方法也避免不了这个问题。
罩门是一样的
STST
这一过程,有反射的话如何写?
QB
责任链比较适合要对一个命令分别处理
比如HttpModule 或 Filter之类的。
假设配置文件中已经把 commandType与handlerType做好映射。
DH
像STST说的那种方案,可否通过Spring配置来避免后续修改代码问题
QB
string typeName = Configuration.GetTypeName(commandType);
Type handlerType = Type.GetType(typeName);
return (IHandler)Activator.CreateInstance(handlerType);
STST
@大模式-广州 那就是IOC
QB
是的。因为这种问题不用反射无解。必须要修改代码。
STST
看修改的是那里的代码,修改最顶层的代码代价很小,修改下面的代价较大
这里无论是哪种方案,都在努力做一件事情,将对"具体"的了解尽量网高层推
最理想的情况下,只有main函数需要知道所有的"具体"类
QB
哈哈 那就是标准的IOC了。
在Main里面做各种Mapping
DH
直接IOC是不是可以解决修改代码的问题,后续只要增加短信类型即可
QB
也是需要做映射的,但是一般IOC容器都支持配置文件,可以使用配置文件进行映射。
STST
@大钱币-北京 没错,肯定不止一种方式处理
DH
把所有短信类型IOC到一个list里,逐个循环,循环到匹配的类型跳出循环,否则next
STST
@大模式-广州 你这叫迭代,当然可以
DH
这样后面有新增的短信类型,只管新增类,往spring里配置
QB
IOC叫容器,他内部实现了这个循环匹配的过程。你要做的是把映射关系注册进去。要用的时候 直接resolve就可以了。
下班撤了。再见。
STST
如果你能自己控制循环过程的话,迭代当然可以
迭代和职责链的区别就是,谁来控制循环过程
X
没搞懂要迭代和职责链的目的是?
把获取具体的处理类抽象出来不行。?
STST
都是用来处理"循环"的
X
要加只要新加获取的处理类的抽象类的实现类。。
为什么会有循环?
一条短信会对应多条处理么?
STST
那么我问你,短信X对应哪个Handler的逻辑在那里?
DH
不是一条短信多个处理
而是有多种类型的短信
X
统一一个接收器。。根据接收到的短信类型,实例化具体的handler.
就这个场景的话没必要用链啊。。
STST
具体一个短信X,选择那个Handler Y,这个处理有两个地方可以处理
X
要是有一些别的场景,校验XXX之类 的到是有可能
STST
1,分支,if else
2,由每个业务处理类自身判断
X
如果只是处理短信这一个handler完全没必要用责任链了。
if else是用来干啥 的呢?
STST
干这个阿
X
这个方法可以抽象出来。。由子类去实现得了。
STST
那么我想了解的是,是否有一个职责链,处理一个请求的时候,会有多个职责去处理的情况吗?
X
就他这个场景。。我觉得没有。
STST
哦,那就是没有,采用什么模式,提前设计实际上并不可靠,要在实际中重构到模式才是可靠的
只要记住一点,要在实际演变中注意观察变化点,把变化点封装起来就可以了
面向对象:封装变化,抽象共同点