الفرق بين المراجعتين لصفحة: «TypeScript/mixins»
سطر 61: | سطر 61: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
==فهم العيّنة== | ==فهم العيّنة== | ||
تبدأ الشيفرة بالصنفين اللذان سيُمثّلان المخلاطين. يمكنك ملاحظة أن كلا منهما يُركّز على قدرة أو نشاط معيّن (Disposable يشير إلى أن الكائن جاهز للاستعمال وقابل للتخلص منه، أمّا Activatable فيسمح بتفعيل الكائن أو تعطيله). سنخلط بعد | |||
تبدأ الشيفرة بالصنفين اللذان سيُمثّلان المخلاطين. يمكنك ملاحظة أن كلا منهما يُركّز على قدرة أو نشاط معيّن (<code>Disposable</code> يشير إلى أن الكائن جاهز للاستعمال وقابل للتخلص منه، أمّا <code>Activatable</code> فيسمح بتفعيل الكائن أو تعطيله). سنخلط بعد قليل هاذين الصنفين معًا لتشكيل صنف جديد من كلا الصفتين (أي أن الصنف النهائي سيكون جاهزًا للاستعمال وقابلًا للتفعيل والتعطيل في نفس الوقت): | |||
<syntaxhighlight lang="typescript"> | <syntaxhighlight lang="typescript"> | ||
// Disposable Mixin | // Disposable Mixin |
مراجعة 16:52، 1 أكتوبر 2018
مقدمة
إضافةً إلى تسلسلات البرمجة كائنية التوجه التقليدية، هناك كذلك طريقة شائعة لبناء أصناف من مكونات قابلة لإعادة الاستعمال، وهي بناؤها عبر دمج أصناف جزئية أبسط. قد تكون فكرة المخاليط (mixins) أو السمات (traits) في لغات مثل Scala مألوفة بالنسبة إليك، وقد أصبح نمط المخاليط مشهورًا في مجتمع JavaScript كذلك.
عينة لمخلاط
يُمكنك التعرف على كيفية إنشاء المخاليط في TypeScript من الشيفرة أدناه. سنشرح مكونات الشيفرة تاليًا:
// Disposable Mixin
class Disposable {
isDisposed: boolean;
dispose() {
this.isDisposed = true;
}
}
// Activatable Mixin
class Activatable {
isActive: boolean;
activate() {
this.isActive = true;
}
deactivate() {
this.isActive = false;
}
}
class SmartObject implements Disposable, Activatable {
constructor() {
setInterval(() => console.log(this.isActive + " : " + this.isDisposed), 500);
}
interact() {
this.activate();
}
// Disposable
isDisposed: boolean = false;
dispose: () => void;
// Activatable
isActive: boolean = false;
activate: () => void;
deactivate: () => void;
}
applyMixins(SmartObject, [Disposable, Activatable]);
let smartObj = new SmartObject();
setTimeout(() => smartObj.interact(), 1000);
////////////////////////////////////////
// ضع هذه الدالة في مكتبة زمن التنفيذ الخاصة بك
////////////////////////////////////////
function applyMixins(derivedCtor: any, baseCtors: any[]) {
baseCtors.forEach(baseCtor => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
derivedCtor.prototype[name] = baseCtor.prototype[name];
});
});
}
فهم العيّنة
تبدأ الشيفرة بالصنفين اللذان سيُمثّلان المخلاطين. يمكنك ملاحظة أن كلا منهما يُركّز على قدرة أو نشاط معيّن (Disposable
يشير إلى أن الكائن جاهز للاستعمال وقابل للتخلص منه، أمّا Activatable
فيسمح بتفعيل الكائن أو تعطيله). سنخلط بعد قليل هاذين الصنفين معًا لتشكيل صنف جديد من كلا الصفتين (أي أن الصنف النهائي سيكون جاهزًا للاستعمال وقابلًا للتفعيل والتعطيل في نفس الوقت):
// Disposable Mixin
class Disposable {
isDisposed: boolean;
dispose() {
this.isDisposed = true;
}
}
// Activatable Mixin
class Activatable {
isActive: boolean;
activate() {
this.isActive = true;
}
deactivate() {
this.isActive = false;
}
}
سنُنشئ بعد ذلك الصنف الذي سيحمل نتيجة دمج المخلاطين. لنلق نظرة تفصيلية على آلية العمل هذه:
class SmartObject implements Disposable, Activatable {
أوّل ما قد تلحظه في السطر أعلاه أنّنا نستخدم الكلمة المفتاحية implements
عوضًا عن extends
. يُعامِل هذا الأصنافَ على أنّها واجهات، وستُستَعمل الأنواع فقط من Disposable
وActivatable
عوضًا عن شيفرة التطبيق (implementation) الموجودة داخل الصنفين. ما يعني أن علينا توفير شيفرة التطبيق في الصنف. لكن، هذا ما نريد تفاديه باستعمال المخاليط، فما العمل؟
لتلبية هذا المتطلّب، ننشئ خاصيات بوصف أنواعها دون تعيين قيمة لها، نقوم بهذا للعناصر التي ستأتي من المخاليط. هذا يلبي متطلب المترجم لأن هذه الخاصيات ستكون متوفرة أثناء التنفيذ. وهذا يسمح لنا بالاستفادة من المخاليط، رغم أن علينا دفع كلفة صغيرة بسبب تكرار الشيفرة.
// Disposable
isDisposed: boolean = false;
dispose: () => void;
// Activatable
isActive: boolean = false;
activate: () => void;
deactivate: () => void;
وفي الأخير نخلط مخاليطنا مع الصنف، وبالتالي سيكون التطبيق كاملًا:
applyMixins(SmartObject, [Disposable, Activatable]);
ننشئ بعد ذلك دالة مساعدةً لإجراء عمليّة الخلط. ستمرّ هذه الدالة على خاصيات كل مخلاط وستنسخها إلى هدف المخاليط مع إعطاء القيم للخاصيات التي عُرّفت دون قيم.
function applyMixins(derivedCtor: any, baseCtors: any[]) {
baseCtors.forEach(baseCtor => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
derivedCtor.prototype[name] = baseCtor.prototype[name];
});
});
}