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