תבנית המצב
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Python של Coddy. שיעור 53 מתוך 64.
תבנית המצב מאפשרת לאובייקט לשנות את התנהגותו כאשר מצבו הפנימי משתנה. נראה שהמחלקה של האובייקט משתנה בהתאם למצבו הנוכחי.
הנה מחלקות מצב פשוטות עבור רמזור:
class RedState:
def next_state(self, light):
print("Red -> Green")
light.state = GreenState()
def current_color(self):
return "Red"
class GreenState:
def next_state(self, light):
print("Green -> Yellow")
light.state = YellowState()
def current_color(self):
return "Green"
class YellowState:
def next_state(self, light):
print("Yellow -> Red")
light.state = RedState()
def current_color(self):
return "Yellow"כל מצב מגדיר מה קורה בעת מעבר למצב הבא.
צרו מחלקת הקשר שמחזיקה את המצב הנוכחי:
class TrafficLight:
def __init__(self):
self.state = RedState() # מתחילים באדום
def change(self):
self.state.next_state(self)
def get_color(self):
return self.state.current_color()הרמזור מאציל את ההתנהגות לאובייקט המצב הנוכחי שלו.
השתמשו ברמזור:
light = TrafficLight()
print(f"Current: {light.get_color()}")
light.change() # אדום -> ירוק
print(f"Current: {light.get_color()}")
light.change() # ירוק -> צהוב
print(f"Current: {light.get_color()}")
light.change() # צהוב -> אדום
print(f"Current: {light.get_color()}")צרו דוגמה נוספת עם נגן פשוט:
class PlayingState:
def play(self, player):
print("Already playing")
def stop(self, player):
print("Stopping music")
player.state = StoppedState()
class StoppedState:
def play(self, player):
print("Starting music")
player.state = PlayingState()
def stop(self, player):
print("Already stopped")
class MusicPlayer:
def __init__(self):
self.state = StoppedState()
def play(self):
self.state.play(self)
def stop(self):
self.state.stop(self)
player = MusicPlayer()
player.play() # מתחיל לנגן מוזיקה
player.play() # כבר מנגן
player.stop() # מפסיק לנגן מוזיקה
player.stop() # הניגון כבר הופסקפלט:
Current: Red
Red -> Green
Current: Green
Green -> Yellow
Current: Yellow
Yellow -> Red
Current: Red
Starting music
Already playing
Stopping music
Already stoppedנקודה מרכזית: תבנית State עוטפת התנהגות ייחודית למצב במחלקות נפרדות ומאפשרת לאובייקט ההקשר להעביר את האחריות למצב הנוכחי. כאשר המצב משתנה, ההתנהגות משתנה אוטומטית. כך נמנעים ממשפטי if/else מורכבים, והוספת מצבים חדשים נעשית קלה יותר.
אתגר
קלממשו את תבנית State בקובץ state.py עבור מנורה עם כפתור אחד. כל לחיצה מעבירה את המנורה בין שלושה מצבים במחזור: כבויה, עוצמה נמוכה, עוצמה גבוהה, וחזרה למצב כבוי. במקום שרשרת של if על מחרוזת, כל מצב הוא אובייקט בפני עצמו שיודע מה קורה כשלוחצים על הכפתור.
LampStateהיא מחלקת הבסיס עםpress(lamp)ו-describe().OffState,LowStateו-HighStateמממשות אותן:pressקוראת ל-lamp.set_state(...)עם המצב הבא (מכבוי לעוצמה נמוכה, מעוצמה נמוכה לעוצמה גבוהה, ומעוצמה גבוהה לכבוי), ו-describeמחזירהLamp is off,Lamp is on lowאוLamp is on high.Lampהיא ההקשר: היא מתחילה במצבOffState, המתודהset_stateמחליפה את אובייקט המצב, המתודהpress_buttonמעבירה את הפעולה למצב הנוכחי, והמתודהstatusמחזירה את התיאור הנוכחי.
הקובץ driver.py נעול: הוא לוחץ על הכפתור ומדפיס את המצב. שימו לב ש-Lamp אף פעם לא בודקת באיזה מצב היא נמצאת; אובייקטי המצב הם שמחליטים.
נסו בעצמכם
from state import Lamp
# מטפל במקרי בדיקה (אין לשנות)
test_case = input()
lamp = Lamp()
if test_case == "initial_test":
print(lamp.status())
elif test_case == "one_press_test":
lamp.press_button()
print(lamp.status())
elif test_case == "cycle_test":
for _ in range(4):
print(lamp.status())
lamp.press_button()
elif test_case == "many_presses_test":
for _ in range(7):
lamp.press_button()
print(lamp.status())
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות התכנות מונחה העצמים (OOP)
קבצים חיצונייםמבוא ל-OOPמחלקות לעומת אובייקטיםהפרמטר selfמתודותמאפייניםשיטת הבנאי (__init__)חזרה: מחשבון פשוט5פולימורפיזם
בחינה מחדש של דריסת מתודותטיפוסיות ברווזמחלקות ומתודות אבסטרקטיותעיצוב ממשקיםסיכום – מחשבון צורות8מושגים מתקדמים בתכנות מונחה עצמים
קומפוזיציה לעומת הורשהMixinsמתודות סטטיות ומתודות מחלקהדקורטורים למחלקותמנהלי הקשרתרגלו בעצמכם: קומפיילר Python אונליין