Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

نمط الحالة

جزء من قسم Object Oriented Programming في رحلة Dart على Coddy — الدرس 98 من 110.

يسمح State pattern للكائن بتغيير سلوكه عندما تتغير حالته الداخلية، مما يجعله يبدو كما لو أن الكائن قد غير الـ class الخاص به. بدلاً من استخدام الشروط المعقدة للتعامل مع الحالات المختلفة، تقوم بتغليف كل حالة في class خاصة بها وتفويض السلوك إلى كائن الحالة الحالي.

يتكون النمط من Context يحافظ على مرجع للحالة الحالية، وفئات State التي تحدد السلوك لكل حالة ممكنة. عندما تتغير حالة السياق، يتغير سلوكه تلقائيًا:

abstract class DocumentState {
  void publish(Document doc);
  String getStatus();
}

class DraftState implements DocumentState {
  @override
  void publish(Document doc) {
    print('Moving to review...');
    doc.setState(ReviewState());
  }

  @override
  String getStatus() => 'Draft';
}

class ReviewState implements DocumentState {
  @override
  void publish(Document doc) {
    print('Document published!');
    doc.setState(PublishedState());
  }

  @override
  String getStatus() => 'Under Review';
}

class PublishedState implements DocumentState {
  @override
  void publish(Document doc) {
    print('Already published.');
  }

  @override
  String getStatus() => 'Published';
}

class Document {
  DocumentState _state = DraftState();

  void setState(DocumentState state) => _state = state;
  void publish() => _state.publish(this);
  String getStatus() => _state.getStatus();
}

void main() {
  var doc = Document();
  print(doc.getStatus());  // مسودة
  doc.publish();           // الانتقال إلى المراجعة...
  print(doc.getStatus());  // قيد المراجعة
  doc.publish();           // تم نشر المستند!
  print(doc.getStatus());  // منشور
}

تتعامل كل فئة حالة مع الإجراء publish() بشكل مختلف ويمكنها نقل المستند إلى الحالة التالية. لا تحتاج فئة Document إلى جمل شرطية للتحقق من حالتها الحالية - فهي ببساطة تفوض الأمر إلى أي كائن حالة نشط حالياً. هذا النمط مثالي للكائنات ذات الأنماط السلوكية المتميزة، مثل معالجة الطلبات، أو مشغلات الوسائط، أو أنظمة سير العمل.

challenge icon

التحدي

سهل

لنقم ببناء نظام إشارات مرور باستخدام نمط الحالة (State pattern)! ستقوم بإنشاء إشارة مرور تتنقل عبر حالات مختلفة، حيث تحدد كل حالة لون الإشارة وكيفية انتقالها إلى الحالة التالية.

ستقوم بتنظيم الكود الخاص بك في ملفين:

  • traffic_light.dart: يحتوي هذا الملف على فئات الحالة (state classes) وسياق إشارة المرور (traffic light context). قم بإنشاء فئة مجردة (abstract class) باسم TrafficLightState مع طريقتين (methods): String getColor() التي تعيد لون الإشارة الحالي، و void next(TrafficLight light) التي تنقل الإشارة إلى حالتها التالية. قم بتنفيذ ثلاث فئات حالة ملموسة (concrete state classes):
    • RedState - تعيد RED كلون لها وتطبع Stop! Light is red. عند استدعاء next()، ثم تنتقل إلى GreenState
    • GreenState - تعيد GREEN كلون لها وتطبع Go! Light is green. عند استدعاء next()، ثم تنتقل إلى YellowState
    • YellowState - تعيد YELLOW كلون لها وتطبع Caution! Light is yellow. عند استدعاء next()، ثم تنتقل إلى RedState
    ثم قم بإنشاء فئة TrafficLight (السياق) التي تبدأ في الحالة RedState. يجب أن تحتوي على طريقة setState() لتغيير الحالة الحالية، وطريقة getColor() التي تفوض المهمة إلى الحالة الحالية، وطريقة change() التي تستدعي next() في الحالة الحالية.
  • main.dart: قم باستيراد ملف إشارة المرور الخاص بك واستعرض نمط الحالة أثناء العمل. قم بإنشاء TrafficLight واطبع لونها الأولي. ثم استدعِ change() ثلاث مرات، مع طباعة اللون بعد كل تغيير. سيظهر هذا تنقل الإشارة عبر جميع الحالات والعودة إلى البداية.

لاحظ كيف أن فئة TrafficLight لا تحتاج إلى أي جمل شرطية لتحديد السلوك - فكل حالة تعرف لونها الخاص والحالة التي تليها. تقوم إشارة المرور ببساطة بالتفويض إلى أي حالة نشطة حالياً!

المخرجات المتوقعة:

RED
Stop! Light is red.
GREEN
Go! Light is green.
YELLOW
Caution! Light is yellow.
RED

ورقة مرجعية

يسمح نمط الحالة (State pattern) للكائن بتغيير سلوكه عندما تتغير حالته الداخلية. بدلاً من استخدام الشروط المعقدة، يتم تغليف كل حالة في فئة (class) خاصة بها.

يتكون النمط من:

  • السياق (Context): يحتفظ بمرجع للحالة الحالية
  • فئات الحالة (State classes): تحدد السلوك لكل حالة ممكنة

الهيكل الأساسي:

abstract class DocumentState {
  void publish(Document doc);
  String getStatus();
}

class DraftState implements DocumentState {
  @override
  void publish(Document doc) {
    print('Moving to review...');
    doc.setState(ReviewState());
  }

  @override
  String getStatus() => 'Draft';
}

class Document {
  DocumentState _state = DraftState();

  void setState(DocumentState state) => _state = state;
  void publish() => _state.publish(this);
  String getStatus() => _state.getStatus();
}

يقوم السياق بتفويض السلوك إلى كائن الحالة الحالي. تتعامل كل فئة حالة مع الإجراءات بشكل مختلف ويمكنها الانتقال إلى الحالة التالية. هذا يلغي الحاجة إلى الشروط للتحقق من الحالة الحالية.

مثالي للكائنات ذات أوضاع سلوكية متميزة مثل معالجة الطلبات، أو مشغلات الوسائط، أو أنظمة سير العمل.

جرّب بنفسك

import 'traffic_light.dart';

void main() {
  // TODO: إنشاء مثيل من TrafficLight
  
  // TODO: طباعة اللون الأولي
  
  // TODO: استدعاء change() وطباعة اللون (3 مرات للمرور عبر جميع الحالات)
  // التغيير الأول: RED -> GREEN
  
  // التغيير الثاني: GREEN -> YELLOW
  
  // التغيير الثالث: YELLOW -> RED
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming