隐藏在Ruby中。 同时从顶级隐藏类

为了不言而喻,我们将立即定义术语。


  • 封装-将数据和功能打包到单个组件中。
  • 隐藏-是一种设计原则,在于区分程序的不同部分对彼此内部组件的访问。

摘自Wiki 。 在带有封装的Ruby编程语言中,一切似乎都很好。 乍一看,本地变量,实例变量以及对方法的不同访问级别( publicprotectedprivate )也可供我们使用。 但是有时候这可能还不够。


考虑以下示例。


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

我们在User内声明Address类,我们假定这不仅是一些抽象地址,而且是仅在User对象的上下文中才需要的具有特定逻辑的地址。 而且,我们不希望该Address可从程序中的任何位置访问,即 不仅将其封装在User ,而且还希望对其他所有对象隐藏它。 怎么做?


您可以通过private尝试。


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

prypry内部pry并执行例如:


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

因此,我们验证了private修饰符在这种情况下不起作用。 但是只有一个神奇的private_constant方法可以正常工作。 毕竟,ruby中的类也是常量。 现在我们可以编写private_constant :Address并在尝试访问User::Address时捕获错误:


NameError: private constant User::Address referenced


现在,我们提出的问题更加复杂。 添加一个将使用redis的缓存类。


 #shared_cache.rb require 'redis' class SharedCache end 

似乎没有什么预兆,直到在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 

Source: https://habr.com/ru/post/zh-CN419969/


All Articles