في لغة Java، تعد الفئة المجردة والواجهة آليتين تدعمان تعريف الفئة المجردة. وبسبب وجود هاتين الآليتين على وجه التحديد، تم منح Java إمكانات قوية موجهة للكائنات. هناك أوجه تشابه كبيرة بين الفئة المجردة والواجهة من حيث دعم تعريف الفئة المجردة، ويمكن حتى استبدالها ببعضها البعض، لذلك يبدو أن العديد من المطورين يتعاملون بشكل غير رسمي مع اختيار الفئة المجردة والواجهة عند تحديد الفئات المجردة. في الواقع، لا يزال هناك فرق كبير بين الاثنين، بل إن اختيارهما يعكس فهم طبيعة مجال المشكلة وما إذا كان فهم نية التصميم صحيحًا ومعقولًا. ستحلل هذه المقالة الاختلافات بينهما وتحاول تزويد المطورين بأساس للاختيار بين الاثنين.
فهم الطبقات المجردة
يتم استخدام كل من الطبقة المجردة والواجهة لتحديد الفئات المجردة في لغة Java (لم تتم ترجمة الفئة المجردة في هذه المقالة من فئة مجردة، فهي تمثل نصًا مجردًا، ويتم استخدام الفئة المجردة لتحديد الفئات المجردة في لغة Java) أ الطريقة، يرجى الانتباه إلى التمييز)، فما هي الفئة المجردة، وما هي الفوائد التي يمكن أن يجلبها لنا استخدام الفئات المجردة؟
في المفهوم الشيئي، نعلم أن جميع الكائنات ممثلة بفئات، لكن العكس ليس صحيحا. لا يتم استخدام كافة الفئات لوصف الكائنات. إذا كانت الفئة لا تحتوي على معلومات كافية لوصف كائن معين، فإن هذه الفئة تعتبر فئة مجردة. غالبًا ما تُستخدم الفصول المجردة لتمثيل المفاهيم المجردة التي نستمدها من تحليل وتصميم مجالات المشكلات، فهي عبارة عن تجريدات لسلسلة من المفاهيم المحددة التي تبدو مختلفة ولكنها في الأساس متماثلة. على سبيل المثال: إذا قمنا بتطوير برنامج لتحرير الرسومات، سنجد أن هناك بعض المفاهيم المحددة مثل الدوائر والمثلثات في مجال المشكلة، ولكنها جميعها تنتمي إلى مفهوم الشكل في مجال المشكلة، إذا كان موجودا، فهو مفهوم مجرد. هذا على وجه التحديد لأن المفاهيم المجردة لا تحتوي على مفاهيم ملموسة مقابلة في مجال المشكلة، لذلك لا يمكن إنشاء مثيل للفئات المجردة المستخدمة لتمثيل المفاهيم المجردة.
في المجال الكائني، تُستخدم الفئات المجردة بشكل أساسي لإخفاء النوع. يمكننا بناء وصف مجرد لمجموعة ثابتة من السلوكيات، ولكن هذه المجموعة من السلوكيات يمكن أن يكون لها أي عدد من التطبيقات الملموسة الممكنة. هذا الوصف المجرد هو فئة مجردة، وهذه المجموعة من أي تطبيقات ملموسة محتملة يتم تمثيلها من خلال جميع الفئات المشتقة الممكنة. يمكن للوحدات أن تعمل على جسم مجرد. نظرًا لأن الوحدة تعتمد على تجريد ثابت، فلا يمكن تعديلها في نفس الوقت، ويمكن أيضًا توسيع سلوك هذه الوحدة من خلال الاشتقاق من هذا التجريد. يجب على القراء الذين هم على دراية بـ OCP أن يعرفوا أنه من أجل تحقيق OCP (المبدأ المفتوح والمغلق)، وهو أحد المبادئ الأساسية للتصميم الموجه للكائنات، فإن الطبقات المجردة هي المفتاح.
النظر إلى الطبقة المجردة والواجهة من مستوى التعريف النحوي
على المستوى النحوي، توفر لغة Java طرق تعريف مختلفة للفئة المجردة والواجهة، وفيما يلي مثال على تعريف فئة مجردة تسمى Demo لتوضيح هذا الاختلاف.
طريقة تعريف الفئة المجردة التجريبية باستخدام الفئة المجردة هي كما يلي:
عرض فئة مجردة {
طريقة الفراغ المجردة1();
طريقة الفراغ المجردة 2 () ؛
…
}
طريقة تحديد فئة التجريد التجريبي باستخدام الواجهة هي كما يلي:
واجهة تجريبية {
طريقة باطلة 1 ()؛
طريقة باطلة 2 ()؛
…
}
في طريقة الفئة المجردة، يمكن أن يحتوي العرض التوضيحي على أعضاء بيانات خاصين به أو أساليب أعضاء غير مجردة. في طريقة الواجهة، يمكن أن يحتوي العرض التوضيحي فقط على أعضاء بيانات ثابتة لا يمكن تعديلها (أي يجب أن تكون ثابتة نهائية، ولكن البيانات). لم يتم تعريف الأعضاء بشكل عام في الواجهة)، وجميع أساليب الأعضاء مجردة. بمعنى ما، تعد الواجهة شكلاً خاصًا من الفئات المجردة.
من منظور البرمجة، يمكن استخدام كل من الفئة المجردة والواجهة لتنفيذ فكرة "التصميم بموجب العقد". ومع ذلك، لا تزال هناك بعض الاختلافات في الاستخدام المحدد.
بادئ ذي بدء، تمثل الفئة المجردة علاقة الميراث في لغة Java، ولا يمكن للفصل استخدام علاقة الميراث إلا مرة واحدة (لأن Java لا تدعم الميراث المتعدد - ملاحظة النقل). ومع ذلك، يمكن للفئة تنفيذ واجهات متعددة. ربما يكون هذا بمثابة حل وسط من قبل مصممي لغة Java عند النظر في دعم Java للميراث المتعدد.
ثانيًا، في تعريف الفئة المجردة، يمكننا تعيين السلوك الافتراضي للطريقة. ولكن في تعريف الواجهة، لا يمكن أن يكون للطرق سلوك افتراضي، ومن أجل التحايل على هذا التقييد، يجب استخدام المفوضين، لكن هذا سيضيف بعض التعقيد ويسبب في بعض الأحيان الكثير من المتاعب.
هناك مشكلة خطيرة أخرى تتمثل في عدم القدرة على تحديد السلوك الافتراضي في فئة مجردة، وهي أنها قد تسبب مشكلة في الصيانة. لأنه إذا أردت لاحقًا تعديل واجهة الفصل (عادةً ما يتم تمثيلها بفئة أو واجهة مجردة) للتكيف مع المواقف الجديدة (على سبيل المثال، إضافة طرق جديدة أو إضافة معلمات جديدة إلى الأساليب المستخدمة بالفعل)، فسيكون ذلك مزعجًا للغاية قد يستغرق الكثير من الوقت (خاصة إذا كان هناك العديد من الفئات المشتقة). ولكن إذا تم تنفيذ الواجهة من خلال فئة مجردة، فقد تحتاج فقط إلى تعديل السلوك الافتراضي المحدد في الفئة المجردة.
وبالمثل، إذا لم يكن من الممكن تعريف السلوك الافتراضي في فئة مجردة، فسيظهر تنفيذ نفس الطريقة في كل فئة مشتقة من الفئة المجردة، مما ينتهك مبدأ "قاعدة واحدة، مكان واحد"، مما يؤدي إلى تكرار التعليمات البرمجية، وهو ما يضر أيضًا المستقبل. لذلك، كن حذرًا جدًا عند الاختيار بين الفئة المجردة والواجهة.
النظر إلى الطبقة المجردة والواجهة من مستوى مفهوم التصميم
يناقش ما ورد أعلاه بشكل أساسي الفرق بين الفئة المجردة والواجهة من منظور التعريف النحوي والبرمجة، والاختلافات في هذه المستويات منخفضة نسبيًا وغير ضرورية. سيقوم هذا القسم بتحليل الفرق بين الفئة المجردة والواجهة من مستوى آخر: مفاهيم التصميم المنعكسة في الاثنين. ويرى المؤلف أنه فقط من خلال التحليل من هذا المستوى يمكننا فهم جوهر المفهومين.
كما ذكرنا سابقًا، تجسد الفئة المجردة علاقة الميراث في لغة Java. من أجل جعل علاقة الميراث معقولة، يجب أن تكون هناك علاقة "is-a" بين الفئة الأصلية والفئة المشتقة، أي الفئة الأصلية والفئة المشتقة. الطبقة المشتقة لها نفس المفهوم في الأساس يجب أن تكون هي نفسها. ليس هذا هو الحال بالنسبة للواجهات، لا يلزم أن يكون منفذ الواجهة وتعريف الواجهة متسقين من الناحية المفاهيمية، ولكن فقط ينفذان العقد المحدد بواسطة الواجهة. ولتسهيل فهم المناقشة، سيتم توضيح مثال بسيط أدناه.
لنفترض أن هناك مفهومًا مجردًا عن الباب في مجال مشكلتنا، فإن الباب له إجراءان: مفتوح وإغلاق، في هذا الوقت، يمكننا تحديد نوع يمثل المفهوم المجرد من خلال الفئة المجردة أو الواجهة الأساليب هي كما يلي:
استخدم فئة مجردة لتحديد الباب:
باب فئة مجردة {
مجردة باطلة مفتوحة () ؛
إغلاق الفراغ الملخص () ؛
}
تحديد الباب باستخدام طريقة الواجهة:
واجهة الباب {
باطلة مفتوحة ()؛
إغلاق باطل () ؛
}
يمكن لأنواع الأبواب المحددة الأخرى توسيع الباب المحدد باستخدام طريقة الفئة المجردة أو تنفيذ الباب المحدد باستخدام طريقة الواجهة. يبدو أنه لا يوجد فرق كبير بين استخدام الفئة المجردة والواجهة.
إذا كان الباب مطلوبًا الآن أن يكون لديه وظيفة إنذار. كيف يجب أن نصمم بنية الفصل في هذا المثال (في هذا المثال، يتم بشكل أساسي إظهار الفرق في مفاهيم التصميم بين الفئة المجردة والواجهة، وقد تم تبسيط أو تجاهل المشكلات الأخرى غير ذات الصلة)؟ الحلول الممكنة مدرجة أدناه ويتم تحليل هذه الخيارات المختلفة من مستوى مفهوم التصميم.
الحل الأول:
ما عليك سوى إضافة طريقة إنذار إلى تعريف الباب، كما يلي:
باب فئة مجردة {
مجردة باطلة مفتوحة () ؛
إغلاق الفراغ الملخص () ؛
مجردة إنذار باطلة ()؛
}
أو
واجهة الباب {
باطلة مفتوحة ()؛
إغلاق باطل () ؛
إنذار باطل () ؛
}
ثم يتم تعريف وظيفة AlarmDoor مع التنبيه على النحو التالي:
فئة AlarmDoor تمتد الباب {
باطلة مفتوحة (){…}
إغلاق باطل (){…}
إنذار باطل (){…}
}
أو
فئة AlarmDoor تنفذ الباب {
باطلة مفتوحة (){…}
إغلاق باطل (){…}
إنذار باطل (){…}
}
تنتهك هذه الطريقة ISP (مبدأ فصل الواجهة)، وهو مبدأ أساسي في التصميم الموجه للكائنات. في تعريف الباب، يتم خلط طريقة السلوك المتأصلة لمفهوم الباب نفسه مع طريقة السلوك لمفهوم آخر "الإنذار". إحدى المشاكل الناجمة عن ذلك هي أن الوحدات التي تعتمد فقط على مفهوم الباب ستتغير بسبب التغييرات في مفهوم "الإنذار" (على سبيل المثال، تعديل معلمات طريقة الإنذار)، والعكس صحيح.
الحل الثاني:
بما أن الفتح والإغلاق والإنذار ينتميان إلى مفهومين مختلفين، وفقًا لمبدأ مزود خدمة الإنترنت (ISP)، فيجب تعريفهما في فئات مجردة تمثل هذين المفهومين. طرق التعريف هي: يتم تعريف كلا المفهومين باستخدام طريقة الطبقة المجردة؛ ويتم تعريف كلا المفهومين باستخدام طريقة الواجهة؛ ويتم تعريف مفهوم واحد باستخدام طريقة الطبقة المجردة، ويتم تعريف المفهوم الآخر باستخدام طريقة الواجهة.
من الواضح أنه نظرًا لأن لغة Java لا تدعم الوراثة المتعددة، فليس من الممكن تحديد كلا المفهومين باستخدام فئة مجردة. تعتبر الطريقتان الأخيرتان ممكنتين، لكن اختيارهما يعكس فهم جوهر المفهوم في مجال المشكلة وما إذا كان انعكاس نية التصميم صحيحًا ومعقولًا. دعونا نحلل ونشرح واحدًا تلو الآخر.
إذا تم تعريف كلا المفهومين باستخدام طريقة الواجهة، فهذا يعكس مشكلتين: 1. قد لا نفهم مجال المشكلة بشكل واضح هل AlarmDoor هو في الأساس باب أم إنذار؟ 2. إذا لم تكن هناك مشكلة في فهمنا لمجال المشكلة، على سبيل المثال: من خلال تحليل مجال المشكلة، نجد أن AlarmDoor متوافق من الناحية المفاهيمية مع Door، فلن نتمكن من الكشف بشكل صحيح عن هدف التصميم الخاص بنا عند تنفيذه لأن تعريفات هذين المفهومين (كلاهما محددان باستخدام طريقة الواجهة) لا تعكس المعنى أعلاه.
إذا كان فهمنا لمجال المشكلة هو: يعتبر AlarmDoor في الأساس بابًا من الناحية النظرية، وله أيضًا وظيفة التنبيه. كيف يجب أن نصممه وننفذه ليعكس معنانا بوضوح؟ كما ذكرنا سابقًا، تمثل الفئة المجردة علاقة وراثة في لغة Java، وعلاقة الميراث هي في الأساس علاقة "is-a". لذا، بالنسبة لمفهوم الباب، يجب أن نستخدم طريقة الفصل المجرد لتعريفه. بالإضافة إلى ذلك، يحتوي AlarmDoor على وظيفة إنذار، مما يعني أنه يمكنه إكمال السلوك المحدد في مفهوم الإنذار، بحيث يمكن تعريف مفهوم الإنذار من خلال الواجهة. كما هو موضح أدناه:
باب فئة مجردة {
مجردة باطلة مفتوحة () ؛
إغلاق الفراغ الملخص () ؛
}
واجهة التنبيه {
إنذار باطل () ؛
}
فئة باب التنبيه يمتد أدوات الباب إنذار {
باطلة مفتوحة (){…}
إغلاق باطل (){…}
إنذار باطل (){…}
}
يمكن أن تعكس طريقة التنفيذ هذه بشكل أساسي فهمنا لمجال المشكلة وتكشف بشكل صحيح عن نوايانا التصميمية. في الواقع، تمثل الفئة المجردة العلاقة "is-a"، وتمثل الواجهة العلاقة "like-a". ويمكن استخدامها كأساس عند الاختيار مثال: إذا كنا نعتقد أن AlarmDoor هو في الأساس إنذار من حيث المفهوم وله وظيفة الباب، فيجب عكس التعريف أعلاه.
ملخص
1. يمثل الفصل الملخص علاقة وراثة في لغة جافا، ويمكن للفصل استخدام علاقة الوراثة مرة واحدة فقط. ومع ذلك، يمكن للفئة تنفيذ واجهات متعددة.
2. في الفصل المجرد، يمكن أن يكون لديك أعضاء بيانات خاصين بك، ويمكنك أيضًا أن يكون لديك أساليب أعضاء غير مجردة، ولكن في الواجهة، لا يمكن أن يكون لديك سوى أعضاء بيانات ثابتة لا يمكن تعديلها (أي، يجب أن تكون ثابتة نهائية، ولكن في الواجهة لا يتم تعريف أعضاء البيانات بشكل عام)، وجميع أساليب الأعضاء مجردة.
3. تعكس الطبقة المجردة والواجهة مفاهيم التصميم المختلفة. في الواقع، تمثل الفئة المجردة العلاقة "is-a"، وتمثل الواجهة العلاقة "like-a".
4. يجب على الفئات التي تنفذ الفئات والواجهات المجردة أن تنفذ جميع الأساليب فيها. يمكن أن تحتوي الفئات المجردة على أساليب غير مجردة. لا يمكن أن تكون هناك طرق تنفيذ في الواجهة.
5. المتغيرات المحددة في الواجهة هي متغيرات عامة ثابتة بشكل افتراضي، ويجب إعطاء قيمتها الأولية، لذلك لا يمكن إعادة تعريفها في فئة التنفيذ، ولا يمكن تغيير قيمها.
6. المتغيرات في الفئات المجردة صديقة بشكل افتراضي، ويمكن إعادة تعريف قيمها في فئات فرعية أو إعادة تعيينها.
7. الأساليب الموجودة في الواجهة هي من النوع العام والمجرد بشكل افتراضي.
ختاماً
تعد الطبقة المجردة والواجهة طريقتين لتحديد الفئات المجردة في لغة Java، وهما متشابهتان جدًا. ومع ذلك، فإن اختيارها غالبا ما يعكس فهم جوهر المفهوم في مجال المشكلة وما إذا كان انعكاس القصد التصميمي صحيحا ومعقولا، لأنها تعبر عن علاقات مختلفة بين المفاهيم (على الرغم من أنها يمكن أن تحقق جميعها الوظائف المطلوبة). هذا في الواقع نوع من الاستخدام الاصطلاحي للغة وآمل أن يتمكن القراء من فهمه بعناية.