
ما هذا ولماذا؟
عند التصميم ، قد يواجه المطور مشكلة: قد يكون لدى الكائنات والكائنات قدرات مختلفة في مجموعات مختلفة. الضفادع تقفز وتسبح ، البط يسبح ويطير ، لكن ليس بوزن ، ويمكن للضفادع أن تطير بفرع وبطة. لذلك ، من المريح التبديل من الميراث إلى التكوين وإضافة قدرات ديناميكية. أدت الحاجة إلى تحريك الضفادع الطائرة إلى رفض غير مبرر لأساليب القدرة وإزالة الكود الخاص بهم في فرق في أحد التطبيقات. ومن هنا:
class CastSpellCommand extends Command { constructor (source, target, spell) { this.source = source; this.target = target; this.spell = spell; } execute () { const spellAbility = this.source.getAbility(SpellCastAbility);
ما الذي يمكن عمله؟
فكر في عدة طرق ذات طبيعة مختلفة:
مراقب
class Executor extends Observer {} class Animator extends Observer {}
حل كلاسيكي معروف جيدا للمبرمجين. ما عليك سوى تغييره إلى الحد الأدنى للتحقق من القيم التي يتم إرجاعها بواسطة المراقبين:
this.listeners.reduce((result, listener) => result && listener(action), true)
العيب: يجب على المراقبين الاشتراك في الأحداث بالترتيب الصحيح.
إذا قمت بمعالجة الأخطاء ، فسيتمكن رسام الرسوم المتحركة أيضًا من عرض الرسوم المتحركة الخاصة بالإجراءات الفاشلة. يمكنك نقل القيمة السابقة للمراقبين ؛ من الناحية النظرية ، يبقى الحل كما هو. سواء تم استدعاء أساليب المراقبة أو وظائف رد الاتصال ، سواء تم استخدام حلقة عادية بدلاً من الإلتفاف ، فإن التفاصيل ليست مهمة جدًا.
اترك كما هو
وبالفعل النهج الحالي له عيوب ومزايا:
- يتطلب اختبار القدرة على تشغيل أمر ما تنفيذ أمر ما
- تكون الوسيطات في ترتيب متغيّر ، وشروط ، وبادئات الأسلوب صلبة
- تبعيات الحلقة (command <spell <command)
- كيانات إضافية لكل إجراء (يتم استبدال الطريقة بالطريقة والفئة ومنشئها)
- المعرفة والإجراءات المفرطة لفريق فردي: من ميكانيكا اللعبة إلى أخطاء المزامنة والتلاعب المباشر بخصائص الآخرين
- الواجهة مضللة (لا تنفذ المكالمات فحسب ، بل تضيف أيضًا الأوامر عبر addChildren ؛ ومن الواضح أنها تفعل العكس)
- الحاجة المشكوك فيها وتنفيذ التعليمات العودية في حد ذاتها
- فئة المرسل ، إن وجدت ، لا تؤدي وظائفها
- [+] يزعم أن الطريقة الوحيدة لتحريك في الممارسة العملية ، إذا كانت الرسوم المتحركة تحتاج إلى بيانات كاملة (يشار إليها على أنها السبب الرئيسي)
- [+] ربما أسباب أخرى
يمكن معالجة بعض أوجه القصور بشكل منفصل ، ولكن الباقي يتطلب تغييرات أكثر جذرية.
مخصص
- يجب إخراج شروط تنفيذ الفريق ، وخاصة ميكانيكا اللعبة ، من الفريق وتنفيذها بشكل منفصل. يمكن أن تتغير الظروف في وقت التشغيل ، ويحدث تسليط الضوء على الأزرار غير النشطة باللون الرمادي في الممارسة قبل وقت طويل من بدء العمل على الرسوم المتحركة ، ناهيك عن المنطق. لتجنب النسخ ، قد يكون من المنطقي تخزين الشروط العامة في النماذج الأولية للقدرة.
- طرق الإرجاع ، بالاقتران مع الفقرة السابقة ، ستختفي الحاجة إلى عمليات الفحص هذه:
const spellAbility = this.source.getAbility(SpellCastAbility);
سيعرض مشغل javascript نفسه TypeError الصحيح عندما يتم استدعاء الطريقة عن طريق الخطأ. - الفريق أيضًا لا يحتاج إلى مثل هذه المعرفة:
healthAbility.health = Math.max( 0, resultHealth );
- لحل مشكلة الوسائط التي تغير الأماكن ، يمكن تمريرها بواسطة الكائن.
- على الرغم من أن رمز الاتصال غير متوفر للدراسة ، يبدو أن معظم أوجه القصور تنمو بسبب الطريقة غير المثلى لاستدعاء إجراءات اللعبة. على سبيل المثال ، تصل معالجات الأزرار إلى كيانات محددة. لذلك ، يبدو استبدالهم في معالجات بأوامر محددة أمرًا طبيعيًا تمامًا. إذا كان لديك مرسل ، فمن الأسهل بكثير استدعاء رسم متحرك بعد الإجراء ، فيمكنك نقل نفس المعلومات إليه ، لذلك لن يكون هناك نقص في البيانات.
قائمة الانتظار
لإظهار الرسوم المتحركة للإجراء بعد اكتمال الإجراء ، يكفي إضافتها إلى قائمة الانتظار وتشغيلها تقريبًا كما في الحل 1.
[ [ walkRequirements, walkAction, walkAnimation ], [ castRequirements, castAction, castAnimation ],
لا يهم الكيانات الموجودة في الصفيف: الدالات المحظورة بالمعلمات الضرورية أو مثيلات الفئات المخصصة أو الكائنات العادية.
قيمة هذا الحل هي البساطة والشفافية ؛ من السهل إنشاء نافذة منزلقة لعرض أوامر N الأخيرة.
مناسبة تماما لنماذج وتصحيح الأخطاء.
الطبقة المضبوطة
نصنع فئة الرسوم المتحركة للقدرة.
class MovementAbility { walk (...args) {
إذا كان من المستحيل إجراء تغييرات على فئة الاتصال ، فنحن نرثها أو نقوم بتزيين الطريقة المطلوبة حتى تستدعي الرسوم المتحركة. أو ننقل الرسوم المتحركة بدلاً من القدرة ، لديهم نفس الواجهة.
مناسب تمامًا عندما تحتاج إلى نفس مجموعة الطرق تقريبًا ، يمكن فحصها واختبارها تلقائيًا.
مجموعات الطريقة
const AnimatedMovementAbility = combinedClass(MovementAbility, { ['*:before'] (method, ...args) {
ستكون فرصة مثيرة للاهتمام بدعم اللغة الأم.
من الجيد استخدامه إذا كان هذا الخيار أكثر إنتاجية ، على الرغم من الحاجة إلى وكيل بالفعل.
الوكلاء
نحن التفاف القدرات في وكلاء ، وأساليب الصيد في getters.
new Proxy(new MovementAbility, {})
العيب: عدة مرات أبطأ من المكالمات العادية ، وهي ليست مهمة جدا للرسوم المتحركة. على الخادم الذي يعالج ملايين الكائنات ، سيكون التباطؤ ملحوظًا ، لكن الخادم لا يحتاج إلى رسوم متحركة.
وعد
يمكنك إنشاء سلاسل من Promise ، ولكن هناك خيار آخر (ES2018):
for await (const action of actionDispatcher.getActions()) {
تقوم getActions بإرجاع مكرر إجراء غير متزامن. الطريقة التالية من التكرار بإرجاع الوعد المؤجل للإجراء التالي. بعد معالجة الأحداث من المستخدم والخادم ، نسمي العزم () ، وإنشاء وعد جديد.
فريق أفضل
إنشاء كائنات مثل هذا:
{actor, ability, method, options}
يأتي الرمز للتحقق من طريقة القدرة واستدعاءها باستخدام المعلمات. الخيار الأسهل والأكثر إنتاجية.
ملاحظة