حتى لا نذهب بعيدًا ، سنحدد المصطلحات على الفور.
- التغليف - تعبئة البيانات والوظائف في مكون واحد.
- الإخفاء - هو مبدأ تصميم يتألف من تمييز وصول أجزاء مختلفة من البرنامج إلى المكونات الداخلية لبعضها البعض.
مأخوذة من الويكي . في لغة برمجة روبي مع التغليف ، يبدو أن كل شيء على ما يرام. مع الاختباء للوهلة الأولى أيضًا ، تتوفر لنا المتغيرات المحلية ، ومتغيرات الحالة ، والمستويات المختلفة للوصول إلى الأساليب ( public
، protected
، private
). ولكن في بعض الأحيان قد لا يكون هذا كافيا.
خذ بعين الاعتبار المثال التالي.
class User class Address < String def ==(other_object)
نعلن عن فئة Address
داخل User
، ونفترض أن هذا ليس مجرد عنوان مجرد ، بل عنوان بمنطق محدد مطلوب فقط في سياق كائنات User
. علاوة على ذلك ، لا نريد الوصول Address
هذا Address
من أي مكان في البرنامج ، أي ليس فقط لتغليفه داخل User
، ولكن أيضًا تريد إخفاءه لجميع الكائنات الأخرى. كيف تفعل ذلك؟
يمكنك تجربته من خلال private
.
class User private class Address < String def ==(other_object)
pry
والتنفيذ على سبيل المثال داخل pry
ونحصل على:
User::Address => User::Address User::Address.new => ""
وبالتالي ، فإننا نتحقق من أن المعدِّل private
في هذا السياق لا يعمل. ولكن هناك طريقة سحرية خاصة خاصة ستعمل كما ينبغي. بعد كل شيء ، الطبقات في الياقوت هي أيضًا ثوابت. الآن يمكننا كتابة private_constant :Address
والقبض على خطأ عند محاولة الوصول إلى User::Address
:
NameError: private constant User::Address referenced
الآن نحن نطرح المشكلة أكثر تعقيدًا. أضف فئة التخزين المؤقت التي ستستخدم redis.
ويبدو أنه لا يوجد شيء ينذر بالمشاكل ، حتى مكان ما في منتصف العرض ، داخل قالب erb ، لا يريد شخص ما كتابة 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
نقوم بإزالة remove_const
من مستوى الرؤية الفعلي للكائنات. ولكن قبل ذلك ، وضعنا SharedCache
داخل SharedCache
. علاوة على ذلك يمكننا تقييد الوصول إلى SharedCache::Redis
خلال private_constant
. ومع ذلك ، في هذه الحالة ، لن نتمكن بعد ذلك من الوصول إلى فئة 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>'
لما يمكن استخدامه:
- لإخفاء فئات المرافق الداخلية داخل فئة أو وحدة نمطية أخرى.
- التغليف مع إخفاء المنطق داخل فئات الخدمة - يمكنك منع الوصول إلى بعض الفئات بتجاوز كائنات الخدمة.
- إزالة فئات "خطيرة" من مستوى الرؤية الأعلى ، على سبيل المثال ، لمنع الوصول إلى قواعد البيانات من العرض أو المتسللين. في Rails ، يمكنك "إخفاء" جميع فئات ActiveRecord ومنحهم الوصول الانتقائي إلى أماكن محددة.
ومثال على تنفيذ 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