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)
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)
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.
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