تصميم الواجهات
جزء من قسم Object Oriented Programming في رحلة Python على Coddy — الدرس 26 من 64.
تحدد الواجهة (interface) عقداً يجب على الأصناف (classes) اتباعه. في لغة Python، نقوم بإنشاء الواجهات باستخدام الأصناف الأساسية المجردة (abstract base classes) حيث تكون جميع الدوال (methods) مجردة.
استورد وحدة abc:
from abc import ABC, abstractmethodقم بإنشاء واجهة تحتوي على دوال مجردة (abstract methods) فقط:
class Drawable(ABC):
@abstractmethod
def draw(self):
pass
@abstractmethod
def resize(self, width, height):
passيجب أن تكون جميع الدوال (methods) في الواجهة (interface) مجردة (abstract) - فهي تحدد ما يجب على الفئات (classes) المنفذة القيام به، وليس كيفية القيام بذلك.
قم بتنفيذ الواجهة في فئة ملموسة:
class Circle(Drawable):
def __init__(self, radius):
self.radius = radius
def draw(self):
return "Drawing a circle"
def resize(self, width, height):
self.radius = min(width, height) / 2
return f"Resized circle to radius {self.radius}"أنشئ فئة أخرى تُنفذ نفس الواجهة:
class Rectangle(Drawable):
def __init__(self, width, height):
self.width = width
self.height = height
def draw(self):
return "Drawing a rectangle"
def resize(self, width, height):
self.width = width
self.height = height
return f"Resized rectangle to {width}x{height}"استخدم الواجهة بتعدد الأشكال:
shapes = [Circle(5), Rectangle(3, 4)]
for shape in shapes:
print(shape.draw())
print(shape.resize(10, 8))المخرجات:
Drawing a circle
Resized circle to radius 4.0
Drawing a rectangle
Resized rectangle to 10x8يمكنك أيضًا استخدام الواجهات كتلميحات للأنواع:
def render_shape(drawable: Drawable):
return drawable.draw()
circle = Circle(3)
print(render_shape(circle))المخرجات:
Drawing a circleنقطة رئيسية: تحدد الواجهات (Interfaces) ما يجب على الفئات (classes) القيام به، وليس كيفية القيام به. استخدم الفئات الأساسية المجردة (abstract base classes) التي تحتوي فقط على أساليب مجردة (abstract methods) لإنشاء عقود واضحة يجب على الفئات المنفذة اتباعها. يضمن ذلك سلوكاً متسقاً عبر التنفيذات المختلفة.
التحدي
متوسطفي هذا التحدي، ستقوم بتنفيذ نظام مشغل وسائط باستخدام الواجهات (interfaces).
تحتاج إلى تعديل الملفات التالية:
playable.py- قم بتنفيذ الفئات الأساسية المجردة (abstract base classes) (الواجهات)song.py- قم بتنفيذ فئة Songvideo.py- قم بتنفيذ فئة Videomediaplayer.py- قم بتنفيذ فئة MediaPlayer
يحتوي كل ملف على تعليقات TODO مفصلة لتوجيه عملية التنفيذ الخاصة بك. اتبع هذه التعليقات لإنشاء نظام مشغل وسائط كامل مع الوراثة الصحيحة وتنفيذ الواجهات.
ورقة مرجعية
قم بإنشاء واجهات (interfaces) باستخدام الفئات الأساسية المجردة (abstract base classes) مع وحدة abc:
from abc import ABC, abstractmethodقم بتعريف واجهة تحتوي على دوال مجردة (abstract methods) فقط:
class Drawable(ABC):
@abstractmethod
def draw(self):
pass
@abstractmethod
def resize(self, width, height):
passقم بتنفيذ الواجهة في فئات ملموسة (concrete classes):
class Circle(Drawable):
def __init__(self, radius):
self.radius = radius
def draw(self):
return "Drawing a circle"
def resize(self, width, height):
self.radius = min(width, height) / 2
return f"Resized circle to radius {self.radius}"استخدم الواجهات بشكل متعدد الأشكال (polymorphically):
shapes = [Circle(5), Rectangle(3, 4)]
for shape in shapes:
print(shape.draw())
print(shape.resize(10, 8))استخدم الواجهات كتلميحات للأنواع (type hints):
def render_shape(drawable: Drawable):
return drawable.draw()تحدد الواجهات ما يجب على الفئات فعله، وليس كيفية فعله. يجب أن تكون جميع الدوال في الواجهة مجردة لإنشاء عقود واضحة للفئات التي تقوم بالتنفيذ.
جرّب بنفسك
from song import Song
from video import Video
from mediaplayer import MediaPlayer
from playable import Playable, MediaInfo
# معالج حالات الاختبار الشاملة
test_case = input()
if test_case == "default_test":
# حالة الاختبار الافتراضية من المشكلة الأصلية
song = Song("Bohemian Rhapsody", "Queen", 355)
video = Video("Python Tutorial", "1080p", 1800)
player = MediaPlayer()
# اختبار مع الأغنية
player.set_media(song)
print(player.current_media.get_info())
print(player.play())
print(player.pause())
print(player.stop())
print() # سطر فارغ لسهولة القراءة
# اختبار مع الفيديو
player.set_media(video)
print(player.current_media.get_info())
print(player.play())
print(player.pause())
print(player.stop())
elif test_case == "empty_player":
# اختبار مشغل الوسائط بدون تعيين أي وسائط
player = MediaPlayer()
print(player.play()) # يجب أن يطبع "No media set"
print(player.pause()) # يجب أن يطبع "No media set"
print(player.stop()) # يجب أن يطبع "No media set"
elif test_case == "time_formatting":
# اختبار تنسيق الوقت في دالة get_info()
song1 = Song("Short Song", "Artist A", 65) # 1:05
song2 = Song("Long Song", "Artist B", 3661) # 61:01
video1 = Video("Hour Video", "720p", 3600) # 60:00
print(song1.get_info())
print(song2.get_info())
print(video1.get_info())
elif test_case == "interface_compliance":
# اختبار أن Song و Video ينفذان الواجهات بشكل صحيح
song = Song("Test Song", "Test Artist", 180)
video = Video("Test Video", "480p", 240)
# التحقق من تنفيذ الواجهة
print(f"Song implements Playable: {isinstance(song, Playable)}")
print(f"Song implements MediaInfo: {isinstance(song, MediaInfo)}")
print(f"Video implements Playable: {isinstance(video, Playable)}")
print(f"Video implements MediaInfo: {isinstance(video, MediaInfo)}")
# اختبار جميع دوال الواجهة
print(song.play())
print(song.pause())
print(song.stop())
print(song.get_title())
print(song.get_duration())
print(song.get_info())
print(video.play())
print(video.pause())
print(video.stop())
print(video.get_title())
print(video.get_duration())
print(video.get_info())
elif test_case == "polymorphism":
# اختبار سلوك تعدد الأشكال مع قائمة من أنواع وسائط مختلفة
media_list = [
Song("Song 1", "Artist 1", 180),
Video("Video 1", "720p", 300),
Song("Song 2", "Artist 2", 240),
Video("Video 2", "1080p", 420)
]
player = MediaPlayer()
for media in media_list:
player.set_media(media)
print(f"Media: {media.get_title()}")
print(f"Info: {media.get_info()}")
print(f"Play: {player.play()}")
print()
elif test_case == "edge_cases":
# اختبار الحالات الحدية
empty_song = Song("", "", 0)
edge_video = Video("A" * 100, "", -10) # عنوان طويل جداً، دقة فارغة، مدة سالبة
print(f"Empty song info: {empty_song.get_info()}")
print(f"Edge video info: {edge_video.get_info()}")
player = MediaPlayer()
player.set_media(empty_song)
print(player.play())
player.set_media(edge_video)
print(player.play())
elif test_case == "stress_test":
# تنفيذ اختبار ضغط مع العديد من كائنات الوسائط
songs = [Song(f"Song {i}", f"Artist {i}", i * 30) for i in range(1, 101)]
videos = [Video(f"Video {i}", f"{i*10}p", i * 60) for i in range(1, 101)]
player = MediaPlayer()
# اختبار مع جميع الأغاني
for i, song in enumerate(songs):
player.set_media(song)
if i % 10 == 0: # طباعة كل عاشر عنصر فقط لتجنب المخرجات الكثيرة جداً
print(f"Playing song {i+1}: {player.play()}")
# اختبار مع جميع الفيديوهات
for i, video in enumerate(videos):
player.set_media(video)
if i % 10 == 0: # طباعة كل عاشر عنصر فقط لتجنب المخرجات الكثيرة جداً
print(f"Playing video {i+1}: {player.play()}")
print("Stress test completed successfully!")يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات OOP
الملفات الخارجيةمقدمة في OOPClasses مقابل Objectsمعامل selfMethodsAttributesدالة البناء (__init__)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسيةدالة ()superإعادة تعريف الدوال (Method Overriding)الوراثة المتعددةترتيب استدعاء الدوال (Method Resolution Order)مراجعة - هيكلية الموظفين5تعدد الأشكال
مراجعة إعادة تعريف الدوالمفهوم Duck Typingالأصناف والدوال المجردةتصميم الواجهاتملخص - حاسبة الأشكال