Penyembunyian di Ruby. Juga sembunyikan kelas dari Top-Level

Agar tidak melangkah terlalu jauh, kami akan segera mendefinisikan persyaratan.


  • Enkapsulasi - mengemas data dan fungsi menjadi satu komponen.
  • Penyembunyian - adalah prinsip desain, yang terdiri dalam membedakan akses berbagai bagian program ke komponen internal satu sama lain.

Diambil dari wiki . Dalam bahasa pemrograman Ruby dengan enkapsulasi, semuanya tampak baik-baik saja. Dengan menyembunyikan pada pandangan pertama juga, variabel lokal, variabel instan, berbagai tingkat akses ke metode ( public , protected , private ) tersedia untuk kita. Tetapi kadang-kadang ini mungkin tidak cukup.


Perhatikan contoh berikut.


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

Kami menyatakan kelas Address di dalam User , kami menganggap bahwa ini bukan hanya beberapa alamat abstrak, tetapi alamat dengan logika spesifik yang hanya diperlukan dalam konteks objek User . Dan terlebih lagi, kami tidak ingin Address ini dapat diakses dari mana saja dalam program ini, mis. tidak hanya merangkumnya di dalam User , tetapi juga ingin menyembunyikannya untuk semua objek lainnya. Bagaimana cara melakukannya?


Anda dapat mencobanya melalui private .


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

pry dan mengeksekusi misalnya di dalam pry dan mendapatkan:


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

Jadi, kami memverifikasi bahwa pengubah private dalam konteks ini tidak berfungsi. Tetapi hanya ada metode private_constant ajaib yang akan berfungsi sebagaimana mestinya. Lagipula, kelas-kelas di ruby โ€‹โ€‹juga merupakan konstanta. Sekarang kita dapat menulis private_constant :Address dan menangkap kesalahan ketika mencoba mengakses User::Address :


NameError: private constant User::Address referenced


Sekarang kami mengajukan masalah lebih rumit. Tambahkan kelas caching yang akan menggunakan redis.


 #shared_cache.rb require 'redis' class SharedCache end 

Dan sepertinya tidak ada yang menandakan masalah, sampai di suatu tempat di tengah View, di dalam template erb, seseorang tidak ingin menulis redis.get \ redis.set , melewati bahkan SharedCache. Kami memperlakukan sebagai berikut:


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

Apa yang terjadi Melalui panggilan ke remove_const kami menghapus Redis dari visibilitas Top-Level objek yang sebenarnya. Tetapi sebelum ini, kami menempatkan Redis di dalam SharedCache . Selanjutnya kita dapat membatasi akses ke SharedCache::Redis melalui private_constant . Namun, dalam hal ini, kita tidak akan lagi dapat mencapai kelas Redis dengan cara apa pun, bahkan jika kita ingin menggunakannya di tempat lain. Kami memuliakan dan membuat kami require di dalam beberapa kelas:


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

Upaya untuk memanggil 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>' 

Untuk apa itu bisa digunakan:


  • Untuk menyembunyikan kelas utilitas internal di dalam kelas atau modul lain.
  • Enkapsulasi dengan persembunyian logika di dalam kelas layanan - Anda dapat mencegah akses ke beberapa kelas melewati objek layanan.
  • Hapus kelas "berbahaya" dari visibilitas Tingkat Atas, misalnya, untuk melarang akses ke database dari View atau serialis. Di Rails, Anda dapat "menyembunyikan" semua kelas ActiveRecord dan memberi mereka akses selektif ke tempat-tempat tertentu.

Dan contoh implementasi require_to yang memindahkan konstanta dari Level Atas ke level visibilitas yang diinginkan.


membutuhkan_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/id419969/


All Articles