مقدمة إلى النموذج الأولي للأنماط والإمبر(ي)

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

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

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

فهم النموذج الأولي في ديبث

ويستند النموذج الأولي إلى مفهوم الإرث البرمجي ، الذي هو من أصل جافاسكريت نفسه، وكل جسم من الـ جافاسكريبت له سلسلة داخلية تسمح بوفد الممتلكات، غير أن نمط التصميم يتجاوز ميكانيكي اللغة: فهو لا يستحدث أبداً صيغة مكرَّسة [للأداة:]

المشاركون الرئيسيون في النمط:

  • النموذج الأولي ] - الواجهة (أو الطبقة المجردة) التي تعلن ] العملية.
  • ConcretePrototype] - The actual object that implements cloning, typically by copying its own properties.
  • Client] - The code that asks the prototype to clone itself.

وكثيرا ما يتطلب الاستنساخ، بلغات مثل جافا أو سي ++، تنفيذ ] والتعامل الدقيق مع النسخ الضحلة العميقة في جافاسكريبت، لأن الأشياء قد انفشت بالفعل بصورة دينامية، والاستنساخ أكثر وضوحا، ولكنه يقدم أيضا كتيبات فرعية حول تقاسم المراجع.

وبالنسبة لمطوري شركة Ember.js، فهم هذا النمط ليس مجرد نموذج أكاديمي، إذ إن نموذج الجسم الخاص في إمبر، الذي يستند إلى ، يوفر طريقة ] يمكن استخدامها لكشف الأشياء من جسم أساسي، غير أن ] أقرب إلى مصنع أكثر من مجرد رغاوى من القيم الداخلية.

تطبيق النموذج الأولي على مكونات الإيمبر.ي

المكونات في الإمبراطور هي حالات من الفصول المحددة عن طريق أو أقدم، وفي الإمبراطور الحديث (أوكتان وما بعده)، تدعم العناصر من خلال صفوف سكان جافاسكريبت، وكل حالة لها (الجرعات، الممتلكات المتبوعة) ويمكن تنفيذ النموذج الأولي على عدة مستويات: يمكن تعديل عنصر مكون (تجريده)

تصنيفات عناصر الإستنساخ عن طريق النموذج الأولي

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

ويتمثل أحد النهجين في استخدام الميراث من فئة إمبير: ولكن الميراث ينشئ علاقة ثابتة بين الوالدين والطفل، ويوفر النموذج الأولي نهجا أكثر دينامية: يمكنك تخزين نموذج " نموذجي " (أو جسم عادي له جميع الممتلكات غير الافتراضية) وإستنساخه لإنتاج حالات جديدة ذات ظروف مصممة خصيصا.

وهنا مثال مبسط يستخدم خدمة تعمل كسجل نموذجي:


// app/services/component-prototypes.js
import Service from '@ember/service';
import { tracked } from '@glimmer/tracking';

export default class ComponentPrototypesService extends Service {
 @tracked prototypes = new Map();

 registerPrototype(name, prototypeObject) {
 this.prototypes.set(name, prototypeObject);
 }

 clonePrototype(name, overrides = {}) {
 const prototype = this.prototypes.get(name);
 if (!prototype) {
 throw new Error(`Prototype '${name}' not found`);
 }
 // Create a shallow copy of the prototype properties
 const clone = Object.assign({}, prototype, overrides);
 return clone;
 }
}

ثم، في تطبيقك، تُسجلُ نموذجاً لزر القاعدةِ:


// app/initializers/register-prototypes.js
export function initialize(application) {
 const service = application.lookup('service:component-prototypes');
 service.registerPrototype('button', {
 componentName: 'base-button',
 args: {
 text: 'Submit',
 type: 'button',
 action: 'defaultAction',
 theme: 'primary',
 },
 });
}

عندما تحتاج إلى زر حذف:


const deleteButtonConfig = service.clonePrototype('button', {
 args: {
 text: 'Delete',
 theme: 'danger',
 action: 'deleteRecord',
 },
});
// Then render using 

هذا النهج يفصل التشكيلة من نموذج المكوّن ويسمح لك بخلق العديد من المتغيرات مع الحد الأدنى من الشفرة

(الشقوق)

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

ومن النمط العملي استخدام " طلقات " من حجج العنصر والحالة الداخلية، ثم بناء حالة جديدة مع تلك الطلقات، ويمكن استخدام مساعد (]) في جعل المكونات من الجسم التكويني ديناميا، مثلا باستخدام جهاز المساعدة :


// In a template
{{#each this.clonedConfigs as |config|}}
 <component @name="base-button" @config={{config}} />
{{/each}}

ومن شأن منطق جافاسكريبت أن يُسجِّل حجج المكوِّن الأصلي باستخدام وأي دولة داخلية مُتعقَبة عبر ثم تُنشئ جسماً جديداً من الشعارات وتدفعه إلى صفيفة ، وهذا ليس نسخة حقيقية من القضية (سيتمثل العنصر الجديد دورة حياة جديدة)، ولكنه يحقق نفس التأثير:

نموذج عملي: بناء أسرة بوتون ميد

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

بدون النموذج الأولي، يمكنك كتابة سبعة ملفات منفصلة لكل منها مع نماذج شبه محددة، مع نمط معين، تحدد مكوناً واحداً ثم تستخدم جسماً تشكيلياً مستنسخاً ومصمماً.

الخطوة 1: تحديد عنصر القاعدي

ويقبل هذا العنصر حجة تتضمن جميع الأجزاء المتغيرة.


// app/components/base-button.js
import Component from '@glimmer/component';
import { action } from '@ember/object';

export default class BaseButton extends Component {
 get text() {
 return this.args.config.text || 'Button';
 }
 get theme() {
 return this.args.config.theme || 'primary';
 }
 get className() {
 return `btn btn-${this.theme}`;
 }
 @action
 handleClick() {
 if (this.args.config.action) {
 this.args.config.action.call(this);
 }
 }
}

{{! app/components/base-button.hbs }}
<button type="button" class={{this.className}} {{on "click" this.handleClick}} disabled={{@config.disabled}}>
 {{this.text}}
 {{#if @config.icon}}
 <span class="icon">{{@config.icon}}</span>
 {{/if}}
</button>

الخطوة 2: إنشاء دائرة لصناعة البستون مع نظام للتشهير

وبدلاً من أن يكون هناك هدف بسيط، يمكننا استخدام طبقة تشمل أساليب التكييف المشتركة.


// app/services/button-prototype.js
import Service from '@ember/service';

export default class ButtonPrototypeService extends Service {
 constructor() {
 super(...arguments);
 this._registry = new Map();
 this._registerDefaults();
 }

 _registerDefaults() {
 const base = {
 text: 'Submit',
 theme: 'primary',
 disabled: false,
 icon: null,
 action: null,
 confirmation: null,
 };
 this._registry.set('button', { ...base });
 }

 registerVariant(name, overrides) {
 const base = this._registry.get('button');
 if (!base) throw new Error('Base button prototype not found');
 const variant = { ...base, ...overrides };
 this._registry.set(`button:${name}`, variant);
 }

 clone(name, customOverrides = {}) {
 const prototype = this._registry.get(name);
 if (!prototype) throw new Error(`Prototype '${name}' not found`);
 return { ...prototype, ...customOverrides };
 }
}

الخطوة 3: المتغيرات في السجل

في مبيد أو طريق:


this.buttonPrototype.registerVariant('danger', {
 text: 'Delete',
 theme: 'danger',
 confirmation: 'Are you sure?',
 action: () => alert('Deleted!'),
});
this.buttonPrototype.registerVariant('success', {
 text: 'Save',
 theme: 'success',
 icon: 'check',
});

الخطوة 4: الاستخدام في نموذج


<BaseButton @config={{this.buttonPrototype.clone 'button:danger'}} />
<BaseButton @config={{this.buttonPrototype.clone 'button' (hash text='Create New' theme='primary')}} />

وهذا النمط يقلل من الازدواجية ويجعل من المتقلب إضافة متغيرات زر جديدة - مجرد تسجيل نموذج جديد مع تجاوزات.

"اللون العميق ضد "اللون الضحل في "إمبر

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

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

ولإستنساخها في أعماقها في جافا سكيربت، يمكن استخدام (مدعمة في المناظير الحديثة و Node.js 17+) أو مكتبة مثل لوداش .] كما أن مساعد ] [ممول من يوفر أيضاً وظائف نسخ عميقة.


import { copy } from '@ember/object/internals';
const deepClone = copy(prototype, true); // true for deep

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

مقارنة النموذج الأولي مع أنماط أخرى في إيمبر

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

النموذج الأولي ضد الالتزام بالفئة

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

النموذج الأولي ضد مصنع

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

النموذج الأولي ضد رمز باتر

"الدبّة المُقرّرة تُضيف سلوكاً إلى جسمٍ دون تغيير هيكله، إنّ "النموذج الأولي يُنشئ نسخة ثمّ يُعدّلها، ويمكن الجمع بينهما: يمكنكِ إستنساخ نموذج أولي ثمّ تزيينه بسلوك إضافيّ عبر خليط أو مكونات أعلى.

Advantages of Using the Prototype Pattern in Ember.js

  • Reduced Code Duplication:] Define the default behavior and appearance once, then clone and tweak. No need to repeat templates or JavaScript logical across variants.
  • Consistency:] Cloning ensures that all derived components start from the same baseline, eliminating accidental divergences.
  • Runtime Flexibility:] You can load new prototypes from an API or user preferences and immediately use them to render components-no rebuild required.
  • Easier Unit Testing:] Testing the base component with a known prototype, then testning logical separately.
  • ]Performance:] If cloning is cheap (shallow copies of flat data), it’s often faster than immediateiating from a class hierarchy, especially when many variants are created.

"الصور المحتملة" "وعندما تتجنب "الخطوبة"

  • Deep copy Overhead:] If your prototypes contain large nested objects, deep cloning can be expensive. Consider using immutable data structures or sharing unmodified parts.
  • Shared Mutable State:]
  • Complexity in Dynamic Templates:] If you rely heavily on objects, the component template can become a huge ]. better to keep the template declarative and handle logical in the JavaScript file.
  • Not Suitable for All componentss:] For components that have complex internal state (e.g., a rich text editor with its own undo stack), cloning the config alone is insufficient, you would need to clone the entire internal state, which is often impractical.
  • Overuse Leads to Abstractions: ] If you find yourself creating an prototype for every small variation, you may be over-engineering. sometimes a simple conditional in the template is clearer.

الموارد الخارجية والقراءة الإضافية

وللفهم الأعمق للنموذج الأولي للطب في جافاسكريبت وإمبر، النظر في الموارد التالية:

الاستنتاج: إدماج نمط أولي في تدفق العمل

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

وكما هو الحال بالنسبة لأي نمط من أنماط التصميم، فإن المفتاح هو استخدامه بحكمة، بدءا بمجموعة صغيرة من المكونات التي لها متغيرات واضحة (البوتون والبطاقات والقوائم)، وبما أنكم تكتسبون الثقة، يمكنكم توسيع النمط ليشمل سيناريوهات أكثر تعقيدا، وبجمع نمط النموذج الأولي مع نظام التفاعل الخاص بالإمبر ومعاوني العناصر المكوِّنة، يمكنكم بناء هيكل مائل ومتطور يكيف مع المتطلبات المتغيرة دون انطلاق.

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