只是出于美观的原因,我想为我的一些类的对象实例化提供一种语法,而不使用关键字“new”。我从未尝试过,但我知道这是可能的:例如 Matrix gem 提供如下语法:
Matrix [ [1,2], [3,4] ]
我怎么能这样做?
只是出于美观的原因,我想为我的一些类的对象实例化提供一种语法,而不使用关键字“new”。我从未尝试过,但我知道这是可能的:例如 Matrix gem 提供如下语法:
Matrix [ [1,2], [3,4] ]
我怎么能这样做?
只需定义一个具有相同名称(类的名称)的方法,该方法将调用.new
.
class User
def initialize(name)
@name = name
end
attr_reader :name
end
def User(name)
User.new(name)
end
u = User("Sergio")
u.name # => "Sergio"
注意:我个人不喜欢这种技术。导致更难理解代码。“只是看起来更好”的理由并不能成为您使用它的借口。
实际上,它是 ruby 中的一种模式,称为“转换方法”。如果仔细观察,ruby stdlib 中的所有大写方法都将值从一种形式转换为另一种形式。除非您正在编写转换方法,否则不要这样做。
你可以重载self.[]
:
class Foo
attr_accessor :bar
def initialize(bar)
self.bar=bar
end
def self.[](*args)
return Foo.new(*args)
end
end
puts Foo[1].bar
只是出于美观的原因,我想为我的一些类的对象实例化提供一种语法,而不使用关键字“new”。
new
不是关键字。这是一种方法,就像任何其他方法一样。而且你不必叫它new
,你可以叫它任何你喜欢的名字。例如,您可以调用它call
:
class Foo
def initialize(val)
puts "Initialized with #{val}!"
end
class << self; alias_method :call, :new end
end
foo = Foo.(42)
# Initialized with 42!
在 Ruby 中,实现call
相当于说“这是一个函数”。任何实现call
的东西基本上都是一个函数,或者可以被视为一个函数。而且,如果您将Class
es 视为对象的工厂函数,那么这样做甚至是有意义的。
如果您将Class
es 视为函数,那么您还应该实现to_proc
以便可以使用它们来代替块:
class << Foo
def to_proc
-> var { new(var) }
end
end
[23, 42].map(&Foo)
# Initialized with 23!
# Initialized with 42!
# => [#<Foo:0xdeadbeef081523>, #<Foo:0xdeadbeef081542>]
事实上,你可以在全球范围内这样做:
class Class
alias_method :call, :new
def to_proc
-> (*args, &block) { new(*args, &block) }
end
end
或者你可以调用你的工厂方法[]
:
class << Foo; alias_method :[], :new end
foo = Foo[42]
# Initialized with 42!
事实上,后一种是任何集合类的常见做法:核心库、标准库Array
和标准库都有这样的工厂方法。Hash
Set
SortedSet
Matrix
另一种可能性是定义一个Kernel
与您的类同名的方法:
module Kernel
private
def Foo(obj)
Foo.new(obj)
end
end
Foo(42)
# Initialized with 42!
但是请注意,此模式通常保留用于将对象转换为不同类的对象的转换方法,除非该对象已经属于该类(有关示例,请参见Kernel#Array
等Kernel#Integer
)。
所以,你宁愿这样实现它(如果有的话):
module Kernel
private
def Foo(obj)
return obj if obj.is_a?(Foo)
begin
return obj.to_foo
rescue NoMethodError => e
return Foo.new(obj) if e.name == 'to_foo'
raise
end
end
end
Foo(42)
# Initialized with 42!