昨天在答一個問題時,看題不清答錯了,不過卻讓我花了點時間想如何實現簡單的AOP。
在其它語言里實現AOP的確比較麻煩,java要用到動態proxy,如果是C++,除了從源碼上修改還真沒好辦法,aspectc就是這么做的。那么ruby里面如何實現呢?
由于ruby是動態腳本語言,運行期可以把一個方法改名,也可以構造一個字符串動態生成方法,那么實現這個就不是難事了。
使用時只需要在類里面include這個模塊就行了,相當于mixin的功能。
注意,由于使用execute_before時后面幾個方法必須要有定義,所以必須放在后面,否則就會出錯。
在其它語言里實現AOP的確比較麻煩,java要用到動態proxy,如果是C++,除了從源碼上修改還真沒好辦法,aspectc就是這么做的。那么ruby里面如何實現呢?
由于ruby是動態腳本語言,運行期可以把一個方法改名,也可以構造一個字符串動態生成方法,那么實現這個就不是難事了。
module?ExecuteBefore
??def?self.included(base)
????base.extend(ClassMethods)
??end
??module?ClassMethods
????def?execute_before(before_method,?*methods)
??????methods.flatten.map(&:to_sym).each?do?|method|
????????alias_method?"#{method}_old".to_sym,?method
????????class_eval?<<-eval_end
??????????def?#{method}(*args)
????????????#{before_method}(*args)
????????????#{method}_old(*args)
??????????end
????????eval_end
??????end
????end
??end
end
??def?self.included(base)
????base.extend(ClassMethods)
??end
??module?ClassMethods
????def?execute_before(before_method,?*methods)
??????methods.flatten.map(&:to_sym).each?do?|method|
????????alias_method?"#{method}_old".to_sym,?method
????????class_eval?<<-eval_end
??????????def?#{method}(*args)
????????????#{before_method}(*args)
????????????#{method}_old(*args)
??????????end
????????eval_end
??????end
????end
??end
end
使用時只需要在類里面include這個模塊就行了,相當于mixin的功能。
class?TestController?<?ApplicationController
??def?index
????a(1)
????b(1,2)
????c(1,2,3)
????render?:text?=>?'hello'
??end
??protected
??def?log(*args)
????puts?"log:?#{args.map(&:to_s).join(',?')}"
??end
??def?a(a)
??end
??def?b(a,b)
??end
??def?c(a,b,c)
??end
??include?ExecuteBefore
??execute_before?:log,?:a,?:b,?:c
end
??def?index
????a(1)
????b(1,2)
????c(1,2,3)
????render?:text?=>?'hello'
??end
??protected
??def?log(*args)
????puts?"log:?#{args.map(&:to_s).join(',?')}"
??end
??def?a(a)
??end
??def?b(a,b)
??end
??def?c(a,b,c)
??end
??include?ExecuteBefore
??execute_before?:log,?:a,?:b,?:c
end
注意,由于使用execute_before時后面幾個方法必須要有定義,所以必須放在后面,否則就會出錯。