您的位置:首页 > 编程语言 > Ruby

译:Ruby动态方法

2017-01-14 21:29 323 查看
通常情况下,我们使用关键字def来定义方法。

但是当有一系列方法具有相同的结构和逻辑时,继续这样做未免显得有些重复并且违反了DRY原则。

Ruby,作为一个动态语言,允许你在运行时定义方法。

那么,这是什么意思呢?

来看一个最简单的例子:

class A
define_method :a do
puts "hello"
end
define_method :greeting do |message|
puts message
end
end

A.new.a #=> hello
A.new.greeting 'Ram ram' #=> Ram ram


define_method 用来定义实例方法,下面的例子展示更加贴合实际的使用场景。

class User
ACTIVE = 0
INACTIVE = 1
PENDING = 2
attr_accessor :status

def active?
status == ACTIVE
end

def inactive?
status == User::INACTIVE
end

def pending?
status == User::PENDING
end
end

user = User.new
user.status = 1

user.inactive?
#=> true
user.active?
#=> false


使用动态方法定义重构:

class User
ACTIVE = 0
INACTIVE = 1
PENDING = 2
attr_accessor :status

[:active, :inactive, :pending].each do |method|
define_method “#{method}?” do
status == User.const_get(method.upcase)
end
end
end

user = User.new
user.status = 1

user.inactive?
#=> true
user.active?
#=> false


我们使用了define_method来动态的定义方法。

我们也可以使用类方法来定义实例方法,利用这一点我们可以暴露一个用来生成实例方法的类方法。COOL!

例如:

class User
ACTIVE = 0
INACTIVE = 1
PENDING = 2
attr_accessor :status

def self.states(*args)
args.each do |arg|
define_method “#{arg}?” do
self.status == User.const_get(arg.upcase)
end
end
end

states :active, :inactive, :pending
end


定义类方法最简单的方式则为:

class A
class << self
define_method method_name do
#...
end
end
end


也可以使用 instance_evalclass_eval来动态定义方法。这些方法允许运行特定类或方法中的任意代码。这些方法有时候很让人迷惑,你可以看看 stackoverflow上的讨论或者读读博客来理解它们的用法。

stackoverflow上的讨论我们可以总结如下:

Foo = Class.new
Foo.class_eval do
def class_bar
“class_bar”
end
end
Foo.instance_eval do
def instance_bar
“instance_bar”
end
end
Foo.class_bar       #=> undefined method ‘class_bar’ for Foo:Class
Foo.new.class_bar   #=> “class_bar”
Foo.instance_bar       #=> “instance_bar”
Foo.new.instance_bar   #=> undefined method ‘instance_bar’ for #<Foo:0x7dce8>


注意,我们没有在_eval中使用*define_method,因为如下例所示,不管在class_eval还是instance_eval内使用define_method方法,它最终定义的都是实例方法。

Foo = Class.new
Foo.class_eval do
define_method “class_bar” do
“class_bar”
end
end
Foo.instance_eval do
define_method “instance_bar” do
“instance_bar”
end
end

Foo.class_bar #=> undefined
Foo.new.class_bar #=> “class_bar”
Foo.instance_bar #=> undefined
Foo.new.instance_bar #=> “instance_bar”


接下来,我们来看看方法的动态调用。Ruby中一个动态调用方法的方式是向对象发送消息。我们可以向类或类定义本身发送消息发送消息,或者只是发送给类对象。这些都可以通过调用send方法完成。

最简单的例子如下:

s= “hi man”

p s.length #=> 6
p s.include? “hi” #=> true

p s.send(:length) #=> 6
p s.send(:include?,”hi”) #=> true


这个方法在什么时候有用呢?

来看看下面这段代码(取例自:http://www.funonrails.com/2011/12/dynamic-methods-inside-ruby-classes.html)

class ApplicationController < ActionController::Base
protect_from_forgery
helper_method :current_staff, :current_employee, current_admin

def authenticate_staff!(opts={})
current_staff || not_authorized
end

def current_staff
current_user if current_user.is_a? Staff
end

def authenticate_employee!(opts={})
current_employee || not_authorized
end

def current_employee
current_user if current_user.is_a? Employee
end

def authenticate_admin!(opts={})
current_admin || not_authorized
end

def current_admin
current_user if current_user.is_a? Admin
end
end


使用send方法重构:

%w(Staff Employee Admin).each do |k|
define_method “current_#{k.underscore}” do
current_user if current_user.is_a?(k.constantize)
end

define_method “authenticate_#{k.underscore}!” do |opts={}|
send(“current_#{k.underscore}”) || not_authorized
end
end


动态方法定义能够减少方法定义的错误,减少重复并且简化代码

享受元编程吧~

原文:http://rohitrox.github.io/2013/07/02/ruby-dynamic-methods/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ruby 动态语言