الفرق بين المراجعتين ل"Refactoring/replace type code with subclasses"

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث
ط
 
(4 مراجعات متوسطة بواسطة مستخدمين اثنين آخرين غير معروضة)
سطر 1: سطر 1:
<noinclude>{{DISPLAYTITLE: تبديل رموز الأنواع بالحالة/الاستراتيجية (Replace Type Code with State/Strategy)}}</noinclude>
+
<noinclude>{{DISPLAYTITLE: تبديل رموز الأنواع بالأصناف الفرعية (Replace Type Code with Subclasses)}}</noinclude>
ما هو رمز النوع؟ يحدث رمز النوع عندما يوجد مجموعة من الأرقام أو السلاسل النصية التي تشكل قائمة بالقيم المسموح بها لبعض العناصر بدلًا من استخدام نوع بيانات منفصل. وغالبًا ما تُعطى هذه الأرقام والسلاسل المحددة أسماءً مفهومة عن طريق الثوابت، وهو السبب في استخدام هذه الرموز بشكل كبير.
+
ما هو رمز النوع؟ يحدث رمز النوع عندما يوجد مجموعة من الأرقام أو السلاسل النصية التي تشكل قائمة بالقيم المسموح بها لبعض العناصر بدلًا من استخدام نوع بيانات منفصل. وغالبًا ما تُعطَى هذه الأرقام والسلاسل المحددة أسماءً مفهومة عن طريق الثوابت، وهو السبب في استخدام هذه الرموز بشكل كبير.
  
 
== المشكلة ==
 
== المشكلة ==
يؤثر نوع مُرمَّز على السلوك ولكن لا يمكن استخدام الأصناف الفرعية للتخلص منه.
+
يؤثر النوع المُرمَّز على سلوك البرنامج (تُطلِق قيم هذا الحقل رموز مختلفة في الشرطيات).
[[ملف:Replace Type Code with State-Strategy - Before.png|بدون|تصغير]]
 
  
 
== الحل ==
 
== الحل ==
استبدال رمز النوع بكائن حالة. إذا كان من الضروري استبدال قيمة حقل برمز النوع، يكون كائن حالة آخر "موصولًا" ("plugged-in").
+
إنشاء أصناف فرعية لكل قيمة من النوع المُرمَّز. ثم استخراج السلوكيات ذات الصلة من الصنف الأصلي إلى هذه الأصناف الفرعية. وتبديل رموز التحكم في التدفق بالتعدديّة الشكليّة (polymorphism).
[[ملف:Replace Type Code with State-Strategy - After.png]]
 
  
== لم إعادة التصميم؟ ==
+
== مثال ==
يؤثر رمز النوع على سلوك الصنف، لذلك لا يمكن استخدام [[Refactoring/replace type code with class|تبديل رموز الأنواع بالأصناف]].
+
 
 +
=== قبل إعادة التصميم ===
 +
احتواء الصنف <code>Employee</code> على حقول ذات أنواع مرمزة مثل <code>ENGINEER</code> و <code>SALESMAN</code>.
 +
[[ملف:Replace_Type_Code_with_Subclasses_-_Before.png|بديل=احتواء الصنف Employee على أنواع مرمزة مثل ENGINEER و SALESMAN.|بدون|تصغير|احتواء الصنف Employee على أنواع مرمزة مثل ENGINEER و SALESMAN.]]
  
يؤثر رمز النوع علي سلوك الصنف ولكن لا يمكن إنشاء أصناف فرعية للنوع المُرمَّز بسبب التسلسل الهرمي للصنف الموجود أو لأسباب أخرى. مما يعني أنه لا يمكن أن تطبيق [[Refactoring/replace type code with subclasses|تبديل رموز الأنواع بالأصناف الفرعية]].
+
=== بعد إعادة التصميم ===
 +
إنشاء صنفان فرعيان للحقل <code>Engineer</code> و <code>Salesman</code> ليحل كل منهما مكان النوع المُرمَّز المقابل له في الصنف <code>Employee</code> الأب.
 +
[[ملف:Replace_Type_Code_with_Subclasses_-_After.png|بديل=إنشاء أصناف فرعية للحقل Engineer و Salesman بدلًا من استعمال النوع المُرمَّز لكليهما.|بدون|تصغير|إنشاء أصناف فرعية للحقل Engineer و Salesman بدلًا من استعمال النوع المُرمَّز لكليهما.]]
  
== فوائد تطبيق الحل ==
+
== لم إعادة التصميم؟ ==
* تقنية إعادة التصميم هذه هي طريقة للخروج من الحالات التي يُغيِّر فيها حقل مع نوع مُرمَّز من قيمته طوال عمر الكائن. في هذه الحالة، تُستبدل القيمة عن طريق استبدال كائن الحالة الذي يشير إليه الصنف الأصلي.
+
تُعد تقنية إعادة التصميم هذه تطورًا أكثر تعقيدًا من [[Refactoring/change value to reference|تبديل رموز الأنواع بالأصناف]].
* إذا دعت الحاجة إلى إضافة قيمة جديدة من نوع مُرمَّز، فيجب إضافة صنف حالة فرعي جديد دون تغيير الرمز الموجود (راجع مبدأ مفتوح/مغلق).
 
  
== مساوئ تطبيق الحل ==
+
كما هو الحال مع تابع إعادة التصميم الأول، يوجد مجموعة من القيم البسيطة التي تشكل كافة القيم المسموح بها للحقل. على الرغم من تحديد هذه القيم غالبًا كثوابت ولها أسماء مفهومة، يجعل استخدامها شيفرتك البرمجية أكثر عرضةً للخطأ لأنها لا تزال بدائية في التأثير. فعلى سبيل المثال، إذا وُجِد تابعٌ يقبل إحدى هذه القيم كمُعامل. في لحظة معينة بدلًا من أن يتلقى التابعُ الثابتَ <code>USER_TYPE_ADMIN</code> بالقيمة <code>"ADMIN"</code>، فسيتلقى نفس السلسلة النصية بالحروف الصغيرة (<code>"admin"</code>)، والتي ستسبب تنفيذ شيء آخر لم يقصده المُبرمِج.
إذا وجدت حالة بسيطة من رمز النوع ولكن استخدمت تقنية إعادة التصميم على أية حال، سيكون هناك الكثير من الأصناف الإضافية (غير الضرورية).
 
  
== من الجيد أن نعرف ==
+
نحن نتعامل هنا مع رمز التحكم في التدفق مثل البنى الشرطية <code>if</code> و <code>switch</code> و <code>‎?:</code>‎. وبعبارة أخرى، تُستخدم الحقول ذات القيم المُرمَّزة (مثل ‎<code>$user->type === self::USER_TYPE_ADMIN</code>) داخل شروط هذه العوامل. إذا كان لنا أن نستخدم [[Refactoring/change value to reference|تبديل رموز الأنواع بالأصناف]] هنا، سيكون من الأفضل نقل كل بُنى التحكم في التدفق هذه إلى صنف مسؤول عن نوع البيانات. في نهاية المطاف، سيُنشئ هذا بالطبع صنف نوع مشابهه جدًا للصنف الأصلي، ومع نفس المشاكل أيضًا.
يمكن أن يستخدم تنفيذ تقنية إعادة التصميم واحدًا من نمطي التصميم: الحالة (State) أو الاستراتيجية (Strategy). ويكون التنفيذ متشابهًا بغض النظر عن استخدام أي النمطين. لذا أي نمط يجب اختياره لكل موقف؟
 
  
إذا كنا بصدد محاولة تقسيم الشرط الذي يتحكم في تحديد الخوارزميات، تُستخدم الاستراتيجية.
+
== فوائد تطبيق الحل ==
 +
* حذف رمز تدفق التحكم. نقل الرموز إلى الأصناف الفرعية المناسبة بدلًا من استعمال <code>switch</code> بشكل ضخم في الصنف الأصلي يزيد من الالتزام بمبدأ المسؤولية الفردية (Single Responsibility Principle) ويجعل البرنامج أكثر قابلية للقراءة.
 +
* إذا دعت الحاجة إلى إضافة قيمة جديدة لنوع مُرمَّز، فيجب إضافة صنف فرعي جديد دون المساس بالرمز الحالي (راجع مبدأ مفتوح/مغلق).
 +
* يُسمح بالتلميح بنوع التوابع والحقول على مستوي لغة البرمجة وذلك باستبدال رموز الأنواع بالأصناف. ولن يكون هذا ممكنًا باستخدام القيم الرقمية البسيطة أو السلاسل النصية المضمنة في النوع المُرمَّز.
  
ولكن إذا كانت كل قيمة من النوع المُرمَّز مسؤولة ليس فقط عن تحديد الخوارزمية ولكن عن كامل شرط الصنف، وحالة الصنف، وقيم الحقل، والعديد من الإجراءات الأخرى، ستكون الحالة هي الأفضل لهذه المهمة.
+
== متى يترك هذا الحل؟ ==
 +
* هذه التقنية غير قابلة للتطبيق إذا وُجِدَ بالفعل تسلسل هرمي للصنف. ولا يمكن إنشاء تسلسل هرمي مزدوج من خلال الوراثة في البرمجة الكائنية. ومع ذلك، يمكن استبدال رمز النوع عن طريق التكوين بدلًا من الوراثة. للقيام بذلك، يُستخدم [[Refactoring/replace type code with state strategy|تبديل رموز الأنواع بالحالة/الاستراتيجية]].
 +
* يجب تجنب هذه التقنية إذا كان يمكن تغيير قيم رمز النوع بعد إنشاء الكائن. سيكون علينا تبديل صنف الكائن نفسه بطريقة أو بأخرى بشكل آلي، وهو أمر غير ممكن. ومع ذلك، سيكون [[Refactoring/replace type code with state strategy|تبديل رموز الأنواع بالحالة/الاستراتيجية]] بديلًا في هذه الحالة أيضًا.
  
 
== آلية الحل ==
 
== آلية الحل ==
 
# استخدم [[Refactoring/self encapsulate field|التغليف الداخلي للحقل]] لإنشاء مُتلقي للحقل الذي يحتوي على رمز النوع.
 
# استخدم [[Refactoring/self encapsulate field|التغليف الداخلي للحقل]] لإنشاء مُتلقي للحقل الذي يحتوي على رمز النوع.
# أنشئ صنف جديد واعطه اسم له معنى يتناسب مع الغرض من رمز النوع. سيلعب هذا الصنف دور الحالة (أو الاستراتيجية). أنشئ مُتلقي حقل مُرمَّز مجرد داخله.
+
# اجعل مُنشئ الصنف الأعلى (superclass) خاصًا. ثم أنشئ تابعًا منتجًا (static factory method) ساكن بنفس مُعاملات مُنشئ الصنف الأب. ويجب أن يحتوي على المعامل الذي سيتخذ القيم الابتدائية للنوع المُرمَّز. اعتمادًا على هذا المعامل، سيُنشئ تابعُ التصميم كائنات بأصناف فرعية مختلفة. يجب إنشاء شرطية كبيرة في الشيفرة البرمجية للقيام بذلك، ولكن عندما تكون ضرورية حقًا، ستكون على الأقل هي الوحيدة؛ وإلا ستقوم الأصناف الفرعية والتعددية الشكلية بذلك.
# أنشئ أصناف فرعية لصنف الحالة لكل قيمة من النوع المُرمَّز. في كل صنف فرعي، أعد تعريف المُتلقي للحقل المُرمَّز بحيث تقوم بإعادة القيمة المقابلة من النوع المُرمَّز.
+
# أنشئ صنفًا فرعيًا فريدًا لكل قيمة من النوع المُرمَّز. أعِد تعريف مُتلقي النوع المُرمََّز فيه بحيث يُعيد القيمة المقابلة للنوع المُرمََّز.
# في صنف الحالة المجردة، أنشئ تابع تصميم ساكن يقبل قيمة النوع المُرمَّز كمعامل. اعتمادًا علي هذا المعامل، سيُنشئ تابعُ التصميم كائنات بحالات مختلفة. لهذا، أنشئ في رمزه شرطية كبيرة؛ فستكون الوحيدة عند اكتمال إعادة تصميم.
+
# احذف الحقل ذا رمز النوع من الصنف الأب. اجعل مُتلقيه مجردًا (abstract).
# غيِّر نوع ترميز الحقل في الصنف الأصلي إلى صنف الحالة. استدعِ تابع حالة التصميم في ضابط الحقل للحصول على كائنات حالة جديدة.
+
# الآن وقد أصبح لديك أصناف فرعية؛ يمكن البدء في نقل الحقول والتوابع من الصنف الأب إلى الأصناف الفرعية المناظرة (باستخدام [[Refactoring/push down field|حقل الدفع إلى أسفل]] و<nowiki/>[[Refactoring/push down method|تابع الدفع إلى أسفل]]).
# يمكن الآن البدء في نقل الحقول والتوابع من الصنف الفائق إلى الأصناف الفرعية المناظرة للحالة (باستخدام [[Refactoring/push down field|حقل الدفع إلى أسفل]] و<nowiki/>[[Refactoring/push down method|تابع الدفع إلى أسفل]]).
+
# عند نقل كل ما يمكن نقله، استخدم [[Refactoring/replace conditional with polymorphism|تبديل الشرطيات بالتعدديّة الشكليّة]] من أجل التخلص من الشروط التي تستخدم رمز النوع مرةً واحدةً وإلى الأبد.
# عند نقل كل ما يمكن نقله، استخدم [[Refactoring/replace conditional with polymorphism|تبديل الشرطيات بالتعدديّة الشكليّة]] من أجل التخلص من الشرطيات التي تستخدم رمز النوع مرة واحدة وإلى الأبد.
 
  
 
== انظر أيضًا ==
 
== انظر أيضًا ==
* [[Refactoring/replace type code with class|تبديل رموز الأنواع بالأصناف]].
+
* [[Refactoring/change value to reference|تبديل رموز الأنواع بالأصناف]].
* [[Refactoring/replace type code with subclasses|تبديل رموز الأنواع بالأصناف الفرعية]].
+
* [[Refactoring/replace type code with state strategy|تبديل رموز الأنواع بالحالة/الاستراتيجية]].
* [[Refactoring/self encapsulate field|التغليف الداخلي للحقل]].
+
 
* [[Refactoring/push down field|حقل الدفع إلى أسفل]].
+
*[[Refactoring/push down field|حقل الدفع إلى أسفل]].
* [[Refactoring/push down method|تابع الدفع إلى أسفل]].
+
*[[Refactoring/push down method|تابع الدفع إلى أسفل]].
* [[Refactoring/replace conditional with polymorphism|تبديل الشرطيات بالتعدديّة الشكليّة]].
+
*[[Refactoring/replace conditional with polymorphism|تبديل الشرطيات بالتعدديّة الشكليّة]].
  
 
== مصادر ==
 
== مصادر ==
* [https://refactoring.guru/replace-type-code-with-state-strategy صفحة توثيق رموز الأنواع بالحالة/الاستراتيجية في موقع refactoring.guru]. [[تصنيف:Refactoring]] [[تصنيف:Refactoring Techniques]] [[تصنيف:Refactoring Organizing Data]]
+
* [https://refactoring.guru/replace-type-code-with-subclasses صفحة توثيق تبديل رموز الأنواع بالأصناف الفرعية في موقع refactoring.guru].
 +
[[تصنيف:Refactoring]]
 +
[[تصنيف:Refactoring Techniques]]
 +
[[تصنيف:Refactoring Organizing Data]]

المراجعة الحالية بتاريخ 21:04، 28 يناير 2019

ما هو رمز النوع؟ يحدث رمز النوع عندما يوجد مجموعة من الأرقام أو السلاسل النصية التي تشكل قائمة بالقيم المسموح بها لبعض العناصر بدلًا من استخدام نوع بيانات منفصل. وغالبًا ما تُعطَى هذه الأرقام والسلاسل المحددة أسماءً مفهومة عن طريق الثوابت، وهو السبب في استخدام هذه الرموز بشكل كبير.

المشكلة

يؤثر النوع المُرمَّز على سلوك البرنامج (تُطلِق قيم هذا الحقل رموز مختلفة في الشرطيات).

الحل

إنشاء أصناف فرعية لكل قيمة من النوع المُرمَّز. ثم استخراج السلوكيات ذات الصلة من الصنف الأصلي إلى هذه الأصناف الفرعية. وتبديل رموز التحكم في التدفق بالتعدديّة الشكليّة (polymorphism).

مثال

قبل إعادة التصميم

احتواء الصنف Employee على حقول ذات أنواع مرمزة مثل ENGINEER و SALESMAN.

احتواء الصنف Employee على أنواع مرمزة مثل ENGINEER و SALESMAN.
احتواء الصنف Employee على أنواع مرمزة مثل ENGINEER و SALESMAN.

بعد إعادة التصميم

إنشاء صنفان فرعيان للحقل Engineer و Salesman ليحل كل منهما مكان النوع المُرمَّز المقابل له في الصنف Employee الأب.

إنشاء أصناف فرعية للحقل Engineer و Salesman بدلًا من استعمال النوع المُرمَّز لكليهما.
إنشاء أصناف فرعية للحقل Engineer و Salesman بدلًا من استعمال النوع المُرمَّز لكليهما.

لم إعادة التصميم؟

تُعد تقنية إعادة التصميم هذه تطورًا أكثر تعقيدًا من تبديل رموز الأنواع بالأصناف.

كما هو الحال مع تابع إعادة التصميم الأول، يوجد مجموعة من القيم البسيطة التي تشكل كافة القيم المسموح بها للحقل. على الرغم من تحديد هذه القيم غالبًا كثوابت ولها أسماء مفهومة، يجعل استخدامها شيفرتك البرمجية أكثر عرضةً للخطأ لأنها لا تزال بدائية في التأثير. فعلى سبيل المثال، إذا وُجِد تابعٌ يقبل إحدى هذه القيم كمُعامل. في لحظة معينة بدلًا من أن يتلقى التابعُ الثابتَ USER_TYPE_ADMIN بالقيمة "ADMIN"، فسيتلقى نفس السلسلة النصية بالحروف الصغيرة ("admin")، والتي ستسبب تنفيذ شيء آخر لم يقصده المُبرمِج.

نحن نتعامل هنا مع رمز التحكم في التدفق مثل البنى الشرطية if و switch و ‎?:‎. وبعبارة أخرى، تُستخدم الحقول ذات القيم المُرمَّزة (مثل ‎$user->type === self::USER_TYPE_ADMIN) داخل شروط هذه العوامل. إذا كان لنا أن نستخدم تبديل رموز الأنواع بالأصناف هنا، سيكون من الأفضل نقل كل بُنى التحكم في التدفق هذه إلى صنف مسؤول عن نوع البيانات. في نهاية المطاف، سيُنشئ هذا بالطبع صنف نوع مشابهه جدًا للصنف الأصلي، ومع نفس المشاكل أيضًا.

فوائد تطبيق الحل

  • حذف رمز تدفق التحكم. نقل الرموز إلى الأصناف الفرعية المناسبة بدلًا من استعمال switch بشكل ضخم في الصنف الأصلي يزيد من الالتزام بمبدأ المسؤولية الفردية (Single Responsibility Principle) ويجعل البرنامج أكثر قابلية للقراءة.
  • إذا دعت الحاجة إلى إضافة قيمة جديدة لنوع مُرمَّز، فيجب إضافة صنف فرعي جديد دون المساس بالرمز الحالي (راجع مبدأ مفتوح/مغلق).
  • يُسمح بالتلميح بنوع التوابع والحقول على مستوي لغة البرمجة وذلك باستبدال رموز الأنواع بالأصناف. ولن يكون هذا ممكنًا باستخدام القيم الرقمية البسيطة أو السلاسل النصية المضمنة في النوع المُرمَّز.

متى يترك هذا الحل؟

  • هذه التقنية غير قابلة للتطبيق إذا وُجِدَ بالفعل تسلسل هرمي للصنف. ولا يمكن إنشاء تسلسل هرمي مزدوج من خلال الوراثة في البرمجة الكائنية. ومع ذلك، يمكن استبدال رمز النوع عن طريق التكوين بدلًا من الوراثة. للقيام بذلك، يُستخدم تبديل رموز الأنواع بالحالة/الاستراتيجية.
  • يجب تجنب هذه التقنية إذا كان يمكن تغيير قيم رمز النوع بعد إنشاء الكائن. سيكون علينا تبديل صنف الكائن نفسه بطريقة أو بأخرى بشكل آلي، وهو أمر غير ممكن. ومع ذلك، سيكون تبديل رموز الأنواع بالحالة/الاستراتيجية بديلًا في هذه الحالة أيضًا.

آلية الحل

  1. استخدم التغليف الداخلي للحقل لإنشاء مُتلقي للحقل الذي يحتوي على رمز النوع.
  2. اجعل مُنشئ الصنف الأعلى (superclass) خاصًا. ثم أنشئ تابعًا منتجًا (static factory method) ساكن بنفس مُعاملات مُنشئ الصنف الأب. ويجب أن يحتوي على المعامل الذي سيتخذ القيم الابتدائية للنوع المُرمَّز. اعتمادًا على هذا المعامل، سيُنشئ تابعُ التصميم كائنات بأصناف فرعية مختلفة. يجب إنشاء شرطية كبيرة في الشيفرة البرمجية للقيام بذلك، ولكن عندما تكون ضرورية حقًا، ستكون على الأقل هي الوحيدة؛ وإلا ستقوم الأصناف الفرعية والتعددية الشكلية بذلك.
  3. أنشئ صنفًا فرعيًا فريدًا لكل قيمة من النوع المُرمَّز. أعِد تعريف مُتلقي النوع المُرمََّز فيه بحيث يُعيد القيمة المقابلة للنوع المُرمََّز.
  4. احذف الحقل ذا رمز النوع من الصنف الأب. اجعل مُتلقيه مجردًا (abstract).
  5. الآن وقد أصبح لديك أصناف فرعية؛ يمكن البدء في نقل الحقول والتوابع من الصنف الأب إلى الأصناف الفرعية المناظرة (باستخدام حقل الدفع إلى أسفل وتابع الدفع إلى أسفل).
  6. عند نقل كل ما يمكن نقله، استخدم تبديل الشرطيات بالتعدديّة الشكليّة من أجل التخلص من الشروط التي تستخدم رمز النوع مرةً واحدةً وإلى الأبد.

انظر أيضًا

مصادر