*** UPDATE ***
抱歉之前精简了部分无关的业务代码,可能导致了一定的误导性。现在补上完整的应用场景
比如同一功能的下述两种实现方式,大家觉得使用那种会更恰当?
# 方案1
module ApiHelper
%w(get post put patch delete).each do |method|
module_eval(<<-EOF, __FILE__, __LINE__)
def api_#{method}(params={},headers={}}
headers.merge!('X-Token' => CFG['api_token']['apollo'])
#{method}(path,params,headers)
end
EOF
end
end
# 方案 2
module ApiHelper
def api_get(params={},headers={})
request_send :get,params,headers
end
def api_post(params={},headers={})
request_send :post,params,headers
end
def api_put(params={},headers={})
request_send :put,params,headers
end
def api_patch(params={},headers={})
request_send :patch,params,headers
end
def api_delete(params={},headers={})
request_send :delete,params,headers
end
private
def request_send(method,params={},headers={})
headers.merge!('X-Token' => CFG['api_token']['apollo'])
send method,params,headers
end
end
可以结合一下
module ApiHelper
%w(get post put patch delete).each do |method|
define_method "api_#{method}" do
request_send method
end
end
def request_send(method)
# dosometing
end
end
2 如果,DRY、可读性的目的是为了可维护。第二种更直接,应该更好维护。而且,方法都是固定的,没动态的东西,没觉得没有用元编程的必要。
而且,第二种,方法和第一种方法没什么区别啊。。。只是换一种方法实现罢了。。。
@cn_boris 这就是我最疑惑的地方了,这段代码明明就是在实现一些 methods,但却被当做 string,这些 methods 岂不是没办法被其它的对象调用了
两种方案我都尝试过!
只有一个优点,就是是很装逼! 缺点是,维护成本高和可读性低, 尤其是在后期调试的时候(打 log 和设置断点,都是问题)。
用软件工程的语言来说, 该方案增加了接口的耦合性, 失去了独立性, 不利于扩展。
这是软件开发这么多年来的常规做法。 历史经验告诉我们,这么做除了多些几行代码,没有其他坏处! 接口独立,该复用的复用,不该复用的木有复用, 还可以轻松的实现接口内部的差异化! 即使某个程序员只学 10 分种的 ruby, 他也能读懂和维护这段代码! 估计奥巴马都能看懂,这不就是“可读性”三个字的最高追求吗!
很多年前的我,会选择方案 1 但此时此刻的我,会选择方案 2 也许 n 年以后,我会再选择方案 1 也许随着时间的增长,我的选择也许会不断的改变。 折腾永无止境!
该方案的优劣,因人而异! 如果团队,里面只有你一个人,哪随你折腾! 如果团队,里面有多个人,最好少数服从多数! (如果是偶数,就“拿”石头/剪刀/布 干掉一个先) 因为代码是属于团队,也是写给团队看的,所以应该尽量让多数人接受! 不要搞独裁!
#22 楼 @ery 其实两种方式都能用,甚至用在同一个项目中。不过首先要搞清楚主次逻辑。而楼主的代码很明显有一定的误导性,因为 request_send
这个实现逻辑跟 api 放到一起了...
方案一对于 api 和逻辑放到一起的类是有助于理清代码条理的,因为这些 api 方法是无关紧要的。断点也不会打到 api 上,因为这类代码几乎不可能在逻辑上有错。
而方案二则对 api 和实现逻辑分开的情况更有利,比如生成 rdoc。典型案例就是 rest-client
,请求逻辑全部是 Request
的类封装了,而 api 则全部放到 RestClient
模块中,这样查阅代码非常方便,也不会弄混淆。
两个方案的区别不重要,问题是这里为什么会有一个公共的方法体? 如果几个 method 都用一个方法,那这里其实只是一堆方法别名而已。 要真是这样还好,最可怕的是——目前写在一起的代码其实是表面相同,其实这些方法的业务本来是不同的几件事,所以本质上方法体应该是不同的,无论方案 1 还是方案 2,都是在制造耦合......
方案一,看不懂来问就对了,对团队成员有好处
方案二增加了一层嵌套调用,还悄悄污染了方法命名空间,万一 include module 的 class 或者别的 module 也定义了 request_send 你就 debug 去吧