Para não ir longe, definiremos imediatamente os termos.
- Encapsulamento - empacotando dados e funções em um único componente.
- Ocultação - é um princípio de design, que consiste em diferenciar o acesso de diferentes partes do programa aos componentes internos um do outro.
Retirado do wiki . Na linguagem de programação Ruby com encapsulamento, tudo parece estar bem. Escondendo à primeira vista também, variáveis locais, variáveis de instância, diferentes níveis de acesso a métodos ( public
, protected
, private
) estão disponíveis para nós. Mas às vezes isso pode não ser suficiente.
Considere o seguinte exemplo.
class User class Address < String def ==(other_object)
Declaramos a classe Address
dentro de User
, assumimos que este não é apenas um endereço abstrato, mas um endereço com lógica específica que é necessária apenas no contexto dos objetos User
. Além disso, não queremos que este Address
seja acessível de qualquer lugar do programa, ou seja, não apenas o encapsula dentro do User
, mas também deseja ocultá-lo para todos os outros objetos. Como fazer isso?
Você pode experimentá-lo através do private
.
class User private class Address < String def ==(other_object)
pry
e executamos, por exemplo, dentro do pry
e obtemos:
User::Address => User::Address User::Address.new => ""
Assim, verificamos que o modificador private
nesse contexto não funciona. Mas existe apenas um método private_constant
mágico que funcionará como deveria. Afinal, as classes em ruby também são constantes. Agora podemos escrever o private_constant :Address
e capturar um erro ao tentar acessar o User::Address
:
NameError: private constant User::Address referenced
Agora, colocamos o problema mais complicado. Adicione uma classe de cache que usará redis.
E parece que nada indica problemas, até que em algum lugar no meio do View, dentro do modelo erb, alguém não queira escrever redis.get
\ redis.set
, ignorando até o SharedCache. Tratamos da seguinte forma:
require 'redis' SharedCache.send :const_set, :Redis, Redis Object.send :remove_const, :Redis Redis NameError: uninitialized constant Redis from (pry):7:in `__pry__'
O que aconteceu Por meio de uma chamada para remove_const
removemos o Redis da visibilidade real dos objetos de nível superior. Mas antes disso, colocamos o Redis dentro do SharedCache
. Além disso, podemos restringir o acesso ao SharedCache::Redis
através de private_constant
. No entanto, nesse caso, não poderemos mais alcançar a classe Redis
nenhuma maneira, mesmo se quisermos usá-la em outro lugar. Nós enobrecemos e permitimos que require
várias classes:
class SharedCache require_to 'redis', :Redis private_constant :Redis def storage Redis end end class SharedCache2 require_to 'redis', :Redis private_constant :Redis end
Tentativas de ligar para 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>'
Para o que pode ser usado:
- Ocultar classes de utilitários internos dentro de outra classe ou módulo.
- Encapsulamento com ocultação de lógica dentro de classes de serviço - você pode impedir o acesso a algumas classes ignorando objetos de serviço.
- Remova as classes "perigosas" da visibilidade de nível superior, por exemplo, para proibir o acesso aos bancos de dados do View ou serializadores. No Rails, você pode "ocultar" todas as classes do ActiveRecord e dar a elas acesso seletivo a locais específicos.
E um exemplo de implementação de require_to
que move constantes do Nível Superior para o nível de visibilidade desejado.
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