الإخفاء في روبي. قم أيضًا بإخفاء الصفوف من المستوى الأعلى

حتى لا نذهب بعيدًا ، سنحدد المصطلحات على الفور.


  • التغليف - تعبئة البيانات والوظائف في مكون واحد.
  • الإخفاء - هو مبدأ تصميم يتألف من تمييز وصول أجزاء مختلفة من البرنامج إلى المكونات الداخلية لبعضها البعض.

مأخوذة من الويكي . في لغة برمجة روبي مع التغليف ، يبدو أن كل شيء على ما يرام. مع الاختباء للوهلة الأولى أيضًا ، تتوفر لنا المتغيرات المحلية ، ومتغيرات الحالة ، والمستويات المختلفة للوصول إلى الأساليب ( public ، protected ، private ). ولكن في بعض الأحيان قد لا يكون هذا كافيا.


خذ بعين الاعتبار المثال التالي.


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

نعلن عن فئة Address داخل User ، ونفترض أن هذا ليس مجرد عنوان مجرد ، بل عنوان بمنطق محدد مطلوب فقط في سياق كائنات User . علاوة على ذلك ، لا نريد الوصول Address هذا Address من أي مكان في البرنامج ، أي ليس فقط لتغليفه داخل User ، ولكن أيضًا تريد إخفاءه لجميع الكائنات الأخرى. كيف تفعل ذلك؟


يمكنك تجربته من خلال private .


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

pry والتنفيذ على سبيل المثال داخل pry ونحصل على:


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

وبالتالي ، فإننا نتحقق من أن المعدِّل private في هذا السياق لا يعمل. ولكن هناك طريقة سحرية خاصة خاصة ستعمل كما ينبغي. بعد كل شيء ، الطبقات في الياقوت هي أيضًا ثوابت. الآن يمكننا كتابة private_constant :Address والقبض على خطأ عند محاولة الوصول إلى User::Address :


NameError: private constant User::Address referenced


الآن نحن نطرح المشكلة أكثر تعقيدًا. أضف فئة التخزين المؤقت التي ستستخدم redis.


 #shared_cache.rb require 'redis' class SharedCache end 

ويبدو أنه لا يوجد شيء ينذر بالمشاكل ، حتى مكان ما في منتصف العرض ، داخل قالب 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 

Source: https://habr.com/ru/post/ar419969/


All Articles