الألة ذات الحالات المحددة Finite-state machine
هو مفهوم يطبق لبناء أي شيء ينتقل بين عدة حالات
سواء هذا الشيء آلة ميكانيكية أو كهربائية أو أي شيء يخضع لهذا المفهوم
قبل أن أبدأ الفكرة سأوضح هذا المفهوم من خلال أمثلة في حياتنا.
إشارة المرور الضوئية تنتقل بين عدة حالات محددة ( أحمر - برتقالي - أخضر - أخضر وبرتقالي معا (وأحيانا يكون أخضر وامض) )
الإشارة في لحظة ما يجب أن تكون في إحدى هذه الحالات تتنقل بينها وفق تسلسل معين بشروط معينة قد تكون فواصل زمنية أو أحداث معينة
طبعا يوجد حالات استثنائية وهي وضع الإطفاء ويكون نتيجة استثناء مثل عدم وجود كهرباء أو خلل في الدارة
كمثال آخر:
المصعد الكهربائي يكون في عدة حالات كأن يكون متوقف في طابق ما وقد يكون في حالة صعود أو هبوط أو حالة توقف استثنائي نتيجة طاريء
الشيء الذي يخضع لهذا المفهوم نسميه آلة Finite-state machine
وكل حالة من هذه الحالات تدعى حالة State ولكون الحالات محددة من هنا سميت الآلة ذات الحالات المحددة
الآن يوجد شيء اسمه انتقال Transaction وهو عملية الانتقال من حالة حالية إلى حالة جديدة
ومن هنا يمكن تعريف عدد من الانتقالات المسموحة بين حالة وأخرى و في حالة لم يتم تعريف انتقال بين حالة وأخرى فهذا يعني أنه لا يمكن أن تأتي الحالة الثانية بعد الحالة الأولى نهائيا
كمثال :
لا يمكن انتقال المصعد من الهبوط إلى الصعود مباشرة وإنما يجب أن ينتقل إلى إحدى حالات التوقف ومن ثم ينتقل للصعود
كذلك يوجد مفهوم اسمه أحداث الانتقال Trigger بمعنى أنه عند تعريف انتقال بين حالة ما وحالة ثانية يجب تحديد الحدث الذي عندما يحصل يجب تنفيذ عملية الانتقال
كمثال :
لو عندنا مصباح فإن له حالتان مستقرتان إطفاء أو إشعال
و الانتقالات هي اثنتان من الإشعال إلى الإطفاء ومن الإطفاء للإشعال
الآن عندما يكون في إحدى الحالات لا يمكن أن ينتقل إلى الحالة الأخرى إلى إذا حدث أمر ما وهو توصيل التيار الكهربائي من خلال الضغط على زر التشغيل أو أي شيء يوصل التيار
إذا وصول التيار يعتبر حدث للانتقال الأول أي من الإطفاء إلى الإشعال
وانقطاع التيار يعتبر حدث للانتقال من الثانية إلى الأولى
كما يمكن تحديد شروط لتنفيذ الانتقال Condition
مثلا :
عندما يكون المصعد متوقف في الطابق 4 ثم يتم الضغط على زر الصعود من الطابق الأرضي وهنا يكون قد حصل حدث الانتقال من حالة توقف إلى حالة نزول
حينها ينتقل
ولكن هناك شرط أن لا يكون في حالة توقف لتنزيل ركاب وقد طلبه شخص من طابق أعلى
هنا لن يتحقق الانتقال إلى النزول لأنه يوجد أولوية للطلبات ويجب أن ينتقل لحالة صعود أولا
كما يوجد شيء اسمه ToDo وهي مهام يجب تنفيذها بحيث يمكن ربطها بالحدث أو أنك تجعل هذه المهمة مرتبطة بالحالة الآنية أو تربطها بعملية الانتقال
بمعنى أنه إما أن يتم تنفيذ مهمة عند وقوع الحدث أو أن تكون المهمة جزء من الحالة بحيث عندما يتم الانتقال إليها يتم تنفيذها أو يتم تنفيذها مع عملية الانتقال
كمثال :
فتح باب المصعد عندما ينتقل من حالة صعود أو نزول إلى حالة توقف
أو إظهار رقم الطابق كلما وصل إلى طابق ما
أو ربط إشارة المارة بإشارة المرور بحيث عندما تكون حمراء تكون تلك خضراء والعكس بالعكس
بعد كل هذا التوضيح للمفهوم سأتطرق لفكرة وهي تطبيقها في البرمجة.
وسأضرب مثال :
عندما يكون لدينا واجهة إدخال بيانات والتي سنعتبرها هي الآلة Machine
يمكن تحديد حالات State مثل ( عرض - جديد - تعديل )
أما الحذف فلا يعتبر حالة لأنك تقوم بالحذف فإما يحذف أو يتراجع وإن أحببت يمكن جعلها حالة إذا كان سيتعلق بها أمور مرحلية قبل الحذف النهائي.
أما الإنشاء والتعديل فلكونك ستكون في وضع يفتح لك الكونترولات ويسمع بتعديل النصوص ومن ثم تحفظ أو تلغي فيجب أن تكون حالات
الحالة الأولى (عرض) ستكون هي الافتراضية لأنك ستبدأ بها وتعود إليها
الآن نعرف الانتقالات Transaction
من العرض للجديد
ومن العرض للتعديل
من الجديد للعرض
من التعديل للعرض
أما من الجديد للتعديل مباشرة أو العكس فلا يمكن
نحدد الآن أحداث الانتقال Trigger
وهو النقر على زر جديد فيقوم بتنفيذ الانتقال من العرض للجديد
وحدث النقر على زر تعديل فيقوم بتنفيذ الانتقال من حالة العرض إلى حالة التعديل
وحدث الحفظ أو التراجع لينقلنا من الجديد إلى العرض
ونفسهما لينقلنا من التعديل للعرض
سنعرف شروط الانتقال Condition
لا يمكن الانتقال من العرض للتعديل في حال لم يكن هناك سجل محدد
سنعرف مهام مرتبطة بالحالة ToDo
كأن ينقل حالة الكونترولات من ReadOnly=true إلى ReadOnly=false ليسمح بإدخال وتعديل البيانات
و نعرف مهام مرتبطة بالأحداث كأن يقوم بعملية حفظ البيانات في القاعدة عند النقر على Save أو إلغاء العملية عند النقر على زر Cancel
كما يمكن تعريف مهام مرتبطة بالانتقال
كأن تقوم بتحديد السجل الجديد في حال انتقل من الجديد إلى العرض بعد عملية الحفظ
وهكذا
في الحقيقة لقد قمت بتطبيقها عمليا في مشروعي الذي أعمل عليه حاليا وقد قدم لي فائدة عظيمة حيث نظم لي العمل وفصل المهام و قدم حماية من الحالات الخاطئة
مثال استخدمته :
Automator = new Automation(this.GetType().Name)
// Normal // ======================================================================
.AddState(new State(C.Default)).SetAsDefault(() =>
{
//MoveTo(10);
})
.AddEntry(() =>
{
EntityMode = EntityModes.Default;
})
// Create // ======================================================================
.AddState(new State(C.Creation))
.AddEntryConditions(() => CanCreate)
.AddEntry(() =>
{
EntityMode = EntityModes.Creation;
SelectedItemBeforCreation = SelectedItem;
this.ActiveItem = LowerModel.Submit(GKeys.DataExt.GetEntity, Data, this.SelectedItem).Result<IEntity>();
this.SelectedItem = null;
PreviousSelectedIndex = SelectedIndex;
SelectedIndex = MaxIndex + 2;
})
// Edit // ======================================================================
.AddState(new State(C.Edition))
.AddEntryConditions(() => CanEdit)
.AddEntry(() =>
{
EntityMode = EntityModes.Edition;
})
// Delete // ======================================================================
.AddState(new State(C.Deletion))
.AddEntryConditions(() => CanDelete)
.AddEntry(() =>
{
EntityMode = EntityModes.Deletion;
DeleteEntity();
Refresh();
Automator.ReturnToState(C.Default);
})
.Automation
.AddTransaction(new Transaction("Df->C", "Default", "Creation")).AddTriggers(Trigger.FromEntry("Create"))
.AddTransaction(new Transaction("Df->E", "Default", "Edition")).AddTriggers(Trigger.FromEntry("Edit"))
.AddTransaction(new Transaction("Df->D", "Default", "Deletion")).AddTriggers(Trigger.FromEntry("Delete"))
.AddTransaction(new Transaction("C->Df", "Creation", "Default"))
.AddTriggers(Trigger.FromSuccess("Save")
.AddToDo(() =>
{
if(CanSave)
{
Operate2(EntityModes.Creation);
IsDirty = false;
EntityMode = EntityModes.Default;
}
})
,
Trigger.FromEntry("Cancel")
.AddToDo(() =>
{
IsDirty = false;
EntityMode = EntityModes.Default;
//SelectedItem = PreviousSelectedItem;
MoveTo(PreviousSelectedIndex);
}),
Trigger.FromExit("Close"))
.AddTransaction(new Transaction("E->Df", "Edition", "Default"))
.AddTriggers(Trigger.FromSuccess("Save")
.AddToDo(() =>
{
if(CanSave)
{
Operate2(EntityModes.Edition);
IsDirty = false;
EntityMode = EntityModes.Default;
}
})
,
Trigger.FromEntry("Cancel")
.AddToDo(() =>
{
IsDirty = false;
EntityMode = EntityModes.Default;
//SelectedItem = PreviousSelectedItem;
MoveTo(PreviousSelectedIndex);
}),
Trigger.FromExit("Close"))
.Automation
;
أخوكم : عبد الرحمن أحمد
في 09-02-2014
دمتم بحفظ الله