Как я могу изменить возвращаемое значение конструктора класса в Ruby? - PullRequest
12 голосов
/ 03 февраля 2011

У меня есть класс, Фу.Я хочу иметь возможность передать конструктору экземпляр 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.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.Если у меня уже есть экземпляр Window, я бы предпочел просто использовать этот экземпляр.Но работа по интерпретации, которая кажется заботой конструктора Window.Во всяком случае, как я уже говорил, я перетаскиваю много ТБ данных и создаю множество экземпляров вещей.Если объект окна передается вокруг, я хочу, чтобы он просто распознавался как объект окна и использовался.

Ответы [ 5 ]

29 голосов
/ 03 февраля 2011

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

Кроме того, в 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
17 голосов
/ 03 февраля 2011
def Foo.new(arg=nil)
  arg || super
end
15 голосов
/ 03 февраля 2011

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 вернет объект Foo. Ваш код вернет любой аргумент. То есть Foo.new(42) вернет 42, целое число, а не Foo объект. Поэтому, если вы собираетесь это сделать, вы должны как минимум только вернуть данный объект, если это Foo объект.
2 голосов
/ 12 ноября 2012

Не работает для:


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
0 голосов
/ 16 декабря 2015

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

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, как указал Джейкоб.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...