Verschleierung in Ruby. Verstecke auch Klassen vor der obersten Ebene

Um nicht weit zu kommen, werden wir die Begriffe sofort definieren.


  • Kapselung - Packen von Daten und Funktionen in eine einzige Komponente.
  • Verschleierung - ist ein Entwurfsprinzip, das darin besteht, den Zugriff verschiedener Programmteile auf die internen Komponenten voneinander zu unterscheiden.

Entnommen aus dem Wiki . In der Ruby-Programmiersprache mit Kapselung scheint alles in Ordnung zu sein. Wenn wir uns auf den ersten Blick verstecken, stehen uns auch lokale Variablen, Instanzvariablen und verschiedene Zugriffsebenen auf Methoden ( public , protected , private ) zur Verfügung. Aber manchmal reicht das nicht aus.


Betrachten Sie das folgende Beispiel.


 class User class Address < String def ==(other_object) #   end end def initialize(name:, address: nil) @name = name @address = Address.new(address) end end 

Wir deklarieren die Address innerhalb von User . Wir gehen davon aus, dass dies nicht nur eine abstrakte Adresse ist, sondern eine Adresse mit spezifischer Logik, die nur im Kontext von User Objekten benötigt wird. Außerdem möchten wir nicht, dass diese Address von irgendwo im Programm zugänglich ist, d. H. kapseln Sie es nicht nur in User , sondern möchten es auch für alle anderen Objekte ausblenden. Wie kann man das machen?


Sie können es private versuchen.


 class User private class Address < String def ==(other_object) #   end end end 

pry und führen zum Beispiel in pry und erhalten:


 User::Address => User::Address User::Address.new => "" 

Daher überprüfen wir, ob der private Modifikator in diesem Kontext nicht funktioniert. Es gibt jedoch nur eine magische Methode private_constant , die so funktioniert, wie sie sollte. Schließlich sind Rubinklassen auch Konstanten. Jetzt können wir private_constant :Address schreiben und einen Fehler abfangen, wenn wir versuchen, auf User::Address : zuzugreifen:


NameError: private constant User::Address referenced


Jetzt stellen wir das Problem komplizierter. Fügen Sie eine Caching-Klasse hinzu, die Redis verwendet.


 #shared_cache.rb require 'redis' class SharedCache end 

Und es scheint, dass nichts auf Probleme hindeutet, bis irgendwo in der Mitte der Ansicht, innerhalb der erb-Vorlage, jemand redis.get \ redis.set schreiben redis.get und sogar SharedCache umgeht. Wir behandeln wie folgt:


 require 'redis' SharedCache.send :const_set, :Redis, Redis Object.send :remove_const, :Redis Redis NameError: uninitialized constant Redis from (pry):7:in `__pry__' 

Was ist passiert? Durch einen Aufruf von remove_const entfernen wir Redis aus der tatsächlichen Sichtbarkeit von Objekten auf remove_const Ebene. SharedCache wir Redis in SharedCache . Außerdem können wir den Zugriff auf SharedCache::Redis über private_constant . In diesem Fall können wir die Redis Klasse jedoch in keiner Weise mehr erreichen, selbst wenn wir sie woanders verwenden möchten. Wir adeln und lassen uns in mehreren Klassen Anforderungen stellen:


 class SharedCache require_to 'redis', :Redis private_constant :Redis def storage Redis end end class SharedCache2 require_to 'redis', :Redis private_constant :Redis end 

Versuche, Redis anzurufen:


 [1] pry(main)> SharedCache::Redis NameError: private constant SharedCache::Redis referenced from (pry):1:in `<main>' [2] pry(main)> require 'redis' => false [3] pry(main)> Redis NameError: uninitialized constant Redis from (pry):6:in `<main>' [4] pry(main)> SharedCache.new.storage => Redis [5] pry(main)> SharedCache2::Redis NameError: private constant SharedCache2::Redis referenced from (pry):1:in `<main>' 

Für was es verwendet werden kann:


  • Interne Dienstprogrammklassen in einer anderen Klasse oder einem anderen Modul ausblenden.
  • Kapselung mit versteckter Logik in Serviceklassen - Sie können den Zugriff auf einige Klassen unter Umgehung von Serviceobjekten verhindern.
  • Entfernen Sie "gefährliche" Klassen aus der Sichtbarkeit der obersten Ebene, um beispielsweise den Zugriff auf Datenbanken aus View oder Serialisierern zu verhindern. In Rails können Sie alle ActiveRecord-Klassen „ausblenden“ und ihnen selektiven Zugriff auf bestimmte Orte gewähren.

Und eine Beispielimplementierung von require_to , die Konstanten von der obersten Ebene auf die gewünschte Sichtbarkeitsebene verschiebt.


require_to
 class Object def const_hide sym, obj _hidden_consts.const_set sym, obj Object.send :remove_const, sym end def hidden_constants _hidden_consts.constants end def hidden_const sym _hidden_consts.const_get sym end def require_to(name, sym, to: nil) require name if Object.const_defined? sym obj = Object.const_get sym const_hide sym, obj else obj = hidden_const sym end (to || self).const_set sym, obj end private def _hidden_consts @@_hidden_consts ||= Class.new end end 

Source: https://habr.com/ru/post/de419969/


All Articles