3

只是出于美观的原因,我想为我的一些类的对象实例化提供一种语法,而不使用关键字“new”。我从未尝试过,但我知道这是可能的:例如 Matrix gem 提供如下语法:

Matrix [ [1,2], [3,4] ] 

我怎么能这样做?

4

3 回答 3

7

只需定义一个具有相同名称(类的名称)的方法,该方法将调用.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 中的所有大写方法都将值从一种形式转换为另一种形式。除非您正在编写转换方法,否则不要这样做。

于 2013-11-07T08:58:00.290 回答
4

你可以重载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
于 2013-11-07T09:10:19.157 回答
4

只是出于美观的原因,我想为我的一些类的对象实例化提供一种语法,而不使用关键字“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的东西基本上都是一个函数,或者可以被视为一个函数。而且,如果您将Classes 视为对象的工厂函数,那么这样做甚至是有意义的。

如果您将Classes 视为函数,那么您还应该实现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和标准库都有这样的工厂方法。HashSetSortedSetMatrix

另一种可能性是定义一个Kernel与您的类同名的方法:

module Kernel
  private

  def Foo(obj)
    Foo.new(obj)
  end
end

Foo(42)
# Initialized with 42!

但是请注意,此模式通常保留用于将对象转换为不同类的对象的转换方法,除非该对象已经属于该类(有关示例,请参见Kernel#ArrayKernel#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!
于 2013-11-07T11:36:01.823 回答