نمط التصميم هو قالب يحل مشكلة شائعة في تصميم البرامج.
نمط الحالة هو نمط سلوكي يسمح للكائن بتغيير سلوكه عندما تتغير حالته الداخلية.
هنا سوف تتعلم كيفية استخدام نمط الحالة في TypeScript.
ما هو نمط الدولة؟
يرتبط نمط تصميم الحالة ارتباطًا وثيقًا بآلة الحالة المحدودة ، التي تصف برنامجًا موجودًا في ملف محدود عدد الحالات في أي لحظة وتتصرف بشكل مختلف داخل كل ولاية.
هناك قواعد - انتقالات - محدودة ومحددة مسبقًا تحكم الحالات الأخرى التي قد تتحول إليها كل ولاية.
للسياق ، في متجر عبر الإنترنت ، إذا تم "تسليم" طلب التسوق الخاص بالعميل ، فلا يمكن "إلغاؤه" لأنه "تم تسليمه" بالفعل. "تم التسليم" و "الملغي" هما حالتان محدودتان من النظام ، وسوف يتصرف الأمر بشكل مختلف بناءً على حالته.
نمط الدولة يخلق فئة لكل حالة ممكنة ، مع وجود سلوك خاص بالدولة في كل فئة.
مثال على تطبيق قائم على الدولة
على سبيل المثال ، افترض أنك تقوم بإنشاء تطبيق يتتبع حالات مقال لشركة نشر. يمكن أن تكون المقالة إما قيد الموافقة المعلقة ، أو تمت صياغتها بواسطة كاتب ، أو تحريرها بواسطة محرر ، أو نشرها. هذه هي الحالات المحدودة للمقال الذي سيتم نشره ؛ داخل كل حالة فريدة ، تتصرف المقالة بشكل مختلف.
يمكنك تصور الحالات والانتقالات المختلفة لتطبيق المقالة باستخدام مخطط الحالة أدناه:
عند تنفيذ هذا السيناريو في الكود ، عليك أولاً أن تعلن عن واجهة للمقال:
واجهه المستخدمالمادة واجهة{
يقذف(): فارغ;
مسودة(): فارغ;
يحرر(): فارغ;
ينشر(): فارغ;
}
ستحتوي هذه الواجهة على جميع الحالات الممكنة للتطبيق.
بعد ذلك ، أنشئ تطبيقًا ينفذ جميع طرق الواجهة:
// طلب
فصلشرطالأدواتالمادة واجهة{
البناء() {
هذا.showCurrentState () ،
}خاصshowCurrentState(): فارغ{
//...
}عاميقذف(): فارغ{
//...
}عاممسودة(): فارغ{
//...
}عاميحرر(): فارغ{
//...
}
عامينشر(): فارغ{
//...
}
}
الخاص showCurrentState الطريقة هي طريقة المنفعة. يستخدمه هذا البرنامج التعليمي لإظهار ما يحدث في كل ولاية. إنه ليس جزءًا مطلوبًا من نمط الدولة.
معالجة انتقالات الحالة
بعد ذلك ، ستحتاج إلى التعامل مع انتقالات الحالة. قد يتطلب التعامل مع انتقال الحالة في فئة التطبيق الخاصة بك الكثير عبارات شرطية. قد ينتج عن هذا رمز متكرر يصعب قراءته والحفاظ عليه. لحل هذه المشكلة ، يمكنك تفويض منطق الانتقال لكل ولاية إلى فئتها الخاصة.
قبل كتابة كل فئة حالة ، يجب عليك إنشاء فئة أساسية مجردة للتأكد من أن أي طريقة يتم استدعاؤها في حالة غير صالحة تؤدي إلى حدوث خطأ.
على سبيل المثال:
خلاصةفصلالمادةالأدواتالمادة واجهة{
الملعب (): ArticleState {
يرميجديدخطأ("عملية غير صالحة: لا يمكن تنفيذ المهمة في الوضع الحالي")؛
}مسودة (): ArticleState {
يرميجديدخطأ("عملية غير صالحة: لا يمكن تنفيذ المهمة في الوضع الحالي")؛
}تحرير (): ArticleState {
يرميجديدخطأ("عملية غير صالحة: لا يمكن تنفيذ المهمة في الوضع الحالي")؛
}
نشر (): ArticleState {
يرميجديدخطأ("عملية غير صالحة: لا يمكن تنفيذ المهمة في الوضع الحالي")؛
}
}
في الفئة الأساسية أعلاه ، تلقي كل طريقة خطأ. الآن ، عليك تجاوز كل طريقة عن طريق إنشاء فئات محددة يمتد الطبقة الأساسية لكل ولاية. ستحتوي كل فئة محددة على منطق خاص بالحالة.
يحتوي كل تطبيق على حالة خمول ، والتي تقوم بتهيئة التطبيق. ستعمل حالة الخمول لهذا التطبيق على تعيين التطبيق إلى مسودة ولاية.
على سبيل المثال:
فصلPendingDraftStateيمتدالمادة{
الملعب (): ArticleState {
يعودجديد مشروع حالة () ؛
}
}
ال يقذف الطريقة في الفئة أعلاه تهيئة التطبيق عن طريق تعيين الحالة الحالية إلى حالة المسودة.
بعد ذلك ، تجاوز باقي الطرق مثل:
فصلحالة المسودةيمتدالمادة{
مسودة (): ArticleState {
يعودجديد EditingState ()؛
}
}
هذا الرمز يلغي مسودة وإرجاع مثيل لـ دولة التحرير.
فصلدولة التحريريمتدالمادة{
تحرير (): ArticleState {
يعودجديد PublishedState () ؛
}
}
يتجاوز مقطع التعليمات البرمجية أعلاه ملف يحرر وإرجاع مثيل نشرت الدولة.
فصلنشرت الدولةيمتدالمادة{
نشر (): ArticleState {
يعودجديد PendingDraftState () ،
}
}
يتجاوز مقطع التعليمات البرمجية أعلاه ملف ينشر الأسلوب ويعيد التطبيق إلى حالة الخمول ، PendingDraftState.
بعد ذلك ، تحتاج إلى السماح للتطبيق بتغيير حالته داخليًا من خلال الرجوع إلى الحالة الحالية من خلال متغير خاص. يمكنك القيام بذلك عن طريق تهيئة حالة الخمول داخل فئة التطبيق وتخزين القيمة في متغير خاص:
خاص الحالة: ArticleState = جديد PendingDraftState () ،
بعد ذلك ، قم بتحديث ملف showCurrentState طريقة لطباعة قيمة الحالة الحالية:
خاصshowCurrentState(): فارغ{
وحدة التحكم.سجل(هذا.ولاية)؛
}
ال showCurrentState الأسلوب يسجل الحالة الحالية للتطبيق إلى وحدة التحكم.
أخيرًا ، أعد تعيين المتغير الخاص إلى مثيل الحالة الحالية في كل من طرق تطبيقك.
على سبيل المثال ، قم بتحديث تطبيقاتك يقذف طريقة كتلة التعليمات البرمجية أدناه:
عاميقذف(): فارغ{
هذا.state = هذا.state.pitch () ؛
هذا.showCurrentState () ،
}
في كتلة التعليمات البرمجية أعلاه ، ملف يقذف طريقة تغيير الحالة من الحالة الحالية إلى حالة الملعب.
وبالمثل ، ستغير جميع الطرق الأخرى الحالة من حالة التطبيق الحالية إلى حالة كل منها.
قم بتحديث طرق التطبيق الخاصة بك إلى كتل التعليمات البرمجية أدناه:
ال مسودة طريقة:
عاممسودة(): فارغ{
هذا.state = هذا.state.draft () ؛
هذا.showCurrentState () ،
}
ال يحرر طريقة:
عاميحرر(): فارغ{
هذا.state = هذا.state.edit () ؛
هذا.showCurrentState () ،
}
و ال ينشر طريقة:
عامينشر(): فارغ{
هذا.state = هذا.state.publish () ؛
هذا.showCurrentState () ،
}
باستخدام التطبيق النهائي
يجب أن تكون فئة التطبيق المكتملة مشابهة لمجموعة التعليمات البرمجية أدناه:
// طلب
فصلشرطالأدواتالمادة واجهة{
خاص الحالة: ArticleState = جديد PendingDraftState () ،البناء() {
هذا.showCurrentState () ،
}خاصshowCurrentState(): فارغ{
وحدة التحكم.سجل(هذا.ولاية)؛
}عاميقذف(): فارغ{
هذا.state = هذا.state.pitch () ؛
هذا.showCurrentState () ،
}عاممسودة(): فارغ{
هذا.state = هذا.state.draft () ؛
هذا.showCurrentState () ،
}عاميحرر(): فارغ{
هذا.state = هذا.state.edit () ؛
هذا.showCurrentState () ،
}
عامينشر(): فارغ{
هذا.state = هذا.state.publish () ؛
هذا.showCurrentState () ،
}
}
يمكنك اختبار انتقالات الحالة عن طريق استدعاء الطرق بالتسلسل الصحيح. على سبيل المثال:
مقدار ثابت مستندات = جديد شرط()؛ // PendingDraftState: {}
docs.pitch () ، // DraftState: {}
docs.draft () ؛ // EditingState: {}
docs.edit () ؛ // PublishedState: {}
docs.publish () ، // PendingDraftState: {}
تعمل كتلة التعليمات البرمجية أعلاه لأن حالات التطبيق تم نقلها بشكل مناسب.
إذا حاولت تغيير الحالة بطريقة غير مسموح بها ، على سبيل المثال ، من حالة الملعب إلى حالة التحرير ، فسيقوم التطبيق بإصدار خطأ:
مقدار ثابت مستندات = جديد شرط()؛ // PendingDraftState: {}
docs.pitch () // DraftState: {}
docs.edit () // عملية غير صالحة: لا يمكن تنفيذ المهمة في الحالة الحالية
يجب استخدام هذا النمط فقط في الحالات التالية:
- أنت تقوم بإنشاء كائن يتصرف بشكل مختلف بناءً على حالته الحالية.
- الكائن له حالات كثيرة.
- يتغير السلوك الخاص بالدولة بشكل متكرر.
مزايا ومقايضات نمط الدولة
هذا النمط يلغي العبارات الشرطية الضخمة ويحافظ على المسؤولية الفردية والمبادئ المفتوحة / المغلقة. ولكن يمكن أن يكون الأمر مبالغة إذا كان التطبيق يحتوي على حالات قليلة أو أن حالاته ليست ديناميكية بشكل خاص.