مفهوم الـ Class والـ Object في الـ OOP
السلام عليكم ورحمة الله وبركاته
المقدمة
سنبدأ سلسلة أو دورة صغيرة مكونة من 5 مقالات نتحدث عن أهم المفاهيم في عالم الـ OOP
وسنبدأ بأول مقالة بالحديث عن الـ Classes
الكلاسات هي بداية أو مدخل للـ OOP وهي اختصار للـ Object Oriented Programming وتعني البرمجة الشيئية أو برمجة كائنية التوجه
أحب أن تكون هذه المقالة مقدمة لبعض أساسيات الكلاسات
ملحوظة: سنطبق مفاهيم الـOOPباستخدام لغةTypescript، لكن لن نتطرق لتفاصيل اللغة أو مميزات اللغة أو أي زيادة تقدمها اللغة في الـOOP، كل ما سنركز عليه هو المفهوم العام للـOOPالثابت في عالم الـOOPوليست الأمور المستحدثة الخاصة بلغة معينة
ما هو الـ OOP
الـ OOP كمفهوم فهو يسمح لك بتصنيف أو تمثيل أشياء معينة لها خواص او مميزات داخل البرمجة
على سبيل المثال البرنامج لا يفهم ما هي الحيوانات البرمجة بشكل عام لا تعرف شيئًا يدعى Animal
إنه مصطلح نستعمله نحن البشر، لكن البرنامج لا يعرفه، الأمر مماثل للسيارات البرنامج لا يعرف شيئًا يدعى سيارة
بمعنى آخر أي شيء نفهمه نحن البشر البرنامج لن يفهمه
فلذلك نحتاج لشيء لوصفه للبرنامج ليعرف كيف يتعامل معه
هنا يأتي دور الـ class هو الذي من خلاله نصنف أو نمثل للبرنامج الشيء الغريب عليه ونبدأ في وصفه
نمثله أي نعطيه خواص وأفعال وهي المتغيرات التي تمثل خواصه والدوال التي ستمثل الأفعال
إنشاء كلاس يمثل الـ Animal
لنأخذ مثالًا، نحن نريد أن نصف للبرنامج ما هو الـ Animal
class Animal {}
نبدأ بتعريف الـ Animal لذا نكتب class ثم اسم الشيء الذي نريد أن نمثله للبرنامج وهو في حالتنا هذه سيكون الـ Animal
الـ class هو كحاوية يحتوي على المتغيرات والدوال التي تصف الشيء
- تعريف الكلاسات والمفاهيم التي تدور حولها ومفاهيم الـ
OOPبشكل عام كلها نفس الشيء في جميع اللغات، نحن سنطبق باستخدام لغةTypescriptلكن كل المفاهيم التي ستتعلمها ستجدها في كل اللغات التي تدعم الـOOPإن شاء الله
الآن البرنامج يعرف أن هناك شيء يدعى Animal لكن هو حاليًا لا يفهم ما هو وما هي مواصفاته وخواصه، لأننا لم نحددها أو نعرفها للبرنامج بعد
نريد أن نعطي صفة لهذا الكلاس ليعرف البرنامج كيف يتعامل معه
لذا لنعطيه متغير يمثل الاسم
class Animal {
public name: string | undefined;
}
هنا أنشأنا كلاس جديد يدعى Animal وكتبنا بداخله متغير وحيد يعبر عن خواصه كالاسم name وطبعا نوعه string، وهنا كتبنا أن هذه الصفة تكون public
سنشرح ما معنى public فيما بعد في هذه المقالة
لأننا نتعامل مع الـ Typescript فيجب علينا قول أن قيمة المتغير name ستكون أما string أو undefined
وسبب أننا نقول undefined لأننا لم نعطي قيمة له بعد
وبطبيعة الحال عندما ندخل ونشرح الـ constructor سنعطي قيمة له وبالتالي لن نحتاج لكتابة undefined بعد ذلك
لكن ما هو الـ constructor؟ سيأتي ذكره في المقالة فيما بعد
دعونا نجعلها هكذا مؤقتًا
كيف نستخدم الكلاس
class Animal {
public name: string | undefined;
}
let cat = new Animal();
console.log(cat.name); // OUTPUT: undefined
هنا كتبنا let cat = new Animal();
هذا معناه أنك عملت متغيرًا جديدًا اسمه cat من نوع Animal
هنا الـ cat اسمه instance من الـ Animal ومعنى instance أنه نسخة من الكلاس
وستجد أنه يطلق عليه أيضًا object ومعناه شيء يحتوي على خواص معينة
يمكنك أن تتخيل الكلاس كأنه مصنع لديه مخطط بتفاصيل الاشياء التي سيصنعها
والـ object هو إنتاج هذا المصنع
بمعنى آخر لدينا كلاس يدعى Car ويحتوي على خصائص مثل model و color وتاريخ التصنيع و... إلخ من الصفات والخواص
عندما ننشئ object فالمصنع ينشئ سيارة جديدة بنفس المخطط وتحتوي على المواصفات التي في الكلاس
- أي شيء داخل الكلاس نستطيع استدعاءه أو الوصول إليه عن طريق
dotالنقطة.التي نكتبها بعد اسم الـobject
بالتالي المتغير name الذي داخل الكلاس نستطيع استدعاءه أو الوصول إليه عن طريق الـ dot
class Animal {
public name: string | undefined;
}
let cat = new Animal();
cat.name = 'Khalid Kashmiri';
console.log(cat.name); // OUTPUT: Khalid Kashmiri
هنا أنشأنا object اسمه cat من النوع Animal ثم أسندنا قيمة للمتغير name الذي بداخل cat وسمينا هذا القط الجميل خالد كشميري Khalid Kashmiri
وكما قلنا للوصول لخواص الـ object نستعمل الـ dot كهذا كما فعلنا cat.name = 'Khalid Kashmiri';
نستطيع إنشاء أكثر من object من نفس الكلاس، مثل أن المصنع ينشيء أكثر من نموذج من نفس المخطط
class Animal {
public name: string | undefined;
}
let cat = new Animal();
cat.name = 'Khalid Kashmiri';
console.log(cat.name); // OUTPUT: Khalid Kashmiri
let dog = new Animal();
dog.name = 'Khadir Karawita';
console.log(dog.name); // OUTPUT: Khadir Karawita
هنا أنشأنا object اسمه cat وواحد اسمه dog وكل واحد عدلنا في اسمه بشكل مستقل عن الأخر
وما أقصده بشكل مستقل أن التعديلات في cat لا تأثر على الـ dog، كل واحد له متغير name مستقل عن الاخر
هنا في الـ cat اعطيناه اسم Khalid Kashmiri والـ dog اسمه Khadir Karawita
يمكنك إنشاء دوال داخل الكلاس فالأمر لا يختصر على المتغيرات فقط بل يمكنك إنشاء عدة دوال
والدوال عندما تكون داخل الكلاس تدعى method
class Animal {
public name: string | undefined;
public age: number | undefined;
public printInfo() {
console.log(`Name is: ${this.name}, his age: ${this.age}.`);
}
}
let cat = new Animal();
cat.name = 'Khalid Kashmiri';
cat.age = 3;
cat.printInfo(); // OUTPUT: Name is: Khalid Kashmiri, his age: 3.
هنا لدينا دالة تدعى printInfo وظيفتها طباعة جملة تعطينا معلومات عن هذا الـ object
هنا this تمثل الـ object الذي سيستخدم الدالة، وفي حالتنا الـ object الذي استخدم دالة الـ printInfo كان الـ cat لذا الـ this هنا تمثل الـ cat في هذه اللحظة
ملحوظة: للوصول لأي متغير او دالة داخل الكلاس في لغة الـjavascriptوالـTypescriptمن داخل الكلاس يجب أن نستعمل الـthisدئمًا عندما نريد الوصول لاي عنصر داخل الكلاس في عالم الـoopالـthisدائما ما تشير للـobjectالذي يستخدم عناصر الكلاس مثل الدوال في حالتنا كان لديناobjectيدعىcatوقمنا باستدعاء دالةprintInfoمن خلال الـcatكهذاcat.printInfo()بالتالي فالـthisداخل دالة الـprintInfoستكون تمثلobjectالـcat
في لغات الـ
oopالأخرى ليس الزاميًا أن نستعمل الـthis، ففي اللغات الأخرى يمكننا أن نستغني عنها ويمكننا الوصول لعناصر الكلاس بدونها
فيمكننا استخدامnameدون الحاجة لاستخدامthis.nameوهذا الشائع في باقي اللغات
أما في لغة الـTypescriptوالـjavascriptإلزامي أن نستعملthis
معلومة إضافية وهي أننا عندما ننشئ object من الكلاس هكذا
let cat = new Animal();
فأسم الـ object يتم تخزينه في الذاكرة Memory Ram في مكان تعرفه يسمى Stack
والـ object نفسه يتم تخزينه في مكان آخر يسمى Heap واسم الـ object في الـ Stack يكون مجرد reference للـ object الذي يتم تخزينه في الـ Heap
Stack | Heap
|
|
| new Animal()
+------------+ | +------------+
| cat | ----------> | name: null |
+------------+ | | age: null |
| +------------+
|
|
|
إنشاء الـ Constructor
الـ constructor معناه الحرفي هو مُنشيء بمعنى أنه هو الذي يُنشيء الـ object من الكلاس
class Animal {
public name: string | undefined;
}
let cat = new Animal(); // this is a constructor
لنركز على هذا السطر let cat = new Animal(); هنا أنت تستخدم constructor وهو Animal()
الـ constructor عند استدعاءه يحمل نفس اسم الكلاس
فيكون new Animal(); معناه كالتالي أنشيء object جديد من الكلاس Animal
وليكن cat مجرد حاوية لهذا الـ object
بالتالي عندما نكتب هذا السطر new Animal(); نكون قد أنشأنا object بالفعل في الـ memory ram
واستعملنا cat ليحمل عنوان الـ object في الـ memory ram فالـ cat هنا مجرد reference للوصول للـ object
لنعود للرسالة التي ظهرت لنا عندما أنشأنا متغير بهذا الشكل
> Property 'name' has no initializer and is not definitely assigned in the constructor.
ترجمة الرسالة الحرفية هي كالآتي:
المتغير
nameلا يملك قيمة وأيضًا لا يتم إسناد قيمة له في الـconstructor
- كيف يتم إسناد قيمة من خلال الـ
constructor؟
let cat = new Animal();
أول شيء ستلاحظه في الـ constructor هو أنه مثل الدالة له اقواس ()
- بالتالي هل يمكننا أن نرسل له قيم المتغيرات من خلاله؟ الاجابة نعم
علينا أن ندرك شيء مهم هنا
class Animal {}
let obj = new Animal(); // Animal() is a default constructor (empty constructor)
عندما تنشيء كلاس حتى ولو كان فارغًا فاللغة تضع في داخله constructor خفي لا تراه
هو يكون constructor فارغ لا يفعل شيء سوء إنشاء object فارغ
يسمى بـ default constructor أو empty constructor
يمكنك أن تتخيله كهذا
class Animal {
constructor() {} // hidden constructor, doesn't do anything
}
let obj = new Animal(); // we are use the default constructor here already
default constructor لا يستقبل أي شيء ولا يقوم بعمل أي شيء
- إذًا يمكننا إنشاء
constructorآخر ينشيءobjectآخر بالشكل الذي نريده ؟ الإجابة نعم
class Animal {
public name: string;
constructor(name: string) {
this.name = name;
}
}
let cat = new Animal('Khalid Kashmiri'); // this is our constructor
console.log(cat.name); // OUTPUT: Khalid Kashmiri
الـ constructor يشبه الدالة كثيرًا, لكنه لا يرجع أي شيء
نكتب constructor ثم نتعامل معه معاملة الدالة، هنا الـ constructor الذي أنشأناه يقوم بأخذ name من نوع string ثم يسندها للـ name الخاص بالكلاس نفسه
لاحظ أننا جعلنا الـ name يقبل string وأزلنا الـ undefined, لماذا ؟
لأنه أصبح لدينا constructor يسند قيمة للـ name
هنا استعملنا الـ this مجددًا للوصول لعناصر الكلاس وقلنا أنها تمثل الـ object الذي يستخدم عناصر الكلاس
وقلنا أن في لغة الـ javascript والـ Typescript يجب أن نستعمل الـ this
هنا لاحظ أننا استطعنا التفريق بين الـ name الذي بداخل الكلاس والـ name الخاص بالـ constructor عن طريق الـ this
تذكر أن في لغة الـ
Typescriptوالـjavascriptإلزامي أن نستعملthisحتى ولو كان اسم المتغير في الـconstructorمختلف عن اسمه في الكلاس هكذاconstructor(n: string) { this.name = n; }هنا المتغيرين مختلفين ولا يوجد أي تصادم بين الاسمين لكن يجب أن نستعملthis
- ملخص ما أريدك أن تعرفه عن الـ
thisأنها دائما ما تشير للـobjectالذي استعملها - و يجب أن نستعملها دائما عندما نريد أن نصل لاي عنصر داخل الكلاس سواء متغير أو دالة
فمعنى هذا السطر let cat = new Animal('Khalid Kashmiri') أننا أنشأنا object باستخدام الـ constructor الذي أنشأناه نحن
والذي يستقبل متغير يدعى name من النوع string لذا أرسلنا مع الـ constructorقيمة لهذا المتغير وهو Khalid Kashmiri
ثم قام الـ constructor في داخله بعمل اسناد بقيمة الـ name المرسلة في الـ constructor لقيمة المتغير name الخاص بالـ object وللتفريق ما بينهما استعملنا this وهي تمثل الـ object الذي تم إنشاءه للتو
- هل هذا معناه أنه لا يمكننا إنشاء
objectمن الـconstructorالفارغ ؟ لنجرب!
class Animal {
public name: string;
constructor(name: string) {
this.name = name;
}
}
let cat = new Animal('Khalid Kashmiri');
let dog = new Animal(); // ERROR!!
هنا سيعترض البرنامج ويقول لك
> Expected 1 arguments, but got 0.
هنا هو يتوقع أن ترسل له قيمة للمتغير الذي يستقبله الـ constructor لأنه يستقبل قيمة للـ name
وبالنسبة للـ constructor الفارغ الذي يتم إنشاءه تلقائيا لنا عند تعريف الكلاس، يختفي من الوجود عندما ننشيء constructor خاص بنا
- مع العلم أن في اغلب لغات الـ
oopيمكنك أن تنشيء أكثر منconstructorونفرق بينهم عن طريق مفهوم الـfunction overloading، لكن بما أن الـTypescriptلا تدعم أكثر منconstructorفلن نتطرق لهذا الأمر
لننظر إلى الكود التالي
class Animal {
public name: string;
public age: number;
public weight: number;
constructor(name: string, age: number, weight: number) {
this.name = name;
this.age = age;
this.weight = weight;
}
public printInfo() {
console.log(
`Name is: ${this.name}, his age: ${this.age}, his weight: ${this.weight} kg.`
);
}
}
let cat = new Animal('Khalid Kashmiri', 3, 4.1);
cat.printInfo(); // OUTPUT: Name is: Khalid Kashmiri, his age: 3, his weight: 4.1 kg.
ستلاحظ أننا كلما أنشأنا متغيرات داخل الكلاس، نقوم بإسناد قيمة لها داخل الـ constructor
يمكننا اختصار الأمر في خطوة واحدة كالتالي
class Animal {
constructor(
public name: string,
public age: number,
public weight: number
) {}
public printInfo() {
console.log(
`Name is: ${this.name}, his age: ${this.age}, his weight: ${this.weight} kg.`
);
}
}
let cat = new Animal('Khalid Kashmiri', 3, 4.1);
cat.printInfo(); // OUTPUT: Name is: Khalid Kashmiri, his age: 3, his weight: 4.1 kg.
هكذا ننشيء متغيرات داخل الـ constructor نسند لها قيمة في نفس اللحظة
هذه الميزة موجودة في بعض اللغات
- سنفعل نفس الأمر لكن على متغير واحد لتبسيط الشكل لا أكثر
كل ما فعلناه هو أننا حولنا هذا
class Animal {
public name: string;
constructor(name: string) {
this.name = name;
}
}
لهذا
class Animal {
constructor(public name: string) {}
}
Access Modifiers
الـ Access Modifiers لها انواع وهي الـ public, private, protect, readonly
نضعها قبل المتغيرات أو الدوال
public
المتغيرات او الدوال من نوع public يستطيع الـ object الوصول لها عن طريق الـ dot من خارج الكلاس بشكل اعتيادي
class Animal {
public name: string | undefined;
public printInfo() {
console.log(`Name is: ${this.name}.`);
}
}
let cat = new Animal();
cat.name = 'Khalid Kashmiri';
cat.printInfo();
// printInfo and name are public, so the object cat can access it outside the class
- واذا لم نحدد النوع، يكون المتغير أو الدالة
publicبشكل افتراضي
class Animal {
name: string | undefined; // public by default
// public by default
printInfo() {
console.log(`Name is: ${this.name}.`);
}
}
let cat = new Animal();
cat.name = 'Khalid Kashmiri';
cat.printInfo();
// printInfo and name are public by default,
private
هو كما يوحي الاسم فهو يمنع الـ object من الوصول لاي شيء نوعه private
class Animal {
private name: string; // It is private!!!
constructor(name: string) {
this.name = name; // allow write & edit it inside class
}
public printInfo() {
console.log(`Name is: ${this.name}.`);
}
public setName(name: string) {
this.name = name; // allow write & edit it inside methods
}
}
let cat = new Animal('Khalid Kashmiri');
cat.printInfo(); // No Error because is a public
console.log(cat.name); // ERROR!!
cat.name = 'Khadir Karawita'; // ERROR!!
cat.setName('Khadir Karawita'); // Ok, we can edit it inside a method
هنا جعلنا الـ name يكون private بالتالي أي object الكلاس لن يستطيع استدعاءه
وإن حاول سيقال له:
> Property 'name' is private and only accessible within class 'Animal'.
ستلاحظ أن الدوال والـ constructor هم فقط من يستطيعون الوصول للـ name والتعديل عليه
- بالتالي أي شيء
privateيمكن الوصول اليه وتعديله داخل الكلاس فقط لكن لا يمكن لأيobjectاستدعاءه او التعديل عليه
ويفضل دائمًا أن تجعل جميع المتغيرات private والـ object يستطيع الوصول له عن طريق الدوال كوسيط بينه وبين المتغيرات مثل ما فعلنا هنا cat.setName('Khadir Karawita');
- وسنتكلم عن هذا الأمر بالتفصيل في مقالة الـ
Encapsulation
ملحوظة: حتى لو كان هناك كلاس يرث من كلاس آخر فالكلاس التاني لا يستطيع هو كذلك الوصول الى الـprivateالخاص بالكلاس الأول وهذا سنناقشه أكثر وبالتفصيل في مقالة عن الـInheritance
protect
هو مثله مثل الـ
privateلكن يمكن للكلاس الذي ورثه أن يصل ويستدعي أي شيءprotectوهذا أيضًا سنناقشه أكثر وبالتفصيل في مقالة عن الـInheritance
readonly
هو كما يوحي الاسم فهو يمنع الـ object من تعديله لكن يسمح له بالوصول له وطباعته
class Animal {
readonly name: string; // It is read-only
constructor(name: string) {
this.name = name; // allow write & edit it ONLY inside the constructor
}
public printInfo() {
console.log(`Name is: ${this.name}.`);
}
public setName(name: string) {
this.name = name; // ERROR!!, we can't write it inside methods
}
}
let cat = new Animal('Khalid Kashmiri');
cat.printInfo(); // OUTPUT: Name is: Khalid Kashmiri.
console.log(cat.name); // Ok, we can read it
cat.name = 'Khadir Karawita'; // ERROR!!, we can't write it
cat.setName('Khadir Karawita'); // ERROR!!, we can't write it inside methods also
لاحظ أننا يمكننا طباعة cat.name لكن لا يمكننا تعديله حتى داخل الدوال الخاصة بالكلاس
يمكننا تعديله فقط مرة واحدة داخل الـ constructor وهذا بديهي جدًا
يمكننا استخدام الـ Access Modifiers الأخرى مع الـ readonly, لو لم نكتب أي شيء معها فسيفترض أنها public مثل الأمثلة السابقة، لكن يمكننا جعلها private وستتصرف مثل أي شيء private لا يمكننا استخدامه مع الـ object لكن يمكننا استخدامه داخل الكلاس
الكود التالي يوضح الأمر private readonly، بمجرد القراءة المفترض أنك فهمت الفكرة الأساسية
class Animal {
private readonly name: string; // It is a private read-only
constructor(name: string) {
this.name = name; // allow write & edit it ONLY inside the constructor
}
public printInfo() {
console.log(`Name is: ${this.name}.`); // We can read it inside methods
}
public setName(name: string) {
this.name = name; // ERROR!!, we can't write it inside methods
}
}
let cat = new Animal('Khalid Kashmiri');
cat.printInfo(); // OUTPUT: Name is: Khalid Kashmiri.
console.log(cat.name); // ERROR!!, name is private now
cat.setName('Khadir Karawita'); // ERROR!!, we can't write it inside methods also
Static
الـ static هو جعل المتغيرات أو الدوال مرئية على مستوى الكلاس وليس الـ object
بمعنى أن أي شيء سواء متغيرات أو دوال تكون static فإننا نستطيع الوصول لها عن طريق اسم الكلاس نفسه وليس عن طريق object
class Animal {
public name: string;
public static numberOfAnimal: number = 0; // Only the class itself can access it
constructor(name: string) {
this.name = name;
Animal.numberOfAnimal += 1; // Use the class itself instead of 'this'
}
public printInfo() {
console.log(`Name is: ${this.name}.`);
}
}
let cat = new Animal('Khalid Kashmiri');
let dog = new Animal('Khadir Karawita');
console.log(Animal.numberOfAnimal); // OUTPUT: 2
هنا لدينا متغير يدعى numberOfAnimal وكما يوحي الاسم فهو سيكون عدد الحيوانات التي أنشأناها
جعلناه static بالتالي يمكن استدعاءه عن طريق اسم الكلاس نفسه وليس عن طريق الـ object
ويكنك أن تلاحظ ذلك في الـ constructor لقد استعملنا Animal.numberOfAnimal بدلًا من this.numberOfAnimal
وهذا منطقي أن يكون numberOfAnimal مرئي على مستوى الكلاس
لانه يحسب عدد الـ object التي يُنشيء من الكلاس
وليس من المنطق هنا أن تقول cat.numberOfAnimal
أن حاولت أن تجرب أن تستدعي numberOfAnimal عن طريق الـ object
فهذه الرسالة سترحب بك
> Property 'numberOfAnimal' does not exist on type 'Animal'. Did you mean to access the static member 'Animal.numberOfAnimal' instead?
بمعنى أن المتغير numberOfAnimal نوعه static بالتالي تستطيع الوصول إليه من خلال اسم الكلاس
باختصار إن أردت أن تنشيء متغيرات أو دوال تستطيع الوصول لها عن طريق اسم الكلاس وليس عن طريق الـ
objectلأنك ربما تريد أن تجعلها معلومات عامة وثابتة على مستوى الكلاس لأي سبب من الأسباب
الدوال بالطبع يمكنها أن تكون static الأمر ينطبق عليها مثل المتغيرات
class Animal {
public name: string;
private static numberOfAnimal: number = 0; // Only the class itself can access it
constructor(name: string) {
this.name = name;
Animal.numberOfAnimal += 1; // Use the class itself instead of 'this'
}
public printInfo() {
console.log(`Name is: ${this.name}.`);
}
public static getNumberOfAnimal() {
// Only the class itself can access it
return Animal.numberOfAnimal; // Use the class itself instead of 'this'
}
}
let cat = new Animal('Khalid Kashmiri');
let dog = new Animal('Khadir Karawita');
console.log(Animal.getNumberOfAnimal()); // OUTPUT: 2
console.log(cat.getNumberOfAnimal()); // ERROR!!
هنا جعلنا الـ numberOfAnimal يكون private ثم أنشأنا دالة تدعى getNumberOfAnimal
تكون public وstatic بالتالي نستطيع الوصول لها عن طريق اسم الكلاس نفسه وليس عن طريق الـ object
لنأخذ مثال بسيط سريع أخر عن الـ static
class Math {
public add(a: number, b: number) {
return a + b;
}
public getCircleArea(radius: number) {
return 3.14 * radius * radius;
}
}
لنفترض أننا لدينا كلاس يدعى Math وكما ترى به دوال بسيطة متعلقة بالرياضيات
مثل دالة add تقوم بجمع رقمين ودالة getCircleArea تقوم بحساب مساحة دائرة وهكذا من الدوال التي لها علاقة بالرياضيات
الآن كيف سنستخدم هذا الكلاس ؟
let math = new Math();
console.log(math.add(5, 5)); // OUTPUT: 10
console.log(math.getCircleArea(5)); // OUTPUT: 78.5
عرفنا object من الكلاس واستدعينا الدوال من خلاله
جميل .. لكن سؤال هل يوجد فائدة حقيقية لاستدعاء الدوال من خلال الـ object ؟
هل الـ object يحتاج لبيانات أو هناك constructor يحتاج لتنفيذه لكي نستطيع استدعاء الدوال add و getCircleArea ؟
الإجابة لا .. لا يوجد داعي لاستدعاء الدوال من خلال الـ object
إذًا ماذا نفعل ؟
ببساطة نجعل الدوال static ونستدعيها من خلال اسم الكلاس نفسه .. طالما أنها لا تحتاج لبيانات أو لا تحتاج لـ object لتنفيذها
لأنها دوال مستقلة بذاتها ولا تحتاج لـ object لتنفيذها
وهذه النوعية من الدوال تسمى Pure Functions وهي دوال مستقلة بذاتها ولا تتأثر بأي شيء خارجي
أي تأخذ بيانات وتعطيك نتيجة ولا تتأثر بأي شيء آخر
class Math {
public static add(a: number, b: number) {
return a + b;
}
public static getCircleArea(radius: number) {
return 3.14 * radius * radius;
}
}
هنا قمنا بجعل الدالتين add و getCircleArea يكونان static
لأنه كما قلنا لا تحتاج لـ object لتنفيذها ونستطيع استدعائها من خلال اسم الكلاس نفسه دون مشاكل
console.log(Math.add(5, 5)); // OUTPUT: 10
console.log(Math.getCircleArea(5)); // OUTPUT: 78.5
هكذا نستطيع استدعاء الدوال من خلال اسم الكلاس نفسه دون الحاجة لإنشاء object
ختام
وهكذا نكون انتهينا من أول مقالة في سلسلتنا الصغيرة المكونة من 5 مقالات نتحدث عن أهم المفاهيم في عالم الـ OOP
تحدثنا عن أهم النقاط في الـ Classes والـ Objects و في المقالة التالية سنتحدث عن الـ Encapsulation