为了不言而喻,我们将立即定义术语。
- 封装-将数据和功能打包到单个组件中。
- 隐藏-是一种设计原则,在于区分程序的不同部分对彼此内部组件的访问。
摘自Wiki 。 在带有封装的Ruby编程语言中,一切似乎都很好。 乍一看,本地变量,实例变量以及对方法的不同访问级别( public
, protected
, private
)也可供我们使用。 但是有时候这可能还不够。
考虑以下示例。
class User class Address < String def ==(other_object)
我们在User
内声明Address
类,我们假定这不仅是一些抽象地址,而且是仅在User
对象的上下文中才需要的具有特定逻辑的地址。 而且,我们不希望该Address
可从程序中的任何位置访问,即 不仅将其封装在User
,而且还希望对其他所有对象隐藏它。 怎么做?
您可以通过private
尝试。
class User private class Address < String def ==(other_object)
pry
在pry
内部pry
并执行例如:
User::Address => User::Address User::Address.new => ""
因此,我们验证了private
修饰符在这种情况下不起作用。 但是只有一个神奇的private_constant
方法可以正常工作。 毕竟,ruby中的类也是常量。 现在我们可以编写private_constant :Address
并在尝试访问User::Address
时捕获错误:
NameError: private constant User::Address referenced
现在,我们提出的问题更加复杂。 添加一个将使用redis的缓存类。
似乎没有什么预兆,直到在View中间的erb模板中的某个地方,有人不想redis.set
编写redis.get
\ redis.set
,甚至绕过了SharedCache。 我们对待如下:
require 'redis' SharedCache.send :const_set, :Redis, Redis Object.send :remove_const, :Redis Redis NameError: uninitialized constant Redis from (pry):7:in `__pry__'
发生什么事了 通过调用remove_const
我们从对象的实际顶级可见性中删除Redis。 但在此之前,我们将Redis放在SharedCache
。 此外,我们可以通过private_constant
限制对SharedCache::Redis
访问。 但是,在这种情况下,即使我们想在其他地方使用它,我们也将不再能够以任何方式到达Redis
类。 我们高尚,让我们在几个类中提出require
:
class SharedCache require_to 'redis', :Redis private_constant :Redis def storage Redis end end class SharedCache2 require_to 'redis', :Redis private_constant :Redis end
尝试致电Redis:
[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>'
对于它的用途:
- 将内部实用程序类隐藏在另一个类或模块中。
- 在服务类内部使用隐藏逻辑进行封装-您可以防止绕过服务对象访问某些类。
- 从顶级可见性中删除“危险”类,例如,禁止从View或序列化程序访问数据库。 在Rails中,您可以“隐藏”所有ActiveRecord类,并为他们提供对特定位置的选择性访问。
还有require_to
的示例实现,它将常量从顶级移动到所需的可见性级别。
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