نمط الحالة
جزء من قسم Object Oriented Programming في رحلة Python على Coddy — الدرس 53 من 64.
يسمح نمط الحالة (State Pattern) للكائن بتغيير سلوكه عندما تتغير حالته الداخلية. يبدو الكائن وكأنه يغير فئته (class) بناءً على حالته الحالية.
إليك فئات حالة بسيطة لإشارة مرور:
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"تحدد كل حالة ما يحدث عند الانتقال إلى الحالة التالية.
أنشئ فئة سياق (context class) تحتفظ بالحالة الحالية:
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 Pattern) بتغليف السلوك الخاص بالحالة في فئات منفصلة ويسمح لكائن السياق (context object) بالتفويض إلى الحالة الحالية. عندما تتغير الحالة، يتغير السلوك تلقائيًا. هذا يلغي جمل if/else المعقدة ويجعل إضافة حالات جديدة أسهل.
التحدي
سهلفي هذا التحدي، ستقوم بتنفيذ فئة State في state.py مع التغليف والأساليب المناسبة. يحتوي الملف على تعليقات TODO مفصلة لإرشادك في التنفيذ خطوة بخطوة.
تم تصميم مجموعة الاختبارات للتحقق من أن تنفيذك يلبي جميع المتطلبات ويتعامل مع الحالات الاستثنائية بشكل صحيح. استخدم نتائج الاختبار لفهم السلوك المتوقع وتحسين الحل الخاص بك.
ورقة مرجعية
يسمح نمط الحالة (State Pattern) للكائن بتغيير سلوكه عندما تتغير حالته الداخلية. يبدو أن الكائن يغير فئته بناءً على حالته الحالية.
قم بإنشاء فئات الحالة التي تحدد السلوك لكل حالة:
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"قم بإنشاء فئة سياق (context class) تحتفظ بالحالة الحالية:
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()}") # الحالي: Red
light.change() # Red -> Green
print(f"Current: {light.get_color()}") # الحالي: Greenيقوم نمط الحالة بتغليف السلوك الخاص بكل حالة في فئات منفصلة ويسمح لكائن السياق بالتفويض إلى الحالة الحالية. هذا يلغي جمل if/else المعقدة ويجعل إضافة حالات جديدة أسهل.
جرّب بنفسك
from state import State
# معالج حالات الاختبار الشامل
test_case = input()
if test_case == "basic_test":
obj = State("Test Name")
obj.display_info()
elif test_case == "validation_test":
obj = State("Validation Test")
print(f"Name: {obj.name}")
elif test_case == "empty_name_test":
obj = State("")
obj.display_info()
print(f"Name property: {obj.name}")
elif test_case == "special_chars_test":
obj = State("Test@#$%")
obj.display_info()
print(f"Name property: {obj.name}")
elif test_case == "long_name_test":
long_name = "A" * 100
obj = State(long_name)
obj.display_info()
print(f"Name length: {len(obj.name)}")
elif test_case == "multiple_states_test":
states = [
State("California"),
State("Texas"),
State("New York"),
State("Florida")
]
for state in states:
state.display_info()
print(f"Name via property: {state.name}")
elif test_case == "attribute_access_test":
obj = State("Access Test")
# الوصول المباشر إلى السمة الخاصة (غير مستحسن)
print(f"Direct access (not recommended): {obj._name}")
# الوصول عبر الخاصية (مستحسن)
print(f"Property access (recommended): {obj.name}")
elif test_case == "property_behavior_test":
obj = State("Original Name")
print(f"Original name: {obj.name}")
# محاولة تعديل الاسم (سيفشل لعدم وجود دالة تعيين setter)
try:
obj.name = "New Name"
except AttributeError as e:
print(f"Error when trying to modify name: {e}")
# إظهار أن الاسم لم يتغير
print(f"Name after modification attempt: {obj.name}")يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات OOP
الملفات الخارجيةمقدمة في OOPClasses مقابل Objectsمعامل selfMethodsAttributesدالة البناء (__init__)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسيةدالة ()superإعادة تعريف الدوال (Method Overriding)الوراثة المتعددةترتيب استدعاء الدوال (Method Resolution Order)مراجعة - هيكلية الموظفين5تعدد الأشكال
مراجعة إعادة تعريف الدوالمفهوم Duck Typingالأصناف والدوال المجردةتصميم الواجهاتملخص - حاسبة الأشكال