مفهوم الـ Abstraction
تجريد: تعني Abstraction في اللغة الإنجليزية, و هو أسلوب مهم جداً يستخدم لتسهيل كتابة الأوامر على المبرمجين, فهو يجعلك قادراً على تنفيذ ما تريد دون الحاجة إلى معرفة كافة التفاصيل التي تم فيها تنفيذ ذلك. إذاً الـ Abstraction يجعلك تتعامل مع الأشياء بسطحية بدل أن تغوص في معرفة تفاصيل الكودات المعقدة.
فمثلاً إذا كنت تنوي بناء برنامج يتيح لمستخدميه إرسال إقتراحات حول التطبيق من خلال البريد الإلكتروني, في هذه الحالة لن يكون عليك القلق بتاتاً حول طريقة تعامل أوامر جافا مع البروتوكولات التي تعمل عندما يتم إرسال رسائل إلكترونية من خلال هذا التطبيق, لأنك لست مسؤولاً عنها, فعلياً هذه وظيفة شركة Sun التي تقوم بتطوير لغة جافا. و هم يخبرونك أنه لإرسال بريد إلكتروني إستخدم هذه الأوامر البسيطة فقط.
إذاً الـ Abstraction هو أسلوب يستخدم لإخفاء تفاصيل تنفيذ البرنامج. لتطبيق مفهوم الـ Abstraction نستخدم الكلمة abstract
ضمن شروط محددة.
مصطلحات تقنية
إذا أردت تعريف الشيء كـ abstract
, أكتب فقط الكلمة abstract
قبله.
الكلاس المعرف كـ
abstract
يسمى Abstract Class.الكلاس العادي الغير معرف كـ
abstract
يسمى Concrete Class.الدالة المعرفة كـ
abstract
تسمى Abstract Method أو Abstract Function.
مفهوم Abstract Class
إذا وضعت الكلمة abstract
قبل إسم الكلاس, ستتغير قليلاً طريقة التعامل معه لأنه لم يعد كلاس عادي. و عندها سيراه المترجم كـ Abstract Class.
نقاط مهمة حول الـ Abstract Class
الكلاس العادي لا يمكنه أن يحتوي على دوال نوعها
abstract
.الـ Abstract Class يمكنه أن يحتوي على دوال عادية, و يمكنه أن يحتوي على دوال نوعها
abstract
.إذا قمت بتعريف الكلاس كـ
abstract
, فهذا يعني أن هذا الكلاس لا يمكن إنشاء كائنات منه.بما أنه لا يمكن إنشاء كائنات من Abstract Class, فهذا يعني أنه للإستفادة من هذا الكلاس, يجب وراثته.
الكلاس الذي يرث من كلاس نوعه
abstract
, يجب أن يفعل Override لجميع الدوال المعرفة كـabstract
.
طريقة تعريف كلاس نوعه abstract
نكتب فقط الكلمة abstract
قبل الكلمة class
.
مثال
abstract class Example { }
مفهوم Abstract Method
إذا كنت تريد بناء دالة و جعل الكلاس الذي يرثها هو المسؤول عن كتابتة محتواها, قم بتعريفها كـ abstract
.
نقاط مهمة حول الـ Abstract Method
إذا وضعت الكلمة
abstract
قبل إسم الدالة, فهذا يعني أنها دالة من النوعabstract
.الدالة التي نوعها
abstract
هي دالة لها إسم و نوع محدد, لكنها لا تحتوي على body (جسم), أي لا تملك أقواس بداية و نهاية{ }
.الدالة العادية تحتوي على أقواس البداية و النهاية
{ }
.الـ Abstract Method يجب وضع فاصلة منقوطة
;
في آخرها بدل أقواس البداية و النهاية.الكلاس الذي يرث من كلاس نوعه
abstract
, يجب أن يفعل Override لكل دوالة نوعهاabstract
, أي يجب أن يكتب الـ body لهذه الدوال.
طريقة تعريف دوال نوعها abstract
نكتب فقط الكلمة abstract
بعد Modifier الدالة.
أمثلة
public abstract void print(); public abstract int getId(); public abstract void setId(int id);
إنتبه: عندما تفعل Override لدالة نوعها abstract
, يجب أن لا تعرفها كـ abstract
من جديد.
مثال
•لنفترض أننا قمنا بتعريف دالة إسمها displayMessage()
كـ abstract
.
public abstract void displayMessage();
•لا يجب أن نكتب abstract
عندما تفعل لها Override.
@Override public abstract void displayMessage() { // abstract methods cannot have a body <-- abstract="" pre="">
•لتصحيح هذا الخطأ, نمسح فقط الكلمة
abstract
.@Override public void displayMessage() { // نلاحظ أن التنبيه إختفى }-->
أمثلة شاملة حول التجريد في جافا
وضعنا هنا ثلاثة أمثلة بسيطة و مترابطة تعلمك أساسيات التجريد في جافا .
الآن سنقوم بتعريف كلاس إسمه A
, نوعه abstract
, يملك متغير إسمه x
, و دالة إسمها print()
.
بعدها سنقوم ببناء الكلاس Main
لتجربة الكود.
المثال الأول
public abstract class A { // إذاً لا يمكن إنشاء كائنات منه ,abstract نوعه A الكلاس int x; public void print() { System.out.println("This is just an example."); } }
public class Main { public static void main(String[] args) { A a = new A(); // Incompatible Type: abstraction.A is abstract; cannot be instantiated <-- pre="">
•سنحصل على النتيجة التالية عند التشغيل.
Exception in thread "main" java.lang.RuntimeException:-->
Uncompilable source code - abstraction.A is abstract; cannot be instantiated
سبب الخطأ هنا أننا حاولنا إنشاء كائن من كلاس نوعه abstract
.
الآن سنقوم بإنشاء كلاس جديد إسمه B
, يرث من الكلاس A
.
بداخل الكلاس Main
سننشئ كائن من الكلاس B
لنحصل على الأشياء الموجودة في الكلاس A
.
المثال الثاني
public abstract class A { int x; public void print() { System.out.println("This is just an example."); } }
public class B extends A { // إذاً سيرث كل شيء موجود فيه .A يرث من الكلاس B هنا قلنا أن الكلاس }
public class Main { public static void main(String[] args) { B b = new B(); // B هنا قمنا بإنشاء كائن من الكلاس b.print(); // A من الكلاس B التي ورثها الكلاس print() هنا قمنا باستدعاء الدالة b.x = 10; // A من الكلاس B الذي ورثه الكلاس x هنا قمنا بتغيير قيمة المتغير System.out.println("b.x contain: " + b.x); // x هنا قمنا بعرض قيمة المتغير } }
•سنحصل على النتيجة التالية عند التشغيل.
This is just an example. b.x contain: 10
إذاً بما أننا لا نستطيع إنشاء كائن مباشرةً من الكلاس A
, قمنا بإنشاء الكلاس B
بهدف وراثة الأشياء الموجودة في الكلاس A
, و بعدها أنشأنا كائن من الكلاس B
و تأكدنا أنه يحتوي على جميع الأشياء الموجودة في الكلاس A
.
الآن سنقوم بإضافة دالتين نوعهما abstract
في الكلاس A
, ثم سنفعل لهما Override في الكلاس B
.
بداخل الكلاس Main
سننشئ كائن من الكلاس B
و سنقوم باستدعاء الدوال الجديدة التي قمنا بإضفاتها.
المثال الثالث
public abstract class A { int x; public void print() { System.out.println("This is just an example."); } public abstract void setX(int x); // كل كلاس يرثها Override إذاً يجب أن يفعل لها <-- abstract هنا قمنا بتعريف دالة نوعها public abstract int getX(); // كل كلاس يرثها Override إذاً يجب أن يفعل لها <-- abstract هنا قمنا بتعريف دالة نوعها }
public class B extends A { // abstract لأي دالة سيرثها من النوع Override يجب أن يفعل A يرث من الكلاس B بما أن الكلاس // setX() للدالة Override هنا فعلنا @Override public void setX(int x) { super.x = x; } // getX() للدالة Override هنا فعلنا @Override public int getX() { return super.x; } }
public class Main { public static void main(String[] args) { B b = new B(); // B هنا قمنا بإنشاء كائن من الكلاس b.print(); // A من الكلاس B التي ورثها الكلاس print() هنا قمنا باستدعاء الدالة b.setX(55); // setX() عن طريق الدالة A من الكلاس B الذي ورثه الكلاس x هنا قمنا بتغيير قيمة المتغير System.out.println("b.x contain: " + b.getX()); // getX() عن طريق الدالة x هنا قمنا بعرض قيمة المتغير } }
وضعنا هنا مثال بسيط حول Abstract Class يرث من Abstract Class
•سنحصل على النتيجة التالية عند التشغيل.
This is just an example. b.x contain: 55
لآن سنقوم بتعريف كلاس إسمه A
نوعه abstract
, يملك متغير إسمه x
, و دالة إسمها print1()
نوعها abstract
.
بعدها سنقوم بتعريف كلاس إسمه B
نوعه abstract
يرث من الكلاس A
, يملك دالة إسمها print2()
نوعها abstract
.
بعدها سنقوم بتعريف كلاس إسمه C
, يرث من الكلاس B
.
في الأخير سنقوم ببناء الكلاس Main
لتجربة الكود.
public abstract class A { int x; public abstract void print1(); }
public abstract class B extends A { public abstract void print2(); // print1() أيضاً ورث الدالة B لا تنسى أن الكلاس }
public class C extends B { // B التي ورثها من الكلاس abstract لجميع الدوال التي نوعها Override يجب أن يفعل C الكلاس @Override public void print1() { System.out.println("Class C should override the method print1()"); } @Override public void print2() { System.out.println("Class C should override the method print2()"); } }
public class Main { public static void main(String[] args) { C c = new C(); // C هنا قمنا بإنشاء كائن من الكلاس c.print1(); // Override و فعل لها C التي ورثها الكلاس print1() هنا قمنا باستدعاء الدالة c.print2(); // Override و فعل لها C التي ورثها الكلاس print2() هنا قمنا باستدعاء الدالة } }
•سنحصل على النتيجة التالية عند التشغيل.
Class C should override the method print1() Class C should override the method print2()
وضعنا هنا مثال مهم يعلمك متى تحتاج إلى إنشاء Abstract Class
الآن سنقوم ببناء كلاس إسمه Person
نوعه abstract
, يملك أربعة خصائص نوعهم private
إسمهم name
, gender
, brithday
و isMarried
, و يملك دوال Setter and Getter لهذه الخصائص, و يملك أيضاً دالة إسمها displayInfo()
نوعها abstract
.
بعدها سنقوم بتعريف كلاس إسمه Student
يرث من الكلاس Person
و يملك متغير إضافي إسمه specialization
.
بعدها سنقوم بتعريف كلاس إسمه Employee
يرث من الكلاس Person
و يملك متغير إضافي إسمه workPlace
.
الفكرة هنا أن أي كلاس سيتم إنشاءه لتمثيل إنسان يجب أن يرث من الكلاس Person
الذي يملك الخصائص المشتركة لكل البشر.
في الأخير سنقوم ببناء الكلاس Main
لتجربة الكود.
public abstract class Person { // هنا قمنا بتعريف الخصائص المشتركة لدى جميع البشر private String name; private String gender; private String birthday; private boolean isMarried; // هنا قمنا بتعريف الكونستركتور public Person(String n, String g, String b, boolean i) { name = n; gender = g; birthday = b; isMarried = i; } // Getter هنا قمنا بتعريف دوال الـ public String getName() { return name; } public String getGender() { return gender; } public String getBirthday() { return birthday; } public boolean getIsMarried() { return isMarried; } // Setter هنا قمنا بتعريف دوال الـ public void setName(String n) { name = n; } public void setGender(String g) { gender = g; } public void setBirthday(String b) { birthday = b; } public void setIsMarried(boolean i) { isMarried = i; } // abstract و التي نوعها displayInfo() هنا قمنا بتعريف الدالة public abstract void displayInfo(); }
public class Student extends Person { String specialization; // هنا قمنا بتعريف خاصية التخصص و التي يملكها فقط التلاميذ // هنا قمنا بتعريف الكونستركتور public Student(String n, String g, String b, boolean i, String s) { super(n, g, b, i); // Person هنا سيتم إرسال أول أربع قيم إلى كونستركتور الكلاس specialization = s; } // بشكل ملائم للتلامذة displayInfo() للدالة Override هنا فعلنا @Override public void displayInfo() { System.out.println("Name: " + getName()); System.out.println("Gender: " + getGender()); System.out.println("Birthday: " + getBirthday()); System.out.println("Specialization: " + specialization); System.out.println("---------------------------------"); } }
public class Employee extends Person { String workPlace; // هنا قمنا بتعريف خاصية مكان العمل و التي يملكها فقط الموظفون و العمال // هنا قمنا بتعريف الكونستركتور public Employee(String n, String g, String b, boolean i, String w) { super(n, g, b, i); // Person هنا سيتم إرسال أول أربع قيم إلى كونستركتور الكلاس workPlace = w; } // بشكل ملائم للموظفين أو العمال displayInfo() للدالة Override هنا فعلنا @Override public void displayInfo() { System.out.println("Name: " + getName()); System.out.println("Gender: " + getGender()); System.out.println("Birthday: " + getBirthday()); if(getIsMarried() == true) { System.out.println("is Married: yes"); } else { System.out.println("is Married: no"); } System.out.println("Work place: " + workPlace); System.out.println("---------------------------------"); } }
public class Main { public static void main(String[] args) { // و هو عبارة عن إنسان له خصائص طالب Student هنا قمنا بإنشاء كائن من الكلاس Student s = new Student("Mhamad", "Male", "1994", false, "Computer Science"); s.displayInfo(); // و هو عبارة عن إنسان له خصائص موظف أو عامل Employee هنا قمنا بإنشاء كائن من الكلاس Employee e = new Employee("Rana", "Female", "1986", true, "Al-Iman school"); e.displayInfo(); } }
•سنحصل على النتيجة التالية عند التشغيل.
Name: Mhamad Gender: Male Birthday: 1994 Specialization: Computer Science --------------------------------- Name: Rana Gender: Female Birthday: 1986 is Married: yes Work place: Al-Iman school ---------------------------------
سترى فائدة الـ Abstraction أيضاً في دروس متقدمة عندما تستخدم كلاسات جاهزة تتيح لك التعامل مع الشبكات (Networks), الواجهات (GUI) و قواعد البيانات (DataBases) بكل سهولة. كما أنك ستراها عندما تعمل على بناء مشاريع كبيرة, تجبرك على إستخدام هذا الأسلوب لتسهيل العمل في المشروع.