2012-01-28 3 views
6

Я хочу создать подкласс Date.Почему Date.new не вызывает инициализацию?

Нормальный, здоровый, молодой rubyist, UNSCARRED по идиосинкразии реализации Дейта будет идти об этом следующим образом:

require 'date' 

class MyDate < Date 

    def initialize(year, month, day) 
    @original_month = month 
    @original_day = day 

    # Christmas comes early! 
    super(year, 12, 25) 
    end 

end 

И продолжить использовать его в наиболее ожидаемым образом ...

require 'my_date' 

mdt = MyDate.new(2012, 1, 28) 

puts mdt.to_s 

... только чтобы быть дважды пересек тот факт, что Date :: новый метод фактически является псевдонимом Date :: гражданским, который не когда-либо вызова Initialize. В этом случае последний фрагмент кода печатает «2012-01-28» вместо ожидаемого «2012-12-25».

Дорогой Ruby-сообщество, wtf это?

Есть некоторые очень веские основания для наложения новый, так что он игнорирует инициализации, и, как следствие, любой здравый смысл и уважение к психическому здоровью программиста клиента в?

+1

По определению "хорошего" чей? –

+0

Какая альтернатива? Если вы вызываете civil из инициализации, вы выделили объект, который вам не нужен. – pguardiario

+0

Ну, кажется, семантически неправильно выделять и инициализировать объект в том же методе. Я имею в виду, если 'civil' является де-факто' new', почему бы не заставить его вызвать 'initialize' в конце? Это может быть чистая формальность в «Дата», но это позволит расширить класс без необходимости фактически просканировать детали реализации. Поэтому я задавался вопросом, почему было принято решение об алиасе 'civil' to' new'? – jst

ответ

6

Вы определяете initialize, но вы создаете новый экземпляр с new. new возвращает новый экземпляр класса, а не результат initialize.

Вы можете сделать:

require 'date' 

class MyDate < Date 

    def self.new(year, month, day) 
    @original_month = month 
    @original_day = day 

    # Christmas comes early! 
    super(year, 12, 25) 
    end 

end 

mdt = MyDate.new(2012, 1, 28) 

puts mdt.to_s 

Примечание: @original_month и @original_day не доступны в этом растворе. Следующее решение расширяет дату, поэтому вы можете получить доступ к исходным месяцам и дням. Для нормальных дат значения будут равны нулю.

require 'date' 

class Date 
    attr_accessor :original_month 
    attr_accessor :original_day 
end 

class MyDate < Date 

    def self.new(year, month, day) 

    # Christmas comes early! 
    date = super(year, 12, 25) 
    date.original_month = month 
    date.original_day = day 
    date 
    end 

end 

mdt = MyDate.new(2012, 1, 28) 

puts mdt.to_s 
puts mdt.original_month 

Но я бы рекомендовал:

require 'date' 

class MyDate < Date 

    def self.create(year, month, day) 
    @original_month = month 
    @original_day = day 

    # Christmas comes early! 
    new(year, 12, 25) 
    end 

end 

mdt = MyDate.create(2012, 1, 28) 

puts mdt.to_s 

или

require 'date' 

class Date 

    def this_year_christmas 
    # Christmas comes early! 
    self.class.new(year, 12, 28) 
    end 

end 

mdt = Date.new(2012, 1, 28).this_year_christmas 

puts mdt.to_s 
+0

Спасибо за отличный ответ! Я закончил перегрузку «нового» метода. Кажется очень странным, что выделение и инициализация объекта происходит одним способом. – jst

+0

В определении 'self.create', экземпляры vars устанавливаются в классе, а не в экземпляре. Использование 'instance_variable_set' в результате' new' позаботится об этом; просто убедитесь, что вы все равно вернете экземпляр. – Kelvin

Смежные вопросы