קומפוזיציה לעומת הורשה
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Python של Coddy. שיעור 37 מתוך 64.
תכנות מונחה עצמים מציע שתי גישות עיקריות לשימוש חוזר בקוד: ירושה (יחס מסוג „הוא”) והרכבה (יחס מסוג „יש לו”).
הנה דוגמה לירושה היוצרת קשר של „הוא-סוג-של”:
class Animal:
def __init__(self, name):
self.name = name
def eat(self):
return f"{self.name} is eating"
class Dog(Animal): # Dog "is an" Animal
def bark(self):
return "Woof!"
dog = Dog("Buddy")
print(dog.eat()) # מתודה שעברה בירושה
print(dog.bark()) # מתודה משלוהנה דוגמה לקומפוזיציה שיוצרת קשר מסוג „יש־לו”:
class Engine:
def start(self):
return "Engine started"
def stop(self):
return "Engine stopped"
class Car: # ל־Car "has an" Engine
def __init__(self):
self.engine = Engine() # הרכבה
def start(self):
return self.engine.start()
car = Car()
print(car.start()) # משתמש במנוע המורכבהשוו בין שתי הגישות באמצעות דוגמה מורכבת יותר:
# גישת ההורשה
class Bird:
def move(self):
return "Flying"
class Duck(Bird):
def quack(self):
return "Quack!"
# גישת ההרכבה
class FlyBehavior:
def move(self):
return "Flying"
class SwimBehavior:
def move(self):
return "Swimming"
class VersatileDuck:
def __init__(self):
self.fly_behavior = FlyBehavior()
self.swim_behavior = SwimBehavior()
def fly(self):
return self.fly_behavior.move()
def swim(self):
return self.swim_behavior.move()
def quack(self):
return "Quack!"בדקו את שתי הגישות:
# ירושה
duck1 = Duck()
print(duck1.move()) # עף
print(duck1.quack()) # קוואק!
# הרכבה
duck2 = VersatileDuck()
print(duck2.fly()) # עף
print(duck2.swim()) # שוחה
print(duck2.quack()) # קוואק!פלט:
Buddy is eating
Woof!
Engine started
Flying
Quack!
Flying
Swimming
Quack!הבדלים מרכזיים:
ירושה:
- צימוד הדוק בין מחלקת האב למחלקת הבת
- יחסי „הוא־סוג־של”
- שינויים במחלקת האב משפיעים על כל מחלקות הבת
- מתאים ביותר ליחסים היררכיים אמיתיים
הרכבה:
- צימוד רופף בין אובייקטים
- יחסי „יש לו”
- גמיש יותר — אפשר לשנות את ההתנהגות בזמן ריצה
- קל יותר לבדוק ולשנות
נקודה מרכזית: השתמשו בירושה כשיש קשר אמיתי של „הוא סוג של”. השתמשו בהרכבה כשאתם זקוקים לגמישות ולצימוד רופף. העיקרון „הרכבה על פני ירושה” מציע להעדיף הרכבה ברוב המקרים בזכות הגמישות ויכולת התחזוקה שלה.
אתגר
בינוניבאתגר הזה תיישמו מערכת ספריית מדיה באמצעות דפוסי הורשה והרכבה.
עליכם לערוך את הקבצים הבאים, בהתאם להערות TODO בכל אחד מהם:
media.py,book.py,movie.py,musicalbum.py— למימוש ההורשהmediaitem.py,bookcomposition.py,moviecomposition.py,musicalbumcomposition.py— למימוש ההרכבה
לכל סוג מדיה צריכים להיות:
- מאפיינים של כותרת, יוצר (מחבר/במאי/אמן) ושנה
- מתודת
display_info()מתאימה
נסו בעצמכם
from media import Media
from book import Book
from movie import Movie
from musicalbum import MusicAlbum
from mediaitem import MediaItem
from bookcomposition import BookComposition
from moviecomposition import MovieComposition
from musicalbumcomposition import MusicAlbumComposition
# מטפל מקיף במקרי בדיקה
test_case = input()
def test_inheritance_basic():
book = Book("The Hobbit", "J.R.R. Tolkien", 1937)
movie = Movie("The Matrix", "Wachowski Sisters", 1999)
album = MusicAlbum("Abbey Road", "The Beatles", 1969)
assert book.display_info() == "Book: The Hobbit by J.R.R. Tolkien (1937)"
assert movie.display_info() == "Movie: The Matrix directed by Wachowski Sisters (1999)"
assert album.display_info() == "Music Album: Abbey Road by The Beatles (1969)"
def test_composition_basic():
book_comp = BookComposition("Dune", "Frank Herbert", 1965)
movie_comp = MovieComposition("Inception", "Christopher Nolan", 2010)
album_comp = MusicAlbumComposition("Thriller", "Michael Jackson", 1982)
assert book_comp.display_info() == "Book: Dune by Frank Herbert (1965)"
assert movie_comp.display_info() == "Movie: Inception directed by Christopher Nolan (2010)"
assert album_comp.display_info() == "Music Album: Thriller by Michael Jackson (1982)"
def test_inheritance_relationships():
book = Book("Test Book", "Test Author", 2000)
movie = Movie("Test Movie", "Test Director", 2001)
album = MusicAlbum("Test Album", "Test Artist", 2002)
assert isinstance(book, Book)
assert isinstance(book, Media)
assert isinstance(movie, Movie)
assert isinstance(movie, Media)
assert isinstance(album, MusicAlbum)
assert isinstance(album, Media)
def test_attribute_access():
# בדיקת גישת הירושה
book = Book("Test Book", "Test Author", 2000)
assert book.title == "Test Book"
assert book.creator == "Test Author"
assert book.year == 2000
# בדיקת גישת ההרכבה
book_comp = BookComposition("Test Book", "Test Author", 2000)
assert book_comp.media.title == "Test Book"
assert book_comp.media.creator == "Test Author"
assert book_comp.media.year == 2000
def test_polymorphism():
media_list = [
Book("Book1", "Author1", 2001),
Movie("Movie1", "Director1", 2002),
MusicAlbum("Album1", "Artist1", 2003)
]
assert media_list[0].display_info() == "Book: Book1 by Author1 (2001)"
assert media_list[1].display_info() == "Movie: Movie1 directed by Director1 (2002)"
assert media_list[2].display_info() == "Music Album: Album1 by Artist1 (2003)"
def test_edge_cases():
# מחרוזות ריקות
book = Book("", "", 0)
assert book.display_info() == "Book: by (0)"
# תווים מיוחדים - שגיאת התחביר תוקנה כאן
movie = Movie("Test\"Movie", "Test\\nDirector", -1)
assert movie.display_info() == "Movie: Test\"Movie directed by Test\\nDirector (-1)"
# שנים קיצוניות
album = MusicAlbum("Test Album", "Test Artist", 9999)
assert album.display_info() == "Music Album: Test Album by Test Artist (9999)"
def test_stress():
# יצירת אובייקטים רבים של מדיה
books = [Book(f"Book{i}", f"Author{i}", 2000+i) for i in range(100)]
movies = [Movie(f"Movie{i}", f"Director{i}", 2000+i) for i in range(100)]
albums = [MusicAlbum(f"Album{i}", f"Artist{i}", 2000+i) for i in range(100)]
for i, book in enumerate(books):
assert book.display_info() == f"Book: Book{i} by Author{i} ({2000+i})"
for i, movie in enumerate(movies):
assert movie.display_info() == f"Movie: Movie{i} directed by Director{i} ({2000+i})"
for i, album in enumerate(albums):
assert album.display_info() == f"Music Album: Album{i} by Artist{i} ({2000+i})"
# הרצת הבדיקה המתאימה בהתאם לקלט
if test_case == "default_test" or test_case == "":
test_inheritance_basic()
test_composition_basic()
print("All tests passed!")
elif test_case == "inheritance_test":
test_inheritance_basic()
test_inheritance_relationships()
print("Inheritance tests passed!")
elif test_case == "composition_test":
test_composition_basic()
print("Composition tests passed!")
elif test_case == "polymorphism_test":
test_polymorphism()
print("Polymorphism tests passed!")
elif test_case == "attribute_test":
test_attribute_access()
print("Attribute tests passed!")
elif test_case == "edge_cases":
test_edge_cases()
print("Edge case tests passed!")
elif test_case == "stress_test":
test_stress()
print("Stress tests passed!")השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות התכנות מונחה העצמים (OOP)
קבצים חיצונייםמבוא ל-OOPמחלקות לעומת אובייקטיםהפרמטר selfמתודותמאפייניםשיטת הבנאי (__init__)חזרה: מחשבון פשוט5פולימורפיזם
בחינה מחדש של דריסת מתודותטיפוסיות ברווזמחלקות ומתודות אבסטרקטיותעיצוב ממשקיםסיכום – מחשבון צורות8מושגים מתקדמים בתכנות מונחה עצמים
קומפוזיציה לעומת הורשהMixinsמתודות סטטיות ומתודות מחלקהדקורטורים למחלקותמנהלי הקשרתרגלו בעצמכם: קומפיילר Python אונליין