المحتوى:
- أنماط التصميم الهيكلية”Structural design patterns”
- ما هو نمط التصميم الهيكلي” Flyweight design pattern”
- مشكلة يمكن حلها باستخدام نمط التصميم” Flyweight design pattern”
- حل المشكلة باستخدام نمط التصميم” Flyweight design pattern”
- بناء نمط التصميم الهيكلي ” Flyweight design pattern”
- تمثيل نمط التصميم” Flyweight design pattern”بشكل كود
- قابلية نمط التصميم” Flyweight design pattern”للتطبيق
- كيفية تنفيذ نمط التصميم” Flyweight design pattern”
- إيجابيات وسلبيات نمط التصميم” Flyweight design pattern”
- علاقات نمط التصميم” Flyweight design pattern”مع الأنماط الأخرى
- الخاتمة
- المراجع
- أنماط التصميم الهيكلية”Structural design patterns”
أنماط التصميم الهيكلية (Structural Design Patterns) هي أنماط تصميم برمجية تهدف إلى تبسيط تصميم البرمجيات عبر تحديد الطرق التي يمكن بها تجميع الكائنات (Objects) والعلاقات بينها. تهدف هذه الأنماط إلى تعزيز الكفاءة في استخدام الموارد، وزيادة مرونة الأنظمة، وجعلها أكثر قابلية للصيانة والتطوير. هذه الأنماط الهيكلية توفر حلولاً قابلة للتكرار لمشاكل شائعة في تصميم البرمجيات، وتساعد في تحسين بنية الأنظمة البرمجية وجعلها أكثر مرونة وقابلية للصيانة.
- ما هو نمط التصميم الهيكلي”Flyweight design pattern”
هو نمط تصميم هيكلي يتيح لنا احتواء المزيد من الكائنات في المقدار المتاح من ذاكرة الوصول العشوائي (RAM) من خلال مشاركة الأجزاء المشتركة من الحالة بين كائنات متعددة بدلاً من الاحتفاظ بجميع البيانات في كل كائن.
- مشكلة يمكن حلها باستخدام نمط التصميم”Flyweight design pattern”
للحصول على بعض المتعة بعد ساعات العمل الطويلة، قررنا إنشاء لعبة فيديو بسيطة: سيتحرك اللاعبون حول الخريطة ويطلقون النار على بعضهم البعض. وتم اختيار تطبيق نظام جسيمات واقعي وجعله سمة مميزة للعبة. يجب أن تتطاير كميات كبيرة من الرصاص والصواريخ وشظايا الانفجارات في جميع أنحاء الخريطة وتقدم تجربة مثيرة للاعب.
عند اكتمالها، قمنا بتنفيذ الالتزام الأخير، وقمت ببناء اللعبة وأرسلناها إلى الأصدقاء لاختبار القيادة. على الرغم من أنَّ اللعبة كانت تعمل بشكل لا تشوبه شائبة على أجهزتنا، إلَّا أنَّ بعض الأصدقاء لم يتمكنوا من اللعب لفترة طويلة. على جهاز الكمبيوتر الخاص بهم، استمرت اللعبة في التعطل بعد بضع دقائق من اللعب. بعد قضاء عدة ساعات في البحث في سجلات التصحيح، تم اكتشاف أنَّ اللعبة تعطلت بسبب عدم كفاية كمية ذاكرة الوصول العشوائي (RAM). اتضح أنَّ جهاز بعض الأصدقاء كان أقل قوة بكثير من جهاز الكمبيوتر الخاص بنا، ولهذا السبب ظهرت المشكلة بسرعة كبيرة على جهازهم.
كانت المشكلة الفعلية مرتبطة بنظام الجسيمات الخاص بنا حيث. تم تمثيل كل جسيم، مثل الرصاصة أو الصاروخ أو قطعة الشظية، بجسم منفصل يحتوي على الكثير من البيانات. في مرحلة ما، عندما وصلت المذبحة على شاشة اللاعب إلى ذروتها، لم تعد الجزيئات المنشأة حديثاً تتناسب مع ذاكرة الوصول العشوائي المتبقية، لذلك تعطل البرنامج.
- حل المشكلة باستخدام نمط التصميم”Flyweight design pattern”
عند الفحص الدقيق لفئة الجسيمات، قد نلاحظ أنَّ حقول اللون والرموز المتحركة تستهلك ذاكرة أكبر بكثير من الحقول الأخرى. والأسوأ من ذلك هو أنَّ هذين الحقلين يخزنان بيانات متطابقة تقريباً عبر جميع الجسيمات. على سبيل المثال، جميع الرموز النقطية لها نفس اللون والشكل.
الأجزاء الأخرى من حالة الجسيم، مثل الإحداثيات ومتجه الحركة والسرعة، تكون فريدة لكل جسيم. بعد كل شيء، تتغير قيم هذه الحقول مع مرور الوقت. تمثل هذه البيانات السياق المتغير دائمًا الذي يوجد فيه الجسيم، بينما يظل اللون والكائن ثابتاً لكل جسيم.
عادةً ما تسمى هذه البيانات الثابتة للكائن بالحالة الجوهرية. إنَّه يعيش داخل الكائن؛ يمكن للكائنات الأخرى قراءتها فقط، وليس تغييرها. بقية حالة الجسم، والتي غالباً ما يتم تغييرها “من الخارج” بواسطة كائنات أخرى، تسمى الحالة الخارجية.
يقترح هذا النمط أن يتم التوقف عن تخزين الحالة الخارجية داخل الكائن. بدلاً من ذلك، يجب علينا تمرير هذه الحالة إلى أساليب محددة تعتمد عليها. تبقى الحالة الجوهرية فقط داخل الكائن، مما يتيح لنا إعادة استخدامه في سياقات مختلفة. ونتيجة لذلك، ستحتاج إلى عدد أقل من هذه الكائنات لأنها تختلف فقط في الحالة الجوهرية، والتي تحتوي على اختلافات أقل بكثير من الحالة الخارجية.
دعونا نعود إلى لعبتنا. على افتراض أننا استخرجنا الحالة الخارجية من فئة الجسيمات لدينا، فإنَّ ثلاثة أشياء مختلفة فقط ستكون كافية لتمثيل جميع الجسيمات في اللعبة: رصاصة، وصاروخ، وقطعة من الشظية. كما خمنت على الأرجح الآن، فإنَّ الكائن الذي يخزن الحالة الجوهرية فقط يسمى Flyweight.
- تخزين الحالة الخارجية
إلى أين تنتقل الدولة الخارجية؟ لا يزال يتعين على بعض الفصول تخزينها، أليس كذلك؟ في معظم الحالات، يتم نقله إلى كائن الحاوية، الذي يقوم بتجميع الكائنات قبل تطبيق النمط.
في حالتنا، هذا هو كائن اللعبة الرئيسي الذي يخزن جميع الجسيمات في مجال الجسيمات. لنقل الحالة الخارجية إلى هذه الفئة، تحتاج إلى إنشاء عدة حقول مصفوفة لتخزين الإحداثيات والمتجهات وسرعة كل جسيم على حدة. ولكن هذا ليس كل شيء. أنت بحاجة إلى مصفوفة أخرى لتخزين المراجع الخاصة بوزن ذبابة محدد يمثل جسيماً. يجب أن تكون هذه المصفوفات متزامنة حتى تتمكن من الوصول إلى جميع بيانات الجسيم باستخدام نفس الفهرس.
فالحل الأكثر أناقة هو إنشاء فئة سياق منفصلة من شأنها تخزين الحالة الخارجية مع الإشارة إلى كائن وزن الذبابة. قد يتطلب هذا الأسلوب وجود مصفوفة واحدة فقط في فئة الحاوية.
انتظر لحظة!
ألن نحتاج إلى وجود عدد كبير من هذه الكائنات السياقية كما كان لدينا في البداية؟ من الناحية الفنية، نعم. لكن المشكلة هي أن هذه الأجسام أصغر بكثير من ذي قبل. تم نقل الحقول الأكثر استهلاكاً للذاكرة إلى عدد قليل من الكائنات ذات وزن الذبابة. الآن، يمكن لألف كائن سياقي صغير إعادة استخدام كائن واحد ثقيل الوزن بدلاً من تخزين ألف نسخة من بياناته.
- Flyweight والثبات
نظراً لأنَّه يمكن استخدام نفس كائن وزن الذبابة في سياقات مختلفة، فيجب عليك التأكد من عدم إمكانية تعديل حالته. يجب أن يقوم هذا النمط بتهيئة حالته مرة واحدة فقط، عبر معلمات المُنشئ. لا ينبغي أن يعرض أي أدوات ضبط أو حقول عامة لكائنات أخرى.
- مصنع Flyweight
للوصول بسهولة أكبر إلى أوزان الذبابة المختلفة، يمكنك إنشاء طريقة مصنع تدير مجموعة من كائنات وزن الذبابة الموجودة. تقبل الطريقة الحالة الجوهرية لوزن الذبابة المطلوب من العميل، وتبحث عن كائن وزن الذبابة الموجود المطابق لهذه الحالة، وتعيده إذا تم العثور عليه. إذا لم يكن الأمر كذلك، فإنَّه يقوم بإنشاء وزن ذبابة جديد وإضافته إلى حوض السباحة.
هناك العديد من الخيارات حيث يمكن وضع هذه الطريقة. المكان الأكثر وضوحاً هو حاوية وزن الذبابة. وبدلاً من ذلك، يمكنك إنشاء فئة مصنع جديدة. أو يمكنك جعل طريقة المصنع ثابتة ووضعها داخل فئة وزن الذبابة الفعلية.
- بناء نمط التصميم الهيكلي “Flyweight design pattern”
- نمط Flyweight هو مجرد تحسين. قبل تطبيقه، نتأكد من أنَّ برنامجنا يعاني من مشكلة استهلاك ذاكرة الوصول العشوائي (RAM) المتعلقة بوجود عدد هائل من الكائنات المتشابهة في الذاكرة في نفس الوقت. تأكد من أنَّ هذه المشكلة لا يمكن حلها بأي طريقة أخرى ذات معنى.
- تحتوي فئة Flyweight على جزء من حالة الكائن الأصلية التي يمكن مشاركتها بين كائنات متعددة. يمكن استخدام نفس كائن وزن الذبابة في العديد من السياقات المختلفة. الحالة المخزنة داخل وزن الذبابة تسمى جوهرية. الحالة التي تنتقل إلى أساليب وزن الذبابة تسمى خارجية.
- تحتوي Context class على الحالة الخارجية، الفريدة عبر جميع الكائنات الأصلية. عندما يتم إقران سياق مع أحد كائنات وزن الذبابة، فإنه يمثل الحالة الكاملة للكائن الأصلي.
- عادة، يبقى سلوك الكائن الأصلي في فئة Flyweight في هذه الحالة، من يستدعي طريقة Flyweight يجب عليه أيضاً تمرير البتات المناسبة من الحالة الخارجية إلى معلمات الطريقة. من ناحية أخرى، يمكن نقل السلوك إلى فئة السياق، والتي قد تستخدم وزن الذبابة المرتبط فقط ككائن بيانات.
- يقوم العميل بحساب أو تخزين الحالة الخارجية للFlyweight من وجهة نظر العميل، Flyweight هو كائن قالب يمكن تهيئته في وقت التشغيل عن طريق تمرير بعض البيانات السياقية إلى معلمات أساليبه.
- يدير Flyweight Factory مجموعة من Flyweight الموجودة. في المصنع، لا يقوم العملاء بإنشاء Flyweight مباشرة. وبدلاً من ذلك، يسمون المصنع، ويمررون إليه أجزاء من الحالة الجوهرية للFlyweight المرغوب فيه. يبحث المصنع في Flyweight التي تم إنشاؤها مسبقاً ويقوم إما بإرجاع أوزان موجودة تتوافق مع معايير البحث أو يقوم بإنشاء أوزان جديدة إذا لم يتم العثور على أي شيء.
- تمثيل نمط التصميم”Flyweight design pattern”بشكل كود
في هذا المثال، يساعد نمط Flyweight على تقليل استخدام الذاكرة عند عرض ملايين الكائنات الشجرية على لوحة الرسم.
حيث يستخرج النمط الحالة الجوهرية المتكررة من فئة الشجرة الرئيسية وينقلها إلى فئة TreeType.
الآن بدلاً من تخزين نفس البيانات في كائنات متعددة، يتم الاحتفاظ بها في عدد قليل من كائنات وزن الذبابة وربطها بكائنات الشجرة المناسبة التي تعمل كسياقات. يقوم كود العميل بإنشاء كائنات شجرة جديدة باستخدام مصنع وزن الذبابة، والذي يتضمن تعقيد البحث عن الكائن الصحيح وإعادة استخدامه إذا لزم الأمر.
- قابلية نمط التصميم”Flyweight design pattern”للتطبيق
- استخدم هذت النمط فقط عندما يتعين على برنامجنا أن يدعم عدداً كبيراً من الكائنات التي بالكاد تتناسب مع ذاكرة الوصول العشوائي المتوفرة.
حيث تعتمد فائدة تطبيق النمط بشكل كبير على كيفية ومكان استخدامه. يكون مفيداً للغاية عندما:
- يحتاج التطبيق إلى إنتاج عدد كبير من الكائنات المتشابهة.
- يؤدي هذا إلى استنزاف كل ذاكرة الوصول العشوائي المتوفرة على الجهاز المستهدف.
- تحتوي الكائنات على حالات مكررة يمكن استخراجها ومشاركتها بين كائنات متعددة.
- كيفية تنفيذ نمط التصميم”Flyweight design pattern”
- نقوم بتقسيم حقول الفئة التي ستصبح Flyweightإلى قسمين:
الحالة الجوهرية:الحقول التي تحتوي على بيانات غير متغيرة مكررة عبر العديد من الكائنات.
الحالة الخارجية: الحقول التي تحتوي على بيانات سياقية فريدة لكل كائن.
- نترك الحقول التي تمثل الحالة الجوهرية في الفصل الدراسي، ولكن نتأكد من أنها غير قابلة للتغيير. يجب أن يأخذوا قيمهم الأولية فقط داخل المُنشئ.
- نراجع الأساليب التي تستخدم حقول الحالة الخارجية. لكل حقل مستخدم في الطريقة، حيث ندخل معلمة جديدة ونستخدمها بدلاً من الحقل.
- اختيارياً، نقوم بإنشاء فئة مصنع لإدارة مجموعة Flyweight. يجب أن يتحقق من وجود Flyweight الموجود قبل إنشاء وزن جديد. بمجرد إنشاء المصنع، يجب على العملاء فقط طلب Flyweight من خلاله. يجب أن يصفوا Flyweight المطلوب عن طريق تمرير حالته الجوهرية إلى المصنع.
- يجب على العميل تخزين أو حساب قيم الحالة الخارجية (السياق) حتى يتمكن من استدعاء أساليب كائنات Flyweight. من أجل التيسير، يمكن نقل الحالة الخارجية مع الحقل المرجعي لوزن الذبابة إلى فئة سياق منفصلة.
- إيجابيات وسلبيات نمط التصميم”Flyweight design pattern”
الإيجابيات:
- يمكن توفير الكثير من ذاكرة الوصول العشوائي (RAM)، على افتراض أنَّ برنامجنا يحتوي على الكثير من الكائنات المشابهة.
السلبيات:
- ربما تقوم بتداول ذاكرة الوصول العشوائي عبر دورات وحدة المعالجة المركزية عندما نحتاج بعض بيانات السياق إلى إعادة الحساب في كل مرة يستدعي فيها شخص ما طريقة وزن الذبابة.
- يصبح الرمز أكثر تعقيداً. سيتساءل أعضاء الفريق الجدد دائماً عن سبب فصل حالة الكيان بهذه الطريقة.
- علاقات نمط التصميم”Flyweight design pattern” مع الأنماط الأخرى
- يمكنك تنفيذ العقد الورقية المشتركة لل Composite tree مثل Flyweights لحفظ بعض ذاكرة الوصول العشوائي.
- يُظهر Flyweight كيفية إنشاء الكثير من الكائنات الصغيرة، بينما يُظهر Facadeكيفية إنشاء كائن واحد يمثل نظاماً فرعياً بأكمله.
- الخاتمة
نمط التصميم Flyweight هو واحد من أنماط التصميم الهيكلية في البرمجة الكائنية التوجه، ويهدف إلى تقليل استهلاك الذاكرة وتحسين الأداء من خلال إعادة استخدام الكائنات المتشابهة بدلاً من إنشاء كائنات جديدة لكل عملية. يتم ذلك عن طريق تخزين الكائنات المشتركة في مكان مركزي يمكن الوصول إليه وإعادة استخدامه عند الحاجة. يستخدم هذا النمط بشكل خاص عندما يكون هناك عدد كبير من الكائنات الصغيرة التي تستهلك مساحة كبيرة من الذاكرة بشكل جماعي. من خلال تطبيق نمط Flyweight، يمكن تحسين الكفاءة وتقليل الحاجة إلى الموارد، مما يجعله حلاً مناسباً للتطبيقات التي تتطلب إدارة فعالة للذاكرة والأداء العالي.
- المراجع