الـ Polymorphism، اختلاف وتعدد الأشكال لشيء واحد
السلام عليكم ورحمة الله وبركاته
الفهرس
المقدمة
يسعدني أننا وصلنا لآخر مقالة من سلسلتنا الصغيرة عن أهم مفاهيم الـ OOP
سنشرح في هذه المقالة عن مفهوم الـ Polymorphism
وهو مفهوم بسيط لكن استخداماته وتطبيقاته ستراها في كل شيء
ستجد أنه حين تتعمق في المشاريع الكبيرة وتبدأ في تعلم واستخدام الـ Design Patterns
ستجد أنك تتعامل مع الـ Polymorphism
بكثرة
في حقيقة الأمر نحن تعاملنا وشرحنا القليل عن الـ Polymorphism
عندما كنا نشرح المفاهيم السابقة
هذا بسبب أنك ستجد أن المفاهيم الأربعة الخاصة بالـ OOP
متقاربة جدًا وتتداخل مع بعضها البعض
وهذا الأساس أن تفهم هذه المفاهيم وتحاول أن توظفها مع بعضها لتخدم الفكرة والمشروع التي تريد تنفيذه
تعريف الـ Polymorphism
مفهوم الـ Polymorphism
يمثل قدرة أي شيء مهما كان سواء كان دالة او object
على أخذ عدة أشكال في آن واحد
بمعنى إن رأيت شيء ما مهما كان يستطيع تغير تصرفاته أو افعاله أو خواصه بناءًا على موقف أو حالة معينة فهذا هو مفهوم الـ Polymorphism
من امثلة ان الدالة ممكن تأخذ أكثر من شكل هو فكرة الـ Overloading
أو فكرة الـ Overriding
أو الـ Generics
وسنفهم معنى هذا بالتفصيل
ملحوظة
: يجب أن تفهم المفهوم نفسه وليس تطبيقات المفهوم، لأن المفهوم ثابت لكن تطبيقاته تتنوع وتختلف
نحن فقط نشرح التطبيقات لكي نستوعب المفهوم بشكل افضل
أنواع الـ Polymorphism
كما قلت ستجد أنك استعملت مفهوم الـ Polymorphism
دون أن تدري
يمكننا تقسيم أنواع الـ Polymorphism
إلى نوعين وهما
Compiler-Time Polymorphism
Function Overloading
(Method Overloading
)Operator Overloading
Template
(Generic
)
Runtime Polymorphism
Function Overriding
(Method Overriding
)Dynamic dispatch
Virtual Function
(خاص بـC++
)
النوع الأول: Compiler-Time Polymorphism
هي قدرة اللغة او البرنامج على التعرف أو ادراك الـ Polymorphism
أثناء كتابتك للكود وقبل تنفيذ البرنامج، لهذا سمى بـ Compiler-Time Polymorphism
بعض تطبيقات المفهوم:
Function Overloading
(Method Overloading
)Operator Overloading
Template
(Generic
)
سنشرح ما هو مفهوم الـ Compiler-Time
عن طريق شرح الـ Function Overloading
و الـ Operator Overloading
فقط
أما الـ Template/Generic
فلا داعي للاستفاضة فيها
فيكفي شرح الـ Function Overloading
والـ Operator Overloading
، وأرجوا بعد ذلك أن تفهم وتستوعب جيدًا أصل وفكرة الـ Compiler-Time Polymorphism
ويمكنك أن تبحث عن الـ Template/Generic
إن اردت لزيادة فهمك ورؤية تطبيقات اخرى على المفهوم
Function Overloading
وهو أننا لدينا نفس الدالة لكن لها أشكال مختلفة وكل شكل يقوم بوظيفة معينة
أو ببساطة لدينا مجموعة دوال تشترك في إسم الدالة
وتختلف في عدد الـ Parameters ونوعها
مثال على هذا
function add(x: number, y: number) {
return x + y;
}
console.log(add(5, 10)); // OUTPUT: 15
هنا لدينا دالة add
تستقبل رقمين وترجع لنا مجموعهما ببساطة
السؤال ماذا ان اردنا ان نقوم بجعل دالة اخرى تجمع لنا ثلاثة ارقام
لكن تلك الدالة نريد ان نسميها أيضًا بـ add
أي بنفس اسم الدالة الأولى
function add(x: number, y: number) {
return x + y;
}
function add(x: number, y: number, z: number) {
return x + y + z;
}
function add(x: string, y: string) {
return x + y;
}
console.log(add(5, 10)); // OUTPUT: 15
console.log(add(5, 10, 15)); // OUTPUT: 30
console.log(add('Ahmed', ' Mohamed')); // OUTPUT: Ahmed Mohamed
لاحظ أن لدينا دوال بنفس الإسم لكن يختلفوا في عدد الـ parameters
هنا اللغة أو الـ compiler
يستطيع أن يفرق بينهم بعدد الـ parameters
أو نوعها سواء number
أو string
وهكذا
بمعنى أننا لو ارسلنا رقمين فهو سيفهم اننا نريد الدالة الأولى
واذا ارسلنا ثلاث ارقام فسيفهم اننا نريد الدالة الثانية
وإذا ارسلنا كلمتين فسيفهم اننا نريد الثالثة
هذا ما يسمى بـ Polymorphism
لان الدالة add
أصبح لديها شكلين الآن
وهنا تستطيع دالة add
تغير تصرفاتها بناءًا على الحالة
هنا بما أن الـ compiler
استطاع ان يفهم ويفرق بين الدوال بسهولة ويعرف متى سيستخدم كل دالة
دون ان نشغل أو ننفذ الكود فهنا سميناها بـ Compiler-Time Polymorphism
كل شيء تراه ينطبق عليه هذا الوصف والمفهوم فهو هكذا Compiler-Time Polymorphism
ملحوظة
: الـFunction Overloading
نستطيع تطبيقه في جميع الدوال سواء خارج الكلاسات أو داخل الكلاسات
لغةTypescript
لا تدعم الـFunction Overloading
بشكل شامل
لكن يظل هذا المفهوم مدعوم في اغلب لغات البرمجة
حتى برغم بفقر بعض اللغات لدعم بعض المفاهيم إلا انها لديها حلول أخرى
ففي المثال السابق للـ function overloading
الخاص بدالة الـ add
فيمكننا عمل نفس الشيء باسلوب مختلف قليلًا
كاستخدام مميزات أخرى تقدمها اللغة لإيجاد حلول أخرى
function add(...numbers: number[]) {
return numbers.reduce((a, b) => a + b, 0); // add all numbers
}
console.log(add(1)); // OUTPUT: 1
console.log(add(1, 2)); // OUTPUT: 3
console.log(add(1, 2, 3)); // OUTPUT: 6
console.log(add(1, 2, 3, 4)); // OUTPUT: 10
// ... ect.
لاحظ لاحظت اننا هكذا حددناها لتكون خاصة بالارقام فقط
لكن هنا لا نستطيع ان نرسل string
او اي نوع أخر
يوجد حل لهذا الأمر لكن باستخدام مفهوم الـ Generic
أريدك أن تبحث عنها وتحاول أن تطبقها وتفهمها بنفسك إن أردت
لاننا لن نتطرق لها هنا كما قلنا
Operator Overloading
يمكنك أن تتخيله كشكل آخر للـ Function Overloading
والـ
وهو يهتم بتنفيذ العمليات الحسابات كـ +
-
*
/
%
... إلخ على الـ object
والكلاسات الجديدة التي تنشئها
على سبيل المثال لو لدينا كلاس يدعى Vector
كلاس أنشأناه ونريده أن يقوم بتمثيل العمليات على المتجهات
class Vector {
public x: number;
public y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}
فنريد أن نقوم بجمع متجهين على سبيل المثال
سننشيء دالة add
تقوم بأخذ object
متجه آخر وتجمعه مع الحالي
ودالة أخرى تدعى add
أيضًا لكن تأخذ رقمًا وتقوم بجمع هذا الرقم بقيم المتجه
class Vector {
public x: number;
public y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
// Operator Overloading
// add two vectors
public add(vector: Vector): Vector {
return new Vector(this.x + vector.x, this.y + vector.y);
}
// add number with vector
public add(n: number): Vector {
return new Vector(this.x + n, this.y + n);
}
}
هنا ستلاحظ أنشأنا دالتين تحملان نفس الإسم add
لاكن تأخذان قيم مختلفة
فالأولى تجمع متجه مع متجه والأخرى تجمع رقم مع متجه
وهذا يذكرك بالـ Function Overloading
أليس كذلك
لكن في الـ Operator Overloading
نطبق ونقوم بعمل نفس الأمر لكن مع الرموز الحسابية +
-
*
وهكذا
ونستخدمهم بهذا الشكل
let vector1 = new Vector(1, 2);
let vector2 = new Vector(3, 4);
let vector3 = vector1.add(vector2); // Vector {x: 4, y: 6}
let vector4 = vector1.add(10); // Vector {x: 11, y: 12}
أظنك كنت تأمل وتتوقع شكلًا أفضل كـ vector1 + vector2
وأظنك أيضًا تقول الآن أن هذا حل تحايلي لاستخدام دالة لتمثل رمز +
لنخدع أنفسنا أننا أنشأنا جمع حقيقي بين الـ object
بصراحة كما قلت فالـ typescript
لا تدعم الـ Function Overloading
والـ Operator Overloading
بشكل شامل
على عكس لغات أخرى كـ C++
فإنها تدعم الـ Operator Overloading
بشكل كامل
فنفس المثال السابق يمكننا كتابته في الـ C++
بهذا الشكل
وسنستخدم رمز +
في الجمع هكذا vector + vector2
بدلًا من استخدام الدوال
نفس المثال في الـ C++
class Vector {
public:
int x;
int y;
Vector(int x, int y) {
this->x = x;
this->y = y;
}
// Operator Overloading
// Overload + operator to add two Vector objects
Vector operator+(const Vector& vector) {
return Vector(vector.x + x, vector.y + y);
}
// Overload + operator to add number with vector objects
Vector operator+(int n) {
return Vector(x + n, y + n);
}
};
لاحظ استخدامنا لـ operator +
هنا
استخدامنا للـ Operator Overloading
في الـ C++
سيكون هكذا
Vector vector1(1, 2);
Vector vector2(3, 4);
Vector vector3 = vector1 + vector2; // Vector {x: 4, y: 6}
Vector vector4 = vector1 + 10; // Vector {x: 11, y: 12}
تذكر المفهوم يظل ثابت حتى وان تغير شكله من لغة للغة
قد تفقد لغة لبعض جوانب المفهوم أو قد تزيد لغة من مفهوم ما وتطوره أو تحسنه
مهما يكن فيظل فهمك لأساس المفهوم هو المهم، أظن أن رؤيتك للفروق في الـC++
زاد وحسن نظرتك للأمر بشكل ما
النوع الثاني: Runtime Polymorphism
هي قدرة اللغة او البرنامج على التعرف أو ادراك الـ Polymorphism
بعد تنفيذ كودك
أي بعد تنفيذ الكود وأثناء ما البرنامج قيد التشغيل، لهذا سمى بـ Runtime Polymorphism
بعض تطبيقات المفهوم:
Function Overriding
(Method Overriding
)Dynamic dispatch
Virtual Function
(خاص بـC++
)
سنتعمق في باقي المقالة عن الـ Function Overriding
والـ Dynamic dispatch
بالأخص
لان هذا هو الجزء الأكثر استخدامًا وأهمية في عالم الـ OOP
على وجه الخصوص
أما الـ Virtual Function
فلن أتطرق لها لانها ليست تطبيقًا شائعًا للمفهوم
بل هو خاص للغة الـ C++
لانها تفردت به
لذا لن يفيدك بشيء أن عرفته أو تعلمته إلا اذا اردت أن تتخصص في لغة الـ C++
تذكر أننا هنا سنستخدم الـ
Inheritance
فقط لنحقق بعض التطبيقات على مفهوم الـRuntime Polymorphism
وتذكر أننا نستطيع تطبيق مفاهيم الـPolymorphism
بطرق اخرى غير الـInheritance
إن رأيت شيء ما يستطيع تغير تصرفاته بناءًا على موقف ما فهذا هو مفهوم الـPolymorphism
Function Overriding
أظنك بالفعل تعرف ما هي الـ Function Overriding
من خلال المقالات السابقة
لذا لا داعي لأن اعيد شرحها بالتفصيل
لكن أريدك فقط أن تفهم ما هي الـ Runtime Polymorphism
فأنا هنا سأقوم بشرح مفهوم الـ Runtime Polymorphism
من خلال شرحي للـ Function Overriding
استخدامنا للـ Inheritance
سيسهل ايصال وتطبيق مفهوم الـ Function Overriding
و الـ Dynamic dispatch
في الـ Runtime Polymorphism
لا أكثر
ولو وجدت لغات اخرى تطبق نفس المفهوم بطرق اخرى، فلن يهمك الكيفية لانك ستكون تفهم المفهوم بالفعل
تتذكر كلاس الـ Employee
من المقالة الماضية ؟
abstract class Employee {
protected name: string;
protected salary: number;
constructor(name: string, salary: number) {
this.name = name;
this.salary = salary;
}
// abstract methods
public abstract getSalary(): number;
}
لاحظ كيف ان دالة الـ getSalary
هي abstract
بالتالي ليس لديها شكل معين أو implementation
معين
سنطبق الآن الـ overriding
عليها
أريدك هنا ان تحلل وتستنتج بنفسك مفهوم الـ Polymorphism
هنا
ولماذا هو Runtime Polymorphism
؟
class CareemEmployee extends Employee {
public getSalary() {
return this.salary * 2 - 2000;
}
}
class SutraEmployee extends Employee {
public getSalary() {
return this.salary * 3 - 4000;
}
}
class SutraEmployee extends Employee {
public getSalary() {
return this.salary / 2 + 3000;
}
}
هل شممت رائحة الـ Polymorphism
؟
سنعيد تعريف الـ Polymorphism
مجددًا لتستنتجه بشكل أفضل
مفهوم الـ Polymorphism
يمثل قدرة أي شيء مهما كدالة على أخذ عدة أشكال في آن واحد
أو تستطيع هذه الدالة تستطيع تغير تصرفاتها وافعالها بناءًا على موقف أو حالة معينة
أظن أن هذا الكلام ينطبق بشكل واضح على الـ Overriding
وبما أنك تعرف الـ Overriding
بالفعل من المقالات السابقة فلا داعي لاعيد شرحها مجددًا هنا
يكفي فقط أن تستوعب أنه تطبيق عملي لمفهوم الـ Runtime Polymorphism
Dynamic dispatch
الآن سنناقش المفهوم الأساسي الأكثر أهمية هنا، وهو الـ Dynamic dispatch
للـ object
وهو يعد من أهم الأجزاء في الـ Polymorphism
لذا سنستفيض فيه لنهاية هذه المقالة
حسنًا، ما هو الـ Dynamic dispatch
بالتحديد ؟
الـ Dynamic dispatch
هو مفهوم يركز على إمكانية التغير بشكل سلس ومرن في أي وقت
فعلى سبيل المثال الـ object
يمكنه أن يغير نوع الـ constructor
أو الـ class
الذي يتم إنشاؤه منه في أي وقت، كيف يمكن هذا ؟
لا تستعجلني سأوضح لك الأمر الآن بأمثلة عملية، لكن أريدك أن تركز جيدًا هنا
هذه الميزة موجودة في معظم لغات البرمجة ومرتبطة بمفهومين آخرين هما Inheritance
والـ Abstraction
بالطبع قد تجد بعض اللغات تطبق المفهوم بشكل آخر وبشكل مختلف
حسنًا، في التطبيق العملي، إذا كان لدينا كلاس يسمى Employee
، وكلاسات أخرى تسمى CareemEmployee
و SutraEmployee
يرثان من الـ Employee
يمكن للـ object
من نوع الـ Employee
أن يبنى أو يستدعي أي constructor
من الـ CareemEmployee
أو SutraEmployee
، بشرط أن تكون الكلاسات ترث من الـ Employee
ركز في الجملة السابقة جيدًا وخصوصًا في الشرط
على سبيل المثال
class Employee {
protected name: string;
constructor(name: string) {
this.name = name;
}
public getInfo() {
console.log(`I am an employee named ${this.name}`);
}
public getName(): string {
return this.name;
}
}
class CareemEmployee extends Employee {
public getInfo() {
console.log(`I am a Careem employee named ${this.name}`);
}
}
class SutraEmployee extends Employee {
public getInfo() {
console.log(`I am a Sutra employee named ${this.name}`);
}
}
في هذا المثال، لدينا كلاس Employee
الذي يحتوي على constructor
ودالة getInfo
ولدينا أيضًا كلاسات CareemEmployee
و SutraEmployee
التي ترث من Employee
وقامت بعمل overriding
لدالة getInfo
لغاية الآن الأمور عادية، لكن أنظر للكود التالي وركز جيدًا
سنقوم بعمل متغير من نوع Employee
وهذا المتغير سيستطيع أن يكون object
من نوع كلاس الـ CareemEmployee
أو SutraEmployee
let employee: Employee;
employee = new CareemEmployee('Ahmed');
employee.getInfo(); // Output: I am a Careem employee named Ahmed
employee = new SutraEmployee('Mohamed');
employee.getInfo(); // Output: I am a Sutra employee named Mohamed
قمنا بإنشاء متغير يدعى employee
من نوع Employee
وجعلناه يأخذ الـ constructor
الخاص بكلاس الـ CareemEmployee
ويستدعي دالة الـ getInfo
الخاصة بالكلاس بداخله يتصرف كأنه object
من CareemEmployee
ثم جعلناه يأخذ الـ constructor
الخاص بكلاس الـ SutraEmployee
ويستدعي دالة الـ getInfo
الخاصة بالكلاس بداخله يتصرف كأنه object
من SutraEmployee
ولاحظ أنه في الأصل كان نوعه Employee
ما السبب ؟
هذه ميزة مدعومة في أغلب لغات البرمجة لدعم مفهوم الـ polymorphism
وجعل إنشاء الـ object
وتغيره أكثر سلاسة ومرونة
طبعًا مع وجود شرط معين يحكم هذه الميزة وهو وراثة الكلاسات لنفس الكلاس
ببساطة أنه كما قلنا أن المتغير من نوع كلاس الـ Employee
يستطيع أن يأخذ ويستدعي constructor
الـ CareemEmployee
أو SutraEmployee
طالما أنهما ينتميان لكلاس الـ Employee
استخدام abstract class و interface
ملحوظة
: عليك أن تعرف أنlet employee: Employee;
هكذا نحن لا نقوم باستدعاءconstructor
الـEmployee
، لاننا لم نقم بعملnew Employee()
بعد
معنى هذا الكلام هو أنه عندما نقوم بعمل let employee: Employee;
هكذا نحن نعرف متغير من نوع Employee
وليس object
لاننا لم نقم بإنشاء أي constructor
من الأساس
بل هو حاليًا يكون متغير عادي تمامًا من نوع Employee
فقط لا غير لحين إنشاء constructor
له
ولهذا فيمكننا استخدام الـ abstract class
والـ interface
لتطبيق الـ Polymorphism
ببساطة لاننا لا نحتاج لإنشاء constructor
abstract class Employee {
protected name: string;
constructor(name: string) {
this.name = name;
}
public abstract getInfo(): void;
public getName(): string {
return this.name;
}
}
class CareemEmployee extends Employee {
public getInfo() {
console.log(`I am a Careem employee named ${this.name}`);
}
}
let employee: Employee = new CareemEmployee('Ahmed');
employee.getInfo(); // Output: I am a Careem employee named Ahmed
لاحظ أن المتغير employee
من نوع كلاس Employee
وهذا الكلاس كما تلاحظ هو abstract
لكن كما قلنا انه هنا يقوم باستدعاء الـ constructor
الخاص بكلاس الـ CareemEmployee
بالطبع كلاس الـ CareemEmployee
استخدم هنا الـ default constructor
الخاص به
ويمكنك استبدال الـ abstract class
وجعلها interface
والأمور ستكون نفس الشيء مع مراعاة الاختلافات بين الـ abstract class
والـ interface
التطبيق العملي واستخداماته
أظنك تحتاج أن ترى شيء مباشر يتطبق الفكرة بشكل واقعي وعملي
إليك هذا المثال البسيط
abstract class Bank {
protected amount: number;
constructor(amount: number) {
this.amount = amount;
}
public abstract deposit(amount: number, employee: Employee): void;
public getAmount() {
return this.amount;
}
}
لدينا هنا abstract class
يدعى Bank
بها دالة تدعى deposit
و getAmount
بالطبع يمكنك جعلها interface
كما قلنا
لاحظ أن دالة deposit
هي دالة تستقبل object
من نوع Employee
ثم لدينا كلاس MisrBack
و CairoBank
سيقومان بوراثة Bank
class MisrBank extends Bank {
public deposit(amount: number, employee: Employee) {
this.amount += amount;
console.log(
`Deposited ${amount} in MisrBank from ${employee.getName()}. Current bank balance: ${
this.amount
}`
);
}
}
class CairoBank extends Bank {
public deposit(amount: number, employee: Employee) {
this.amount += amount;
console.log(
`Deposited ${amount} in CairoBank from ${employee.getName()}. Current bank balance: ${
this.amount
}`
);
}
}
حسنًا برأيك كيف سيتم استخدام دالة deposit
الآن ؟
الدالة الآن كما تلاحظ تستقبل object
من نوع Employee
هل هذه الدالة ستستقبل object
من نوع CareemEmployee
و SutraEmployee
أيضًا ؟، بالطبع نعم !
طالما CareemEmployee
و SutraEmployee
من عائلة Employee
فيمكن للدالة أن تستقبلهم دون مشاكل
let bank: Bank = new MisrBank(10000);
let employee: Employee;
employee = new CareemEmployee('Ahmed');
bank.deposit(5000, employee); // Deposited 5000 in MisrBank from Ahmed. Current bank balance: 15000
employee = new SutraEmployee('Mohamed');
bank.deposit(5000, employee); // Deposited 10000 in MisrBank from Mohamed. Current bank balance: 25000
هل تستطيع ان تنظر للكود وتحاول أن تفهم ماذا جرى ؟
لقد طبقنا الكثير من مفهوم الـ Polymorphism
هنا
- أولًا المتغير
bank
كان من نوعBank
و أخذconstructor
الـMisrBank
- ثانيًا المتغير
employee
كان من نوعEmployee
و أخذconstructor
الـCareemEmployee
مرة ثم أخذconstructor
الـSutraEmployee
مرة، بكل سلاسة - ثالثًا والأهم أن الدالة
deposit
كانت تستقبل متغير من نوعEmployee
لكننا أرسلنا لهاobject
من نوعCareemEmployee
وSutraEmployee
تعليق ChatGPT
عندما تعرف المتغير
bank
بأنه من نوعBank
ولكنه يتم تهيئته باستخدامMisrBank
، فإنه يتم استخدامPolymorphism
لأنMisrBank
هو نوع مشتق منBank
وبالتالي يمكن استخدامه في متغير من نوعBank
. بالنسبة للمتغيرemployee
، فإنه يعتبر متغيرًا متعدد الاستخدامات أيضًا. يتم تعيينه أولاً بنوعCareemEmployee
ثم بنوعSutraEmployee
.
ومن خلال الاستفادة منPolymorphism
، يمكننا تعيين متغير من نوعEmployee
لكائناتCareemEmployee
أوSutraEmployee
، لأن كلاهما هو نوع فرعي منEmployee
. باستخدام هذا النوع من الهيكلة، يمكننا تحقيق العديد من الفوائد، بما في ذلك القدرة على استخدام واجهات مشتركة لمعاملة كائنات مختلفة بطريقة متجانسة. على سبيل المثال، يمكننا تصميم دوال تقوم بتنفيذ عمليات على كائناتEmployee
بشكل عام دون الحاجة إلى معرفة نوع الكائن الفعلي.
- ChatGPT
خاتمة
أريدك أن ترى الاستخدامات المحتملة لجعل الدالة تستقبل نوع Employee
لكننا نرسل لها أي object
ينتمي لعائلة الـ Employee
كان نوع CareemEmployee
او SutraEmployee
الأمر لا يتوقف هنا، أريدك تتخيل أمثلة أخرى كنظام Authentication
دالة تقوم بالتحقق من صلاحيات الـ User
فتقوم باستقبال أي object
ينتمي للـ User
سواء كان من Admin
أو Client
أو غيره
وإن لم يكن تقوم الدالة بارسال رسالة بأنك ليست مرخص لك هنا على سبيل المثل
أظن أننا وصلنا لنهاية سلسلة مقالات الـ OOP
الجميلة تلك كانت رحلة ممتعة
أردت من تلك المقالات الخمس أريد أرسم وعي وفهم للمفاهيم الأساسية والتقليل من التركيز على التطبيقات العملية لها
لأنه مهما اختلفت التطبيقات تظل المفاهيم ثابتة
ستجد العديد من اللغات تطبق هذه المفاهيم لكن باختلافات صغيرة
وأحيانًا تجد لغات تتخلى عن بعض المفاهيم وتستبدلها أو تعدلها أو تضيف شيء جديد
لك دائًما ما تجد أن المياه الجارية تخرج من نفس المنبع
ماذا بعد الـ OOP
عليك أن تعرف أولًا أن الـ OOP
تستخدمها بشكل أساسي في اللغات أو المشاريع التي تتطلب OOP
لكن عليك أن تدرك أن الـ OOP
هي مقترح أنت ممكن تستخدمها في مشروعك التي تتطلب وجودها بشكل أساسي
هناك مفاهيم وطرق اخرى تستطيع استخدامها في المشاريع بحسب اللغة أو المشروع
هناك شيء يدعى Functional Programming Paradigm
والـ Scripting programming
وهي تكون طرق مختلفة لكتابة الكود بشكل آخر وبها مفاهيمها الخاصة واساليبها الخاصة
الـ OOP
اعتبرها كأداة انت تمتلكها يمكنك استخدامها في الوقت المناسب
قد تجد صراعات كثيرة بين المبرمجين عن المفاهيم وأيها أهم وأيها سئ وجيد
لا تهتم لهذه الصراعات المهم أنك تفهم هذه المفاهيم وتوظفها في أماكنها الصحيحة
حسنًا سأعطيك فقط بعض العناوين وأريدك أن تبحث عنهم بنفسك
مع العلم أن كل واحدة منهم سيكون عالم لوحده وستخوص فيه كثيرًا
وستكتشف أمور أخرى كثيرة ومختلفة لذا استمتع!
- Object-Oriented Design
- SOLID Principles
- Design Patterns
- KISS Principle
- Dry Principle
- YAGNI Principle