Когда использовать undef_method и когда использовать remove_method?

Я хочу переопределить метод, но избегаю связанного с ним предупреждения. Должен ли я использовать undef_method или remove_method для этого?

(Да, методы переопределения немного взломаны. Я делаю это, потому что у меня есть memoization, который я хочу использовать, когда выполняются модульные тесты, но не при запуске самой программы.)

Ответы

Ответ 1

Из прекрасного руководства:

undef_method (символ) → self

Предотвращает реакцию текущего класса на вызовы по указанному методу. Контрастируйте это с помощью remove_method, который удаляет метод из определенного класса; Ruby будет по-прежнему искать суперклассы и смешанные модули для возможного приемника.

Итак, remove_method:

class CC < C
    remove_method :m
end

по существу противоположно этому:

class CC < C
    def m
    end
end

Где def m добавляет метод m в класс, remove_method :m удаляет m. Но если суперкласс имеет метод m, то он все равно будет использоваться.

undef_method, OTOH, больше похоже на это:

class CC < C
    def m
        raise 'No, you cannot do that.'
    end
end

Итак, undef_method фактически не удаляет этот метод, он заменяет метод специальным внутренним флагом, который заставляет Ruby жаловаться, если вы попытаетесь вызвать этот метод.

Похоже, вы пытаетесь заменить существующий метод, и заменить семантически то же самое, что и удаление, за которым следует добавление, поэтому remove_method, вероятно, более уместно. Однако, если вы хотите быть параноидальным и убедиться, что метод замены на месте, тогда undef_method будет полезен; или, если по какой-то причине вам нужно удалить метод в одном месте и добавить его в другое, undef_method, по крайней мере, скажет вам, что вы выполнили только половину задания, тогда как remove_method либо оставит вас с реализацией суперкласса ( и возможные странные ошибки) или довольно запутанный NoMethodError.

Ответ 2

Вы можете удалить метод двумя способами. Резкий

Module#undef_method( ) 

удаляет все методы, включая унаследованные. Более добрый

Module#remove_method( ) 

удаляет метод из приемника, но он оставляет только унаследованные методы.

См. ниже 2 простых примера -

Пример 1 с использованием undef_method

class A 
    def x
        puts "x from A class"
    end
end

class B < A
    def x
        puts "x from B Class"
    end
    undef_method :x
end

obj = B.new
obj.x

результат - main.rb: 15: in ': undefined method x 'для # (NoMethodError)

Пример 2 с использованием remove_method

class A 
    def x
        puts "x from A class"
    end
end

class B < A
    def x
        puts "x from B Class"
    end
    remove_method :x
end

obj = B.new
obj.x

Результат - $ ruby ​​main.rb

x из класса A