Разве Ruby-методы instance_eval() и send() не сводят на нет преимущества частной видимости?
-
23-08-2019 - |
Вопрос
w = Widget.new # Create a Widget
w.send :utility_method # Invoke private method!
w.instance_eval { utility_method } # Another way to invoke it
w.instance_eval { @x } # Read instance variable of w
Если посмотреть на приведенный выше пример, относящийся к классу Widget (ниже), методы send и instance_eval нарушают все меры защиты, предоставляемые частной и защищенной видимостью.Если да, то зачем вообще беспокоиться о частном и защищенном доступе в Ruby, поскольку нет никакой гарантии, что ваши определения будут соблюдены?
class Widget
def x # Accessor method for @x
@x
end
protected :x # Make it protected
def utility_method # Define a method
nil
end
private :utility_method # And make it private
end
Решение
Если вы Действительно хотите защитить экземпляры Widget
, вы можете сделать это (и кучу других вещей;код здесь нет полное решение безопасности, просто ориентировочное):
class Widget
def some_public_method
...
end
private
def utility_method
...
end
def send(method, *args, &block)
raise NotImplementedError.new('Widget is secure. Stop trying to hack me.')
end
def instance_eval(&block)
raise NotImplementedError.new('Widget is secure. Stop trying to hack me.')
end
class <<self
private
def class_eval(&block)
raise NotImplementedError.new('Widget is secure. Stop trying to hack me.')
end
end
end
Widget.freeze
Другие советы
Ruby верит в то, что дает вам возможность делать то, что вы хотите.это просто не облегчает задачу случайно отбросьте ногу - если вы хотите нарушить частные объявления, вам придется использовать синтаксис, который ясно дает понять, что вы это делаете.обратите внимание, что окончательное решение о том, что должен или не должен делать код, принимает человек, использующий библиотеку, а не тот, кто ее пишет.
Я не могу комментировать из-за низкой репутации :/.
Переопределение send бесполезно, потому что send — это просто общее имя для __отправлять__ (это подчеркивание, подчеркивание, «отправить», подчеркивание, подчеркивание), который фактически реализует отправку сообщения.Переопределение любого __метод__ не рекомендуется.Кроме того, другой человек также может повторно открыть класс и отменить определение:
class Widget
def send(method, *args, &block)
super
end
#and so on
end
В Ruby 1.9 поведение немного другое:#send на самом деле обеспечивает видимость, __отправлять__ нет.
Private в Ruby имеет скорее декларативную цель:методы, объявленные как частные, являются деталями реализации, а не деталями API.Вам не разрешено случайно отправить сообщение извне.Но любой желающий по-прежнему может принудительно обойти это ограничение, если сочтет нужным — за свой счет.
По крайней мере, вы выражаете, что такое общедоступный API класса Widget.
Итоговое послание таково:не беспокойся.
Ruby, как и Python, абсолютно не справляется с песочницей.Если вы попытаетесь что-то заблокировать, скорее всего, всегда найдется способ обойти это.Множество способов получить приватный атрибут в Ruby доказывают мою точку зрения.
Почему?Потому что они созданы для этого.Оба языка спроектированы так, чтобы с ними можно было работать во время выполнения — именно это придает им силу.Запечатывая свой класс, вы лишаете других возможностей, которые предоставляет метапрограммирование Руби.
В Java есть отражение.В C++ есть указатели.Даже в Haskell есть unsafePerformIO.Если вы хотите защитить свою программу, вам необходимо защитить ее на уровне операционной системы, а не на уровне языка.