نمط خفيف الوزن

لقطة شاشة لحزمة برنامج الكتابة في LibreOffice.
تستخدم برامج تحرير النصوص، مثل LibreOffice Writer ، نمط الوزن الخفيف في كثير من الأحيان.

في برمجة الحاسوب ، يشير نمط تصميم البرمجيات الخفيف إلى كائن يقلل من استخدام الذاكرة عن طريق مشاركة بعض بياناته مع كائنات أخرى مماثلة. يُعد نمط البرمجيات الخفيف أحد أنماط تصميم البرمجيات الثلاثة والعشرين المعروفة باسم "نمط الوظائف" . [ 1 ]

في سياقات أخرى، تُسمى فكرة مشاركة هياكل البيانات بـ "التجزئة" .

صاغ بول كالدر ومارك لينتون هذا المفهوم واستكشفاه على نطاق واسع لأول مرة في عام 1990 [ 2 ] لمعالجة معلومات الرموز الرسومية بكفاءة في محرر مستندات WYSIWYG . [ 3 ] ومع ذلك، فقد استُخدمت تقنيات مماثلة في أنظمة أخرى منذ عام 1988. [ 4 ]

ملخص

يُعدّ نمط Flyweight مفيدًا عند التعامل مع عدد كبير من الكائنات التي تشترك في عناصر بسيطة متكررة، والتي ستستهلك كمية كبيرة من الذاكرة إذا تم تضمينها بشكل فردي. من الشائع تخزين البيانات المشتركة في هياكل بيانات خارجية وتمريرها إلى الكائنات مؤقتًا عند استخدامها.

من الأمثلة الكلاسيكية على ذلك هياكل البيانات المستخدمة لتمثيل الأحرف في معالج النصوص . ببساطة، قد يحتوي كل حرف في المستند على كائن رسومي يتضمن مخطط الخط، وقياسات الخط، وبيانات التنسيق الأخرى. إلا أن هذا يتطلب استخدام مئات أو آلاف البايتات من الذاكرة لكل حرف. بدلاً من ذلك، يمكن لكل حرف أن يحتوي على مرجع لكائن رسومي مشترك بين جميع نسخ الحرف نفسه في المستند. بهذه الطريقة، لا يلزم سوى تخزين موضع كل حرف داخليًا.

ونتيجة لذلك، يمكن للأجسام خفيفة الوزن أن: [ 5 ]

  • تخزين حالة جوهرية ثابتة، مستقلة عن السياق وقابلة للمشاركة (على سبيل المثال، رمز الحرف 'A' في مجموعة أحرف معينة)
  • توفير واجهة لتمرير حالة خارجية متغيرة، تعتمد على السياق، ولا يمكن مشاركتها (على سبيل المثال، موضع الحرف 'A' في مستند نصي).

يمكن للعملاء إعادة استخدام Flyweightالكائنات وتمرير الحالة الخارجية حسب الحاجة، مما يقلل من عدد الكائنات التي يتم إنشاؤها فعليًا.

بناء

نموذج لفئة UML ومخطط تسلسل لنمط تصميم Flyweight. [ 6 ]

يوضح مخطط فئات UML أعلاه ما يلي :

  • الفئة Clientالتي تستخدم نمط وزن الذبابة
  • الفئة FlyweightFactoryالتي تقوم بإنشاء ومشاركة Flyweightالكائنات
  • الواجهة التي تستقبل الحالة الخارجية وتنفذ عملية Flyweightما
  • الفئة Flyweight1التي تُنفذ Flyweightوتخزن الحالة الجوهرية

يوضح مخطط التسلسل التفاعلات التالية أثناء التشغيل :

  1. Clientيستدعي الكائن الدالة ، getFlyweight(key)والتي FlyweightFactoryبدورها تُرجع Flyweight1كائنًا.
  2. بعد استدعاء الكائن operation(extrinsicState)المُعاد Flyweight1، يتم استدعاء الكائن Clientمرة أخرى .getFlyweight(key)FlyweightFactory
  3. تقوم هذه FlyweightFactoryالدالة بإرجاع الكائن الموجود مسبقًا Flyweight1.

تفاصيل التنفيذ

توجد طرق متعددة لتطبيق نمط الوزن الخفيف. أحد الأمثلة على ذلك هو قابلية التغيير: ما إذا كان من الممكن تغيير الكائنات التي تخزن حالة الوزن الخفيف الخارجية.

يسهل مشاركة الكائنات غير القابلة للتغيير ، لكنها تتطلب إنشاء كائنات خارجية جديدة كلما طرأ تغيير على حالتها. في المقابل، يمكن للكائنات القابلة للتغيير مشاركة حالتها. تتيح قابلية التغيير إعادة استخدام أفضل للكائنات من خلال التخزين المؤقت وإعادة تهيئة الكائنات القديمة غير المستخدمة. عادةً ما تكون المشاركة غير مجدية عندما تكون الحالة متغيرة للغاية.

وتشمل المخاوف الرئيسية الأخرى الاسترجاع (كيف يصل العميل النهائي إلى برنامج Flyweight)، والتخزين المؤقت والتزامن .

استرجاع

غالبًا ما تكون واجهة المصنع لإنشاء أو إعادة استخدام الكائنات الخفيفة واجهةً لنظام أساسي معقد. على سبيل المثال، تُنفَّذ واجهة المصنع عادةً كنمط Singleton لتوفير وصول شامل لإنشاء الكائنات الخفيفة.

بشكل عام، تبدأ خوارزمية الاسترجاع بطلب للحصول على كائن جديد عبر واجهة المصنع.

يُحال الطلب عادةً إلى ذاكرة تخزين مؤقتة مناسبة بناءً على نوع الكائن. إذا تم تلبية الطلب بواسطة كائن موجود في ذاكرة التخزين المؤقتة، فقد يُعاد تهيئته وإعادته. وإلا، يتم إنشاء كائن جديد. إذا كان الكائن مُقسّمًا إلى عدة مكونات فرعية خارجية، فسيتم تجميعها معًا قبل إعادة الكائن.

التخزين المؤقت

هناك طريقتان لتخزين الكائنات الخفيفة الوزن مؤقتًا: التخزين المؤقت المُدار والتخزين المؤقت غير المُدار.

يمكن تخزين الكائنات ذات الحالة المتغيرة للغاية مؤقتًا باستخدام بنية FIFO . تحافظ هذه البنية على الكائنات غير المستخدمة في ذاكرة التخزين المؤقت، دون الحاجة إلى البحث فيها.

في المقابل، تتميز ذاكرات التخزين المؤقت غير المُدارة بانخفاض التكاليف الأولية: حيث تُهيأ الكائنات الخاصة بها دفعة واحدة عند وقت الترجمة أو بدء التشغيل. وبمجرد ملء ذاكرة التخزين المؤقت بالكائنات، قد تكون تكلفة خوارزمية استرجاع الكائنات أعلى من تكلفة عمليات الإضافة/الحذف في ذاكرة التخزين المؤقت المُدارة.

عند استرجاع كائنات خارجية ذات حالة ثابتة، يكفي البحث في ذاكرة التخزين المؤقت عن كائن بالحالة المطلوبة. إذا لم يُعثر على كائن من هذا النوع، يجب إنشاء كائن جديد بتلك الحالة. أما عند استرجاع كائنات خارجية ذات حالة قابلة للتغيير، فيجب البحث في ذاكرة التخزين المؤقت عن كائن غير مستخدم لإعادة تهيئته إذا لم يُعثر على كائن مستخدم. إذا لم يتوفر أي كائن غير مستخدم، يجب إنشاء كائن جديد وإضافته إلى ذاكرة التخزين المؤقت.

يمكن استخدام ذاكرات تخزين مؤقتة منفصلة لكل فئة فرعية فريدة من الكائنات الخارجية. ويمكن تحسين ذاكرات التخزين المؤقتة المتعددة بشكل منفصل، مع ربط خوارزمية بحث فريدة بكل ذاكرة. ويمكن تغليف نظام تخزين الكائنات المؤقت هذا بنمط سلسلة المسؤولية ، مما يعزز الترابط المرن بين المكونات.

التزامن

يجب مراعاة بعض الأمور عند إنشاء كائنات Flyweight على عدة خيوط معالجة. إذا كانت قائمة القيم محدودة ومعروفة مسبقًا، فيمكن إنشاء كائنات Flyweight مسبقًا واسترجاعها من حاوية على عدة خيوط معالجة دون حدوث تنازع. أما إذا تم إنشاء كائنات Flyweight على عدة خيوط معالجة، فهناك خياران:

  1. اجعل عملية إنشاء مثيلات Flyweight أحادية الخيوط، مما يؤدي إلى حدوث تنازع ويضمن وجود مثيل واحد لكل قيمة.
  2. السماح للخيوط المتزامنة بإنشاء مثيلات متعددة من نوع flyweight، وبالتالي القضاء على التنازع والسماح بوجود مثيلات متعددة لكل قيمة.

لتمكين المشاركة الآمنة بين العملاء والخيوط، يمكن تحويل كائنات flyweight إلى كائنات ذات قيمة ثابتة ، حيث تعتبر مثيلتان متساويتين إذا كانت قيمهما متساوية.

أمثلة

سي شارب

في هذا المثال، MyObjectيستخدم كل مثيل من الفئة Pointerفئة لتوفير البيانات.

// يُعرّف كائن Flyweight الذي يتكرر. public class Flyweight { public string Name { get ; set ; } public string Location { get ; set ; } public string Website { get ; set ; } public byte [] Logo { get ; set ; } }public static class Pointer { public static readonly Flyweight Company = new Flyweight { Name = "ABC" , Location = "XYZ" , Website = "www.example.com" }; }public class MyObject { public string Name { get ; set ; } public string Company => Pointer . Company . Name ; }

لغة سي++

توفر مكتبة القوالب القياسية للغة C++ العديد من الحاويات التي تسمح بربط الكائنات الفريدة بمفتاح. ويساعد استخدام الحاويات على تقليل استهلاك الذاكرة بشكل أكبر من خلال الاستغناء عن إنشاء كائنات مؤقتة.

استيراد std ؛template < typename K , typename V > using TreeMap = std :: map < K , V > ; using String = std :: string ; using StringView = std :: string_view ; template < typename K , typename V > using HashMap = std :: unordered_map < K , V > ;// ستكون مثيلات Tenant هي فئة Flyweights Tenant { خاص : ثابت String name ؛ عام : صريح Tenant ( StringView name ) : name { name } {}[[ nodiscard ]] String getName () const noexcept { return name ; } };// يعمل السجل كمصنع وذاكرة تخزين مؤقتة لكائنات Tenant خفيفة الوزن class Registry { private : HashMap < String , Tenant > tenants ; public : Registry () = default ;[[ nodiscard ] ] Tenant & findByName ( StringView name ) { if ( ! tenants.contains ( name )) { tenants [ name ] = Tenant { name } ; } return tenants [ name ]; } };// يربط كل شقة برقم غرفتها. class Apartment { private : TreeMap < int , Tenant *> occupants ; Registry registry ; public : Apartment () = default ;void addOccupant ( StringView name , int room ) { occupants [ room ] = & registry . findByName ( name ); }void printTenants () { // room: int, tenant: Tenant for ( const auto & [ room , tenant ] : occupants ) { std :: println ( " {} يشغل الغرفة {}" , tenant.name (), room ); } } };int main ( int argc , char * argv []) { شقة سكنية ; شقة . addOccupant ( "ديفيد" , 1 ); شقة . addOccupant ( "سارة" , 3 ); شقة . addOccupant ( "جورج" , 2 ); شقة . addOccupant ( "سارة" , 12 ); شقة . addOccupant ( "مايكل" ​​, 10 ); شقة . printTenants ();return 0 ; }

PHP

<?phpفئة نكهة القهوة {private static array $CACHE = [];دالة خاصة __construct ( سلسلة خاصة $name ) {}public static function intern ( string $name ) : self { self :: $CACHE [ $name ] ?? = new self ( $name ); return self :: $CACHE [ $name ]; }public static function flavorsInCache () : int { return count ( self :: $CACHE ); }public function __toString () : string { return $this -> name ; }}فئة الطلب {دالة خاصة __construct ( خاصة CoffeeFlavour $flavour ، خاصة int $tableNumber ) {}public static function create ( string $flavourName , int $tableNumber ) : self { $flavour = CoffeeFlavour :: intern ( $flavourName ); return new self ( $flavour , $tableNumber ); }public function __toString () : string { return "تقديم { $this -> flavor } إلى الطاولة { $this -> tableNumber } " ; } }فئة المقهى {مصفوفة خاصة $orders = [];public function takeOrder ( string $flavour , int $tableNumber ) { $this -> orders [] = Order :: create ( $flavour , $tableNumber ); }public function service () { print ( implode ( PHP_EOL , $this -> orders ) . PHP_EOL ); } }$shop = new CoffeeShop (); $shop- > takeOrder ( "كابتشينو" , 2 ); $shop- > takeOrder ( "فرابيه" , 1 ); $shop- > takeOrder ( "إسبريسو" , 1 ); $shop- > takeOrder ( "فرابيه" , 897 ); $shop- > takeOrder ( "كابتشينو" , 97 ); $shop- > takeOrder ( "فرابيه" , 3 ); $shop- > takeOrder ( "إسبريسو" , 3 ); $shop- > takeOrder ( "كابتشينو" , 3 ); $shop- > takeOrder ( "إسبريسو" , 96 ); $shop- > takeOrder ( "فرابيه" , 552 ); $shop- > takeOrder ( "كابتشينو" , 121 ); $shop- > takeOrder ( "Espresso" , 121 ); $shop- > service (); print ( "CoffeeFlavour objects in cache: " . CoffeeFlavour :: flavorsInCache () . PHP_EOL );

انظر أيضاً

مراجع

  1. إريك غاما، ريتشارد هيلم، رالف جونسون، جون فليسيدس (1994). أنماط التصميم: عناصر البرمجيات القابلة لإعادة الاستخدام والموجهة للكائنات . أديسون ويسلي. ص 195 وما بعدها . ISBN  978-0-201-63361-0.{{cite book}}: صيانة CS1: أسماء متعددة: قائمة المؤلفين ( رابط )
  2. غاما، إريك ؛ ريتشارد هيلم ؛ رالف جونسون ؛ جون فليسيدس (1995). أنماط التصميم: عناصر البرمجيات القابلة لإعادة الاستخدام والموجهة للكائنات . أديسون-ويسلي . الصفحات 205-206 . ISBN  978-0-201-63361-0.
  3. كالدير، بول ر.؛ لينتون، مارك أ. (أكتوبر 1990). "الرموز الرسومية: كائنات خفيفة الوزن لواجهات المستخدم". وقائع الندوة السنوية الثالثة لجمعية ACM SIGGRAPH حول برمجيات وتكنولوجيا واجهات المستخدم - UIST '90 . الندوة السنوية الثالثة لجمعية ACM SIGGRAPH حول برمجيات وتكنولوجيا واجهات المستخدم. سنوبيرد، يوتا، الولايات المتحدة. الصفحات 92-101 . doi : 10.1145/97924.97935 . ISBN  0-89791-410-4.
  4. ^ ويناند، أندريه؛ جاما، إريك. مارتي ، رودولف (1988). ET++ — إطار تطبيق موجه للكائنات في لغة C++ . OOPSLA (أنظمة البرمجة الشيئية واللغات والتطبيقات). سان دييغو، كاليفورنيا، الولايات المتحدة. ص 46 – 57. CiteSeerX 10.1.1.471.8796 . دوى : 10.1145/62083.62089 . رقم ISBN   0-89791-284-5.
  5. "تطبيق أنماط Flyweight في جافا" . Developer.com . 2019-01-28 . تم الاطلاع عليه بتاريخ 2021-06-12 .
  6. "نمط تصميم Flyweight - الهيكل والتعاون" . w3sDesign.com . تم الاطلاع عليه بتاريخ 12 أغسطس 2017 .