استكشف حقول الكلاس الخاصة في جافاسكريبت، وتأثيرها على التغليف، وعلاقتها بأنماط التحكم في الوصول التقليدية لتصميم برمجيات متين.
حقول الكلاس الخاصة في جافاسكريبت: التغليف مقابل أنماط التحكم في الوصول
في عالم جافاسكريبت دائم التطور، يمثل إدخال حقول الكلاس الخاصة تقدمًا كبيرًا في كيفية هيكلة وإدارة الكود الخاص بنا. قبل اعتمادها على نطاق واسع، كان تحقيق التغليف الحقيقي في كلاسات جافاسكريبت يعتمد على أنماط، على الرغم من فعاليتها، إلا أنها قد تكون مطولة أو أقل بديهية. يتعمق هذا المقال في مفهوم حقول الكلاس الخاصة، ويحلل علاقتها بالتغليف، ويقارنها بأنماط التحكم في الوصول الراسخة التي استخدمها المطورون لسنوات. هدفنا هو تقديم فهم شامل لجمهور عالمي من المطورين، وتعزيز أفضل الممارسات في تطوير جافاسكريبت الحديث.
فهم التغليف في البرمجة كائنية التوجه
قبل أن نتعمق في تفاصيل الحقول الخاصة في جافاسكريبت، من الضروري تأسيس فهم أساسي للتغليف. في البرمجة كائنية التوجه (OOP)، يعد التغليف أحد المبادئ الأساسية، إلى جانب التجريد والوراثة وتعدد الأشكال. يشير إلى تجميع البيانات (السمات أو الخصائص) والأساليب التي تعمل على تلك البيانات داخل وحدة واحدة، غالبًا ما تكون كلاس. الهدف الأساسي للتغليف هو تقييد الوصول المباشر إلى بعض مكونات الكائن، مما يعني أنه لا يمكن الوصول إلى الحالة الداخلية للكائن أو تعديلها من خارج تعريف الكائن.
تشمل الفوائد الرئيسية للتغليف ما يلي:
- إخفاء البيانات: حماية الحالة الداخلية للكائن من التعديلات الخارجية غير المقصودة. هذا يمنع تلف البيانات العرضي ويضمن بقاء الكائن في حالة صالحة.
- الوحداتية (Modularity): تصبح الكلاسات وحدات قائمة بذاتها، مما يسهل فهمها وصيانتها وإعادة استخدامها. التغييرات في التنفيذ الداخلي للكلاس لا تؤثر بالضرورة على أجزاء أخرى من النظام، طالما بقيت الواجهة العامة متسقة.
- المرونة والصيانة: يمكن تغيير تفاصيل التنفيذ الداخلي دون التأثير على الكود الذي يستخدم الكلاس، شريطة أن تظل واجهة برمجة التطبيقات العامة (API) مستقرة. هذا يبسط بشكل كبير إعادة الهيكلة والصيانة على المدى الطويل.
- التحكم في الوصول إلى البيانات: يسمح التغليف للمطورين بتحديد طرق معينة للوصول إلى بيانات الكائن وتعديلها، غالبًا من خلال التوابع العامة (getters and setters). يوفر هذا واجهة محكومة ويسمح بالتحقق من الصحة أو الآثار الجانبية عند الوصول إلى البيانات أو تغييرها.
أنماط التحكم في الوصول التقليدية في جافاسكريبت
جافاسكريبت، كونها لغة ديناميكية النوع وتعتمد على النموذج الأولي تاريخيًا، لم يكن لديها دعم مدمج للكلمات المفتاحية مثل `private` في الكلاسات كما هو الحال في العديد من لغات البرمجة كائنية التوجه الأخرى (مثل Java، C++). اعتمد المطورون على أنماط مختلفة لتحقيق شكل من أشكال إخفاء البيانات والوصول المتحكم فيه. لا تزال هذه الأنماط ذات صلة لفهم تطور جافاسكريبت وفي الحالات التي قد لا تكون فيها حقول الكلاس الخاصة متاحة أو مناسبة.
1. اصطلاحات التسمية (بادئة الشرطة السفلية)
كان الاصطلاح الأكثر شيوعًا وتاريخيًا هو وضع بادئة شرطة سفلية (`_`) قبل أسماء الخصائص التي يُقصد أن تكون خاصة. على سبيل المثال:
class User {
constructor(name, email) {
this._name = name;
this._email = email;
}
get name() {
return this._name;
}
set email(value) {
// Basic validation
if (value.includes('@')) {
this._email = value;
} else {
console.error('Invalid email format.');
}
}
}
const user = new User('Alice', 'alice@example.com');
console.log(user._name); // Accessing 'private' property
user._name = 'Bob'; // Direct modification
console.log(user.name); // Getter still returns 'Alice'
الإيجابيات:
- بسيطة في التنفيذ والفهم.
- معترف بها على نطاق واسع داخل مجتمع جافاسكريبت.
السلبيات:
- ليست خاصة حقًا: هذا مجرد اصطلاح. لا تزال الخصائص قابلة للوصول والتعديل من خارج الكلاس. إنها تعتمد على انضباط المطور.
- لا يوجد فرض: لا يمنع محرك جافاسكريبت الوصول إلى هذه الخصائص.
2. الإغلاقات (Closures) و IIFEs (التعبيرات الوظيفية المستدعاة فورًا)
كانت الإغلاقات، جنبًا إلى جنب مع IIFEs، طريقة قوية لإنشاء حالة خاصة. الدوال التي يتم إنشاؤها داخل دالة خارجية لديها إمكانية الوصول إلى متغيرات الدالة الخارجية، حتى بعد انتهاء تنفيذ الدالة الخارجية. سمح هذا بإخفاء البيانات بشكل حقيقي قبل حقول الكلاس الخاصة.
const User = (function() {
let privateName;
let privateEmail;
function User(name, email) {
privateName = name;
privateEmail = email;
}
User.prototype.getName = function() {
return privateName;
};
User.prototype.setEmail = function(value) {
if (value.includes('@')) {
privateEmail = value;
} else {
console.error('Invalid email format.');
}
};
return User;
})();
const user = new User('Alice', 'alice@example.com');
console.log(user.getName()); // Valid access
// console.log(user.privateName); // undefined - cannot access directly
user.setEmail('bob@example.com');
console.log(user.getName());
الإيجابيات:
- إخفاء حقيقي للبيانات: المتغيرات المعلنة داخل IIFE هي خاصة حقًا ولا يمكن الوصول إليها من الخارج.
- تغليف قوي.
السلبيات:
- الإطالة: يمكن أن يؤدي هذا النمط إلى كود أكثر إطالة، خاصة بالنسبة للكلاسات التي تحتوي على العديد من الخصائص الخاصة.
- التعقيد: قد يكون فهم الإغلاقات و IIFEs عائقًا للمبتدئين.
- الآثار المترتبة على الذاكرة: قد يكون لكل مثيل تم إنشاؤه مجموعة خاصة به من متغيرات الإغلاق، مما قد يؤدي إلى استهلاك ذاكرة أعلى مقارنة بالخصائص المباشرة، على الرغم من أن المحركات الحديثة محسّنة تمامًا.
3. دوال المصنع (Factory Functions)
دوال المصنع هي دوال تُرجع كائنًا. يمكنها الاستفادة من الإغلاقات لإنشاء حالة خاصة، على غرار نمط IIFE، ولكن دون الحاجة إلى دالة بانية والكلمة المفتاحية `new`.
function createUser(name, email) {
let privateName = name;
let privateEmail = email;
return {
getName: function() {
return privateName;
},
setEmail: function(value) {
if (value.includes('@')) {
privateEmail = value;
} else {
console.error('Invalid email format.');
}
},
// Other public methods
};
}
const user = createUser('Alice', 'alice@example.com');
console.log(user.getName());
// console.log(user.privateName); // undefined
الإيجابيات:
- ممتازة لإنشاء كائنات ذات حالة خاصة.
- تتجنب تعقيدات ربط `this`.
السلبيات:
- لا تدعم الوراثة مباشرة بنفس طريقة البرمجة كائنية التوجه القائمة على الكلاسات دون أنماط إضافية (مثل التركيب).
- يمكن أن تكون أقل ألفة للمطورين القادمين من خلفيات البرمجة كائنية التوجه التي تركز على الكلاسات.
4. الخرائط الضعيفة (WeakMaps)
توفر الخرائط الضعيفة (WeakMaps) طريقة لربط البيانات الخاصة بالكائنات دون كشفها بشكل عام. مفاتيح WeakMap هي كائنات، ويمكن أن تكون القيم أي شيء. إذا تم جمع الكائن كقمامة (garbage collected)، تتم إزالة الإدخال المقابل له في WeakMap أيضًا.
const privateData = new WeakMap();
class User {
constructor(name, email) {
privateData.set(this, {
name: name,
email: email
});
}
getName() {
return privateData.get(this).name;
}
setEmail(value) {
if (value.includes('@')) {
privateData.get(this).email = value;
} else {
console.error('Invalid email format.');
}
}
}
const user = new User('Alice', 'alice@example.com');
console.log(user.getName());
// console.log(privateData.get(user).name); // This still accesses the data, but WeakMap itself isn't directly exposed as a public API on the object.
الإيجابيات:
- توفر طريقة لإرفاق بيانات خاصة بالمثيلات دون استخدام الخصائص مباشرة على المثيل.
- المفاتيح هي كائنات، مما يسمح ببيانات خاصة حقًا مرتبطة بمثيلات محددة.
- جمع قمامة تلقائي للإدخالات غير المستخدمة.
السلبيات:
- تتطلب بنية بيانات مساعدة: يجب إدارة `privateData` WeakMap بشكل منفصل.
- يمكن أن تكون أقل بديهية: إنها طريقة غير مباشرة لإدارة الحالة.
- الأداء: على الرغم من كفاءتها بشكل عام، قد يكون هناك عبء إضافي طفيف مقارنة بالوصول المباشر إلى الخصائص.
تقديم حقول الكلاس الخاصة في جافاسكريبت (`#`)
تم تقديم حقول الكلاس الخاصة في ECMAScript 2022 (ES13)، وهي توفر صيغة أصلية مدمجة للإعلان عن أعضاء خاصين داخل كلاسات جافاسكريبت. هذا يغير قواعد اللعبة لتحقيق التغليف الحقيقي بطريقة واضحة وموجزة.
يتم الإعلان عن حقول الكلاس الخاصة باستخدام بادئة الهاش (`#`) متبوعة باسم الحقل. تشير بادئة `#` هذه إلى أن الحقل خاص بالكلاس ولا يمكن الوصول إليه أو تعديله من خارج نطاق الكلاس.
الصيغة والاستخدام
class User {
#name;
#email;
constructor(name, email) {
this.#name = name;
this.#email = email;
}
// Public getter for #name
get name() {
return this.#name;
}
// Public setter for #email
set email(value) {
if (value.includes('@')) {
this.#email = value;
} else {
console.error('Invalid email format.');
}
}
// Public method to display info (demonstrating internal access)
displayInfo() {
console.log(`Name: ${this.#name}, Email: ${this.#email}`);
}
}
const user = new User('Alice', 'alice@example.com');
console.log(user.name); // Accessing via public getter -> 'Alice'
user.email = 'bob@example.com'; // Setting via public setter
user.displayInfo(); // Name: Alice, Email: bob@example.com
// Attempting to access private fields directly (will result in an error)
// console.log(user.#name); // SyntaxError: Private field '#name' must be declared in an enclosing class
// console.log(user.#email); // SyntaxError: Private field '#email' must be declared in an enclosing class
الخصائص الرئيسية لحقول الكلاس الخاصة:
- خاصة تمامًا: لا يمكن الوصول إليها من خارج الكلاس، ولا من الكلاسات الفرعية. أي محاولة للوصول إليها ستؤدي إلى `SyntaxError`.
- حقول خاصة ثابتة: يمكن أيضًا الإعلان عن الحقول الخاصة كـ `static`، مما يعني أنها تنتمي إلى الكلاس نفسه بدلاً من المثيلات.
- توابع خاصة: يمكن أيضًا تطبيق بادئة `#` على التوابع، مما يجعلها خاصة.
- الكشف المبكر عن الأخطاء: تؤدي صرامة الحقول الخاصة إلى إطلاق أخطاء في وقت التحليل أو وقت التشغيل، بدلاً من الفشل الصامت أو السلوك غير المتوقع.
حقول الكلاس الخاصة مقابل أنماط التحكم في الوصول
إن إدخال حقول الكلاس الخاصة يجعل جافاسكريبت أقرب إلى لغات البرمجة كائنية التوجه التقليدية ويوفر طريقة أكثر قوة وتصريحية لتنفيذ التغليف مقارنة بالأنماط القديمة.
قوة التغليف
حقول الكلاس الخاصة: توفر أقوى شكل من أشكال التغليف. يفرض محرك جافاسكريبت الخصوصية، مما يمنع أي وصول خارجي. هذا يضمن أن الحالة الداخلية للكائن لا يمكن تعديلها إلا من خلال واجهته العامة المحددة.
الأنماط التقليدية:
- اصطلاح الشرطة السفلية: أضعف شكل. استشاري بحت، يعتمد على انضباط المطور.
- الإغلاقات / IIFEs / دوال المصنع: توفر تغليفًا قويًا، على غرار الحقول الخاصة، عن طريق إبقاء المتغيرات خارج النطاق العام للكائن. ومع ذلك، فإن الآلية أقل مباشرة من صيغة `#`.
- الخرائط الضعيفة (WeakMaps): توفر تغليفًا جيدًا، ولكنها تتطلب إدارة بنية بيانات خارجية.
القراءة والصيانة
حقول الكلاس الخاصة: صيغة `#` تصريحية وتشير على الفور إلى نية الخصوصية. إنها نظيفة وموجزة وسهلة الفهم للمطورين، خاصة أولئك المطلعين على لغات البرمجة كائنية التوجه الأخرى. هذا يحسن قابلية قراءة الكود وصيانته.
الأنماط التقليدية:
- اصطلاح الشرطة السفلية: قابل للقراءة ولكنه لا ينقل خصوصية حقيقية.
- الإغلاقات / IIFEs / دوال المصنع: يمكن أن تصبح أقل قابلية للقراءة مع نمو التعقيد، وقد يكون تصحيح الأخطاء أكثر صعوبة بسبب تعقيدات النطاق.
- الخرائط الضعيفة (WeakMaps): تتطلب فهم آلية WeakMaps وإدارة البنية المساعدة، مما قد يضيف عبئًا إدراكيًا.
معالجة الأخطاء وتصحيحها
حقول الكلاس الخاصة: تؤدي إلى الكشف المبكر عن الأخطاء. إذا حاولت الوصول إلى حقل خاص بشكل غير صحيح، فستحصل على `SyntaxError` أو `ReferenceError` واضح. هذا يجعل تصحيح الأخطاء أكثر مباشرة.
الأنماط التقليدية:
- اصطلاح الشرطة السفلية: الأخطاء أقل احتمالًا ما لم يكن المنطق معيبًا، حيث أن الوصول المباشر صالح من الناحية النحوية.
- الإغلاقات / IIFEs / دوال المصنع: قد تكون الأخطاء أكثر دقة، مثل قيم `undefined` إذا لم تتم إدارة الإغلاقات بشكل صحيح، أو سلوك غير متوقع بسبب مشكلات النطاق.
- الخرائط الضعيفة (WeakMaps): يمكن أن تحدث أخطاء متعلقة بعمليات `WeakMap` أو الوصول إلى البيانات، ولكن قد يتضمن مسار تصحيح الأخطاء فحص `WeakMap` نفسه.
التوافقية والتشغيل البيني
حقول الكلاس الخاصة: هي ميزة حديثة. على الرغم من دعمها على نطاق واسع في إصدارات المتصفحات الحالية و Node.js، قد تتطلب البيئات القديمة التحويل البرمجي (transpilation) (على سبيل المثال، باستخدام Babel) لتحويلها إلى جافاسكريبت متوافق.
الأنماط التقليدية: تستند إلى ميزات جافاسكريبت الأساسية (الدوال، النطاقات، النماذج الأولية) التي كانت متاحة لفترة طويلة. إنها توفر توافقًا رجعيًا أفضل دون الحاجة إلى التحويل البرمجي، على الرغم من أنها قد تكون أقل اصطلاحية في قواعد الكود الحديثة.
الوراثة
حقول الكلاس الخاصة: لا يمكن الوصول إلى الحقول والتوابع الخاصة من قبل الكلاسات الفرعية. هذا يعني أنه إذا احتاج كلاس فرعي إلى التفاعل مع أو تعديل عضو خاص في الكلاس الأصلي، فيجب على الكلاس الأصلي توفير تابع عام للقيام بذلك. هذا يعزز مبدأ التغليف من خلال ضمان عدم تمكن الكلاس الفرعي من كسر ثوابت الكلاس الأصلي.
الأنماط التقليدية:
- اصطلاح الشرطة السفلية: يمكن للكلاسات الفرعية الوصول بسهولة إلى الخصائص ذات البادئة `_` وتعديلها.
- الإغلاقات / IIFEs / دوال المصنع: الحالة الخاصة خاصة بالمثيل ولا يمكن الوصول إليها مباشرة من قبل الكلاسات الفرعية ما لم يتم كشفها صراحة عبر التوابع العامة. هذا يتماشى جيدًا مع التغليف القوي.
- الخرائط الضعيفة (WeakMaps): على غرار الإغلاقات، تتم إدارة الحالة الخاصة لكل مثيل ولا يتم كشفها مباشرة للكلاسات الفرعية.
متى تستخدم أي نمط؟
غالبًا ما يعتمد اختيار النمط على متطلبات المشروع، والبيئة المستهدفة، ومدى إلمام الفريق بالنهج المختلفة.
استخدم حقول الكلاس الخاصة (`#`) عندما:
- تعمل على مشاريع جافاسكريبت حديثة تدعم ES2022 أو أحدث، أو تستخدم محولات برمجية مثل Babel.
- تحتاج إلى أقوى ضمان مدمج لخصوصية البيانات والتغليف.
- تريد كتابة تعريفات كلاس واضحة وتصريحية وقابلة للصيانة تشبه لغات البرمجة كائنية التوجه الأخرى.
- تريد منع الكلاسات الفرعية من الوصول إلى الحالة الداخلية للكلاس الأصل أو العبث بها.
- تقوم ببناء مكتبات أو أطر عمل حيث تكون حدود واجهة برمجة التطبيقات الصارمة حاسمة.
مثال عالمي: قد تستخدم منصة تجارة إلكترونية متعددة الجنسيات حقول الكلاس الخاصة في كلاسات `Product` و `Order` الخاصة بها لضمان عدم إمكانية التلاعب بمعلومات التسعير الحساسة أو حالات الطلب مباشرة بواسطة نصوص برمجية خارجية، مما يحافظ على سلامة البيانات عبر مختلف عمليات النشر الإقليمية.
استخدم الإغلاقات / دوال المصنع عندما:
- تحتاج إلى دعم بيئات جافاسكريبت أقدم دون تحويل برمجي.
- تفضل أسلوب البرمجة الوظيفية أو تريد تجنب مشكلات ربط `this`.
- تقوم بإنشاء كائنات مساعدة بسيطة أو وحدات حيث لا تكون وراثة الكلاس مصدر قلق أساسي.
مثال عالمي: قد يختار مطور يبني تطبيق ويب لأسواق متنوعة، بما في ذلك تلك ذات النطاق الترددي المحدود أو الأجهزة القديمة التي قد لا تدعم ميزات جافاسكريبت المتقدمة، دوال المصنع لضمان التوافق الواسع وأوقات التحميل السريعة.
استخدم الخرائط الضعيفة (WeakMaps) عندما:
- تحتاج إلى إرفاق بيانات خاصة بالمثيلات حيث يكون المثيل نفسه هو المفتاح، وتريد التأكد من جمع هذه البيانات كقمامة عندما لا تتم الإشارة إلى المثيل.
- تقوم ببناء هياكل بيانات معقدة أو مكتبات حيث تكون إدارة الحالة الخاصة المرتبطة بالكائنات أمرًا بالغ الأهمية، وتريد تجنب تلويث مساحة اسم الكائن نفسه.
مثال عالمي: قد تستخدم شركة تحليلات مالية WeakMaps لتخزين خوارزميات تداول خاصة مرتبطة بكائنات جلسة عميل معينة. هذا يضمن أن الخوارزميات لا يمكن الوصول إليها إلا في سياق الجلسة النشطة ويتم تنظيفها تلقائيًا عند انتهاء الجلسة، مما يعزز الأمان وإدارة الموارد عبر عملياتها العالمية.
استخدم اصطلاح الشرطة السفلية (بحذر) عندما:
- تعمل على قواعد كود قديمة حيث لا تكون إعادة الهيكلة إلى حقول خاصة ممكنة.
- للخصائص الداخلية التي من غير المرجح أن يساء استخدامها وحيث لا يكون العبء الإضافي للأنماط الأخرى مبررًا.
- كإشارة واضحة للمطورين الآخرين بأن الخاصية مخصصة للاستخدام الداخلي، حتى لو لم تكن خاصة تمامًا.
مثال عالمي: قد يستخدم فريق يتعاون في مشروع مفتوح المصدر عالمي اصطلاحات الشرطة السفلية للتوابع المساعدة الداخلية في المراحل المبكرة، حيث يتم إعطاء الأولوية للتكرار السريع وتكون الخصوصية الصارمة أقل أهمية من الفهم الواسع بين المساهمين من خلفيات مختلفة.
أفضل الممارسات لتطوير جافاسكريبت على مستوى عالمي
بغض النظر عن النمط المختار، فإن الالتزام بأفضل الممارسات أمر حاسم لبناء تطبيقات قوية وقابلة للصيانة وقابلة للتطوير في جميع أنحاء العالم.
- الاتساق هو المفتاح: اختر نهجًا أساسيًا واحدًا للتغليف والتزم به في جميع أنحاء مشروعك أو فريقك. يمكن أن يؤدي خلط الأنماط بشكل عشوائي إلى الارتباك والأخطاء.
- وثق واجهات برمجة التطبيقات الخاصة بك: وثق بوضوح التوابع والخصائص العامة والمحمية (إن وجدت) والخاصة. هذا مهم بشكل خاص للفرق الدولية حيث قد يكون التواصل غير متزامن أو كتابيًا.
- فكر في التوريث (Subclassing): إذا كنت تتوقع أن يتم تمديد كلاساتك، ففكر بعناية في كيفية تأثير آلية التغليف التي اخترتها على سلوك الكلاس الفرعي. عدم قدرة الكلاسات الفرعية على الوصول إلى الحقول الخاصة هو خيار تصميم متعمد يفرض تسلسلات هرمية أفضل للوراثة.
- ضع في اعتبارك الأداء: على الرغم من أن محركات جافاسكريبت الحديثة محسّنة للغاية، كن على دراية بالآثار المترتبة على الأداء لبعض الأنماط، خاصة في التطبيقات ذات الأهمية الحاسمة للأداء أو على الأجهزة منخفضة الموارد.
- احتضن الميزات الحديثة: إذا كانت بيئاتك المستهدفة تدعمها، فاحتضن حقول الكلاس الخاصة. إنها توفر الطريقة الأكثر مباشرة وأمانًا لتحقيق التغليف الحقيقي في كلاسات جافاسكريبت.
- الاختبار أمر حاسم: اكتب اختبارات شاملة للتأكد من أن استراتيجيات التغليف الخاصة بك تعمل كما هو متوقع وأن الوصول أو التعديل غير المقصود يتم منعه. اختبر عبر بيئات وإصدارات مختلفة إذا كان التوافق مصدر قلق.
الخاتمة
تمثل حقول الكلاس الخاصة في جافاسكريبت (`#`) قفزة كبيرة إلى الأمام في قدرات اللغة كائنية التوجه. إنها توفر آلية مدمجة وتصريحية وقوية لتحقيق التغليف، مما يبسط إلى حد كبير مهمة إخفاء البيانات والتحكم في الوصول مقارنة بالنهج القديمة القائمة على الأنماط.
بينما تظل الأنماط التقليدية مثل الإغلاقات ودوال المصنع و WeakMaps أدوات قيمة، خاصة للتوافق الرجعي أو الاحتياجات المعمارية المحددة، فإن حقول الكلاس الخاصة توفر الحل الأكثر اصطلاحية وأمانًا لتطوير جافاسكريبت الحديث. من خلال فهم نقاط القوة والضعف لكل نهج، يمكن للمطورين في جميع أنحاء العالم اتخاذ قرارات مستنيرة لبناء تطبيقات أكثر قابلية للصيانة وأمانًا وجيدة التنظيم.
يعزز اعتماد حقول الكلاس الخاصة الجودة الإجمالية لكود جافاسكريبت، مما يجعله يتماشى مع أفضل الممارسات المتبعة في لغات البرمجة الرائدة الأخرى ويمكّن المطورين من إنشاء برامج أكثر تطورًا وموثوقية لجمهور عالمي.