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

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث
ط
ط (مراجعة وتدقيق.)
سطر 1: سطر 1:
 
<noinclude>{{DISPLAYTITLE: تبديل رموز الأنواع بالأصناف الفرعية (Replace Type Code with Subclasses)}}</noinclude>
 
<noinclude>{{DISPLAYTITLE: تبديل رموز الأنواع بالأصناف الفرعية (Replace Type Code with Subclasses)}}</noinclude>
ما هو رمز النوع؟ يحدث رمز النوع عندما يوجد مجموعة من الأرقام أو السلاسل النصية التي تشكل قائمة بالقيم المسموح بها لبعض العناصر بدلًا من استخدام نوع بيانات منفصل. وغالبًا ما تُعطى هذه الأرقام والسلاسل المحددة أسماءً مفهومة عن طريق الثوابت، وهو السبب في استخدام هذه الرموز بشكل كبير.
+
ما هو رمز النوع؟ يحدث رمز النوع عندما يوجد مجموعة من الأرقام أو السلاسل النصية التي تشكل قائمة بالقيم المسموح بها لبعض العناصر بدلًا من استخدام نوع بيانات منفصل. وغالبًا ما تُعطَى هذه الأرقام والسلاسل المحددة أسماءً مفهومة عن طريق الثوابت، وهو السبب في استخدام هذه الرموز بشكل كبير.
  
 
== المشكلة ==
 
== المشكلة ==
سطر 6: سطر 6:
  
 
== الحل ==
 
== الحل ==
إنشاء أصناف فرعية لكل قيمة من النوع المُرمَّز. ثم استخراج السلوكيات ذات الصلة من الصنف الأصلي إلى هذه الأصناف الفرعية. وتبديل رموز التحكم في التدفق بالتعدديّة الشكليّة.
+
إنشاء أصناف فرعية لكل قيمة من النوع المُرمَّز. ثم استخراج السلوكيات ذات الصلة من الصنف الأصلي إلى هذه الأصناف الفرعية. وتبديل رموز التحكم في التدفق بالتعدديّة الشكليّة (polymorphism).
[[ملف:Replace Type Code with Subclasses - After.png]]
 
  
== لم إعادة التصميم؟ ==
+
== مثال ==
 +
 
 +
=== قبل إعادة التصميم ===
 +
احتواء الصنف <code>Employee</code> على حقول ذات أنواع مرمزة مثل <code>ENGINEER</code> و <code>SALESMAN</code>.
 +
[[ملف:Replace_Type_Code_with_Subclasses_-_Before.png|بديل=احتواء الصنف Employee على أنواع مرمزة مثل ENGINEER و SALESMAN.|بدون|تصغير|احتواء الصنف Employee على أنواع مرمزة مثل ENGINEER و SALESMAN.]]
 +
 
 +
=== بعد إعادة التصميم ===
 +
إنشاء صنفان فرعيان للحقل <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|تبديل رموز الأنواع بالأصناف]].
 
تُعد تقنية إعادة التصميم هذه تطورًا أكثر تعقيدًا من [[Refactoring/change value to reference|تبديل رموز الأنواع بالأصناف]].
  
كما هو الحال مع تابع إعادة التصميم الأول، يوجد مجموعة من القيم البسيطة التي تشكل كافة القيم المسموح بها للحقل. علي الرغم من تحديد هذه القيم غالبًا كثوابت ولها أسماء مفهومة، يجعل استخدامها شيفرتك البرمجية أكثر عرضة للخطأ لأنها لا تزال بدائية في التأثير. فعلي سبيل المثال، إذا وُجِد تابع يقبل إحدى هذه القيم كمُعامل. في لحظة معينة، بدلًا من أن يتلقى التابعُ الثابتَ <code>USER_TYPE_ADMIN</code> بالقيمة <code>"ADMIN"</code>، يتلقى نفس السلسلة النصية بالحروف الصغيرة (<code>"admin"</code>)، والتي ستسبب تنفيذ شيء آخر لم يقصده المُبرمِج.
+
كما هو الحال مع تابع إعادة التصميم الأول، يوجد مجموعة من القيم البسيطة التي تشكل كافة القيم المسموح بها للحقل. على الرغم من تحديد هذه القيم غالبًا كثوابت ولها أسماء مفهومة، يجعل استخدامها شيفرتك البرمجية أكثر عرضةً للخطأ لأنها لا تزال بدائية في التأثير. فعلى سبيل المثال، إذا وُجِد تابعٌ يقبل إحدى هذه القيم كمُعامل. في لحظة معينة بدلًا من أن يتلقى التابعُ الثابتَ <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|تبديل رموز الأنواع بالأصناف]] هنا، سيكون من الأفضل نقل كل بُنى التحكم في التدفق هذه إلى صنف مسؤول عن نوع البيانات. في نهاية المطاف، سيُنشئ هذا بالطبع صنف نوع مشابهه جدًا للصنف الأصلي، ومع نفس المشاكل أيضًا.
+
نحن نتعامل هنا مع رمز التحكم في التدفق مثل البنى الشرطية <code>if</code> و <code>switch</code> و <code>‎?:</code>‎. وبعبارة أخرى، تُستخدم الحقول ذات القيم المُرمَّزة (مثل ‎<code>$user->type === self::USER_TYPE_ADMIN</code>) داخل شروط هذه العوامل. إذا كان لنا أن نستخدم [[Refactoring/change value to reference|تبديل رموز الأنواع بالأصناف]] هنا، سيكون من الأفضل نقل كل بُنى التحكم في التدفق هذه إلى صنف مسؤول عن نوع البيانات. في نهاية المطاف، سيُنشئ هذا بالطبع صنف نوع مشابهه جدًا للصنف الأصلي، ومع نفس المشاكل أيضًا.
  
 
== فوائد تطبيق الحل ==
 
== فوائد تطبيق الحل ==
* حذف رمز تدفق التحكم. نقل الرموز إلى الأصناف الفرعية المناسبة بدلًا من switch بشكل ضخم في الصنف الأصلي. الأمر الذي يزيد من الالتزام بمبدأ المسؤولية الفردية (Single Responsibility Principle) ويجعل البرنامج أكثر قابلية للقراءة.
+
* حذف رمز تدفق التحكم. نقل الرموز إلى الأصناف الفرعية المناسبة بدلًا من استعمال <code>switch</code> بشكل ضخم في الصنف الأصلي يزيد من الالتزام بمبدأ المسؤولية الفردية (Single Responsibility Principle) ويجعل البرنامج أكثر قابلية للقراءة.
* إذا دعت الحاجة إلى إضافة قيمة جديدة لنوع مُرمَّز، يجب إضافة صنف فرعي جديد دون المساس بالرمز الحالي (راجع مبدأ مفتوح/مغلق).
+
* إذا دعت الحاجة إلى إضافة قيمة جديدة لنوع مُرمَّز، فيجب إضافة صنف فرعي جديد دون المساس بالرمز الحالي (راجع مبدأ مفتوح/مغلق).
* ويُسمح بالتلميح بنوع التوابع والحقول علي مستوي لغة البرمجة وذلك باستبدال رموز الأنواع بالأصناف. ولن يكون هذا ممكنًا باستخدام القيم الرقمية البسيطة أو السلاسل النصية المضمنة في النوع المُرمَّز.
+
* يُسمح بالتلميح بنوع التوابع والحقول على مستوي لغة البرمجة وذلك باستبدال رموز الأنواع بالأصناف. ولن يكون هذا ممكنًا باستخدام القيم الرقمية البسيطة أو السلاسل النصية المضمنة في النوع المُرمَّز.
  
== متى يُترك هذا الحل؟ ==
+
== متى يترك هذا الحل؟ ==
 
* هذه التقنية غير قابلة للتطبيق إذا وُجِدَ بالفعل تسلسل هرمي للصنف. ولا يمكن إنشاء تسلسل هرمي مزدوج من خلال الوراثة في البرمجة الكائنية. ومع ذلك، يمكن استبدال رمز النوع عن طريق التكوين بدلًا من الوراثة. للقيام بذلك، يُستخدم [[Refactoring/replace type code with state strategy|تبديل رموز الأنواع بالحالة/الاستراتيجية]].
 
* هذه التقنية غير قابلة للتطبيق إذا وُجِدَ بالفعل تسلسل هرمي للصنف. ولا يمكن إنشاء تسلسل هرمي مزدوج من خلال الوراثة في البرمجة الكائنية. ومع ذلك، يمكن استبدال رمز النوع عن طريق التكوين بدلًا من الوراثة. للقيام بذلك، يُستخدم [[Refactoring/replace type code with state strategy|تبديل رموز الأنواع بالحالة/الاستراتيجية]].
 
* يجب تجنب هذه التقنية إذا كان يمكن تغيير قيم رمز النوع بعد إنشاء الكائن. سيكون علينا تبديل صنف الكائن نفسه بطريقة أو بأخرى بشكل آلي، وهو أمر غير ممكن. ومع ذلك، سيكون [[Refactoring/replace type code with state strategy|تبديل رموز الأنواع بالحالة/الاستراتيجية]] بديلًا في هذه الحالة أيضًا.
 
* يجب تجنب هذه التقنية إذا كان يمكن تغيير قيم رمز النوع بعد إنشاء الكائن. سيكون علينا تبديل صنف الكائن نفسه بطريقة أو بأخرى بشكل آلي، وهو أمر غير ممكن. ومع ذلك، سيكون [[Refactoring/replace type code with state strategy|تبديل رموز الأنواع بالحالة/الاستراتيجية]] بديلًا في هذه الحالة أيضًا.
سطر 27: سطر 36:
 
== آلية الحل ==
 
== آلية الحل ==
 
# استخدم [[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|تبديل الشرطيات بالتعدديّة الشكليّة]] من أجل التخلص من الشروط التي تستخدم رمز النوع مرةً واحدةً وإلى الأبد.
  
 
== انظر أيضًا ==
 
== انظر أيضًا ==

مراجعة 06:20، 19 ديسمبر 2018

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

المشكلة

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

الحل

إنشاء أصناف فرعية لكل قيمة من النوع المُرمَّز. ثم استخراج السلوكيات ذات الصلة من الصنف الأصلي إلى هذه الأصناف الفرعية. وتبديل رموز التحكم في التدفق بالتعدديّة الشكليّة (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. عند نقل كل ما يمكن نقله، استخدم تبديل الشرطيات بالتعدديّة الشكليّة من أجل التخلص من الشروط التي تستخدم رمز النوع مرةً واحدةً وإلى الأبد.

انظر أيضًا

مصادر