一位同事和我正在研究一个RoR项目,该项目接收来自Twilio消息服务的文本消息,然后解析它们以执行命令。为了与Twilio接口,我们有一个控制器动作,Twilio POST到一些参数,如to,from和message body。
问题是我们有很多可能的命令,用户可以通过发短信来执行。最后,我们的代码开始看起来像这样:
# message_controller.rb
def process_message
words = params[:Body].upcase.split
if words[0] == 'FOO'
do_something()
render 'foo'
elsif words[0] == 'BAR' && params[:From] == 'some number'
.
. # 10 to 15 lines
.
else
render 'command_not_found'
end
end
我们决定使用Interpreter模式进行重构,并将消息体解析为类似语言。我们在控制器中得到了类似的东西:
# message_controller.rb
def process_message
interpreter = InitialInterpreter.new(message_params)
while !interpreter.is_terminal? do
interpreter = interpreter.next_interpreter()
end
render interpreter.template
end
def message_params
params.permit(:To, :From, :Body)
end
我们创建了这样的模型:
# initial_interpreter.rb, a non-terminal interpreter
attr_accessible :To, :From, :Body
def next_interpreter
if words[0] == 'FOO'
FooInterpreter.new
elsif
.
. # a couple of non-terminal interpreters
.
else
CommandNotFoundInterpreter.new
end
end
# command_not_found_interpreter.rb, a terminal interpreter
def is_terminal?
true
end
def template
'command_not_found'
end
然后我们开始思考它并意识到几乎所有命令都只有两个表达式,所以我们并不需要while循环并重构如下:
# message_controller.rb
def process_message
interpreter = InitialInterpreter.new(message_params)
render interpreter.template
end
# initial_interpreter.rb
def template
if words[0] == 'FOO'
interpreter = FooInterpreter.new
elsif
.
. # a couple of non-terminal interpreters
.
else
interpreter = CommandNotFoundInterpreter.new
end
interpreter.template
end
# command_not_found_interpreter.rb
def template
'command_not_found'
end
但这似乎有点过于简单了。现在,它只是类包装方法。我们是否过度思考这个?这种模式看起来好还是坏?我觉得我们做错了什么,但我不能把手指放在上面。处理这种情况的最佳方法是什么?
修改
我也应该提到我也在想效率。我们使用的这个解决方案看起来效率不高,当我们上线时,这个动作可能会收到很多流量。我想知道是否有任何特定的rails或ruby可以提供一个很好的解决方案。
答案 0 :(得分:2)
在我看来,你的模式似乎很好。我过去做过类似的事情,并没有遇到任何糟糕的经历。如果你真的有很多案例,那么观察者模式也许没问题。您的InitialInterpreter可以接受该消息并将其发送给所有观察者(这将是您的其他解释器)。每个解释器都可以根据正则表达式或字符串匹配或您选择的任何内容选择对消息执行某些操作。
在这种模式中,解释器包含何时解释自己内部的逻辑。只要订阅者订阅了InitialInterpreter的事件,InitialInterpreter和你的控制器就不会关心解释器接收的消息。
观察者模式的另一个好处是,如果您将来需要这样做,它允许您为单个命令轻松添加多个解释器。 Ruby通过Observable模块内置了对观察者的支持。我相信Rails也有它的东西,但我从来没有亲自使用它。