Как изменить возвращаемое значение конструктора класса в Ruby?

у меня занятия, Фу. Я хочу иметь возможность передать конструктору экземпляр Foo, foo и получить тот же экземпляр обратно.

другими словами, Я хочу, чтобы этот тест прошел:

class Foo; end

foo = Foo.new
bar = Foo.new(foo)

assert_equal foo, bar

кто-нибудь знает как я могу это сделать? Я попробовал:

class Foo
  def initialize(arg = nil)
    return arg if arg
  end
end

foo = Foo.new
bar = Foo.new(foo)

assert_equal foo, bar  # => fails

но это не работает.

помочь?

редактировать

потому что ряд людей попросили мое обоснование:

Я делаю быстрый анализ лотов данных (много ТБ), и у меня будет много экземпляров многих объектов. Для некоторых из этих объектов не имеет смысла иметь два разных экземпляра с одинаковыми данными. Например, одним из таких объектов является объект" окно " (как во временном окне), который имеет два свойства: время начала и время окончания. Я хочу иметь возможность использовать конструктор любым из этих способов и вернуть объект window:

window = Window.new(time_a, time_b)
window = Window.new([time_a, time_b])
window = Window.new(seconds_since_epoch_a, seconds_since_epoch_b)
window = Window.new(window_obj)
window = Window.new(end => time_b, start => time_a)
...

может быть создан экземпляр другого объекта, для которого требуется окно путь:

obj = SomeObj.new(data => my_data, window => window_arg)

Я не обязательно знаю, что находится в window_arg, и мне все равно-он примет любой аргумент, который может быть интерпретирован конструктором окна. В случае уже имеющегося экземпляра окна я бы предпочел просто использовать этот экземпляр. Но работа по интерпретации, которая кажется проблемой конструктора окна. Во всяком случае, как я уже упоминал, я просматриваю много ТБ данных и создаю много экземпляров вещей. Если объект window передается вокруг, я хочу, чтобы он просто был распознан как объект окна и использовался.

5 ответов


def Foo.new(arg=nil)
  arg || super
end

By определение, конструкторы предназначены для возврата вновь созданного объекта класса, членом которого они являются, поэтому вы не должны не переопределить это поведение.

кроме того, в Ruby, new звонки initialize где-то в теле метода, и его возвращаемое значение игнорируется, поэтому в любом случае значение, которое вы возвращаете из initialize не будет возвращен из new.

С учетом сказанного, я думаю, что в вашем случае вы, возможно, захотите чтобы создать фабричный метод, который будет возвращать разные Foo объекты, основанные на аргументах, переданных заводскому методу:

class Foo
  def self.factory(arg = nil)
    return arg if arg.kind_of? Foo
    Foo.new
  end
end
foo = Foo.factory
bar = Foo.factory(foo)

assert_equal foo, bar #passes

initialize называется new который игнорирует его возвращаемое значение. В основном по умолчанию new метод выглядит так (за исключением того, что он реализован в C, а не в ruby):

class Class
  def new(*args, &blk)
    o = allocate
    o.send(:initialize, *args, &blk)
    o
  end
end

таким образом, вновь выделенный объект возвращается в любом случае, независимо от того, что вы делаете в initialize. Единственный способ изменить это-переопределение new метод, например, такой:

class Foo
  def self.new(arg=nil)
    if arg
      return arg
    else
      super
    end
  end
end

однако я настоятельно рекомендую против этого, так как это противоречит многим ожиданиям что люди имеют при вызове new:

  • люди ожидают new для возврата нового объекта. Я имею в виду, что это даже называется new. Если вам нужен метод, который не всегда создает новый объект, вы, вероятно, должны назвать его чем-то другим.
  • по крайней мере люди ожидают Foo.new возвратить

не работает:


class Some
    def self.new( str )
        SomeMore.new( str )
    end
end

# the Some is parent of SomeMore class SomeMore < Some def initialize( str ) @str = str end end


для этого конкретного случая использования, возможно, было бы лучше использовать один из этих подходов.

Class Foo
    def self.new(args=nil)
        @@obj ||= super(args)
    end
end


Class Foo
    def self.new(args)
      @@obj = super(args)
    end

    def self.new
      @@obj
    end
end

Это позволяет иметь только один объект, который создается, который может использоваться повсеместно, но возвращает Foo объект, что делает его более встроенным в стандартные ожидания new метод, как указал Джейкоб.