2009-05-22 6 views
2

Wenn Sie sich das obige Beispiel anschauen, das sich auf die Widget-Klasse (unten) bezieht, verstoßen die Methoden send und instance_eval gegen alle Schutzmechanismen, die von privater und geschützter Sichtbarkeit bereitgestellt werden. Wenn ja, warum sollten Sie sich überhaupt mit dem privaten und geschützten Zugriff in Ruby beschäftigen, da es keine Garantie dafür gibt, dass Ihre Definitionen eingehalten werden?Beeinträchtigen die Ruby-Methoden instance_eval() und send() nicht die Vorteile der privaten Sichtbarkeit?

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 
+0

Ähnliche Fragen: http://stackoverflow.com/questions/2519136/why-can-i-access-private-protected-methods-using-objectsend-in-ruby –

Antwort

3

Wenn Sie wirklich wollen Instanzen von Widget zu schützen, können Sie dies tun (und ein paar andere Sachen, die Code hier ist nicht eine komplette Sicherheitslösung, nur indikativ):

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 
+2

(Aber ich würde es nicht empfehlen, da Widget mit fast allen Bibliotheken, die Metaprogrammierung verwenden, nicht kompatibel wäre.) –

+0

Interessante mögliche Lösung. Was verhindern Sie, indem Sie class_eval neu definieren? Ich verstehe den letzten Abschnitt nicht: "class << self .. end" –

+0

Klasse << self ... end wertet den Inhalt im Metaklassenbereich aus. Das heißt, ich privatisiere die _class_ Methode class_eval, nicht die Instanzmethode. Siehe http://stackoverflow.com/questions/678037/. Der Grund dafür ist, dass Sie die Klasse nicht ändern können, um sie zu deaktivieren. Sie sollten die Klasse wahrscheinlich auch einfrieren. –

1

Zumindest drücken Sie aus, was die öffentliche API der Widget-Klasse ist.

12

ruby ​​glaubt daran, Ihnen die Macht zu geben, das zu tun, was Sie wollen. Es macht es einfach nicht versehentlich schießen Sie Ihren Fuß aus - wenn Sie die privaten Deklarationen untergraben möchten, müssen Sie Syntax verwenden, die Ihnen klar macht, dass Sie es tun. Beachten Sie, dass die Person, die schließlich entscheidet, was der Code tun sollte oder nicht tun sollte, die Person ist, die eine Bibliothek verwendet, nicht die Person, die sie schreibt.

+0

Dies ist die richtige Antwort. –

5

kann ich nicht kommentieren, wegen niedrigen rep: /.

Redefining Sende es keine Verwendung, da send für nur der gemeinsame Name ist __send__ (das ist zu unterstreichen, unterstreichen, „senden“, unterstreichen, unterstreichen), die das Verfahren implementiert Nachricht tatsächlich gesendet wird. Die Neudefinition von __method__ wird nicht empfohlen. Zusätzlich kann die andere Person auch die Klasse wieder öffnen und die Definition zurückkehren:

class Widget 
    def send(method, *args, &block) 
    super 
    end 
    #and so on 
end 

In Ruby 1.9 ist das Verhalten etwas anders: #send tatsächlich Sichtbarkeit ehrt, __send__ nicht.

Private in Ruby hat eher einen deklarativen Zweck: Methoden, die als privat deklariert sind, sind ein Implementierungsdetail und kein API-Detail. Es ist Ihnen nicht erlaubt, versehentlich eine Nachricht von außen zu senden. Aber jemand kann diese Einschränkung immer noch gewaltsam umgehen, wenn sie es für richtig halten - auf eigene Rechnung.

0

Die Nachricht nach Hause ist: nicht stören.

Ruby, wie Python, ist absolut am Sandboxing. Wenn du versuchst etwas abzusperren, wird es immer Chancen geben, es zu umgehen. Die Vielzahl von Möglichkeiten, ein privates Attribut in Ruby zu erhalten, beweist meinen Standpunkt.

Warum? Weil sie so gestaltet sind. Beide Sprachen sind so konzipiert, dass sie zur Laufzeit mit – herumgestochert werden können, was ihnen ihre Macht gibt. Indem Sie Ihre Klasse versiegeln, entziehen Sie anderen die Kraft, die Rubys Metaprogrammierung bietet.

Java hat Reflexion. C++ hat Zeiger. Selbst Haskell hat unsafePerformIO. Wenn Sie Ihr Programm schützen möchten, müssen Sie es auf der Betriebssystemebene schützen und nicht die Sprache verwenden.