الفرق بين المراجعتين لصفحة: «Refactoring/replace conditional with polymorphism»
Khaled-yassin (نقاش | مساهمات) أنشأ الصفحة ب' تبديل الشرطيات بالتعدديّة الشكليّة (Replace Conditional with Polymorphism) == المشكلة == وجود شَرطية تنفذ إجر...' |
جميل-بيلوني (نقاش | مساهمات) ط مراجعة وتدقيق. |
||
(مراجعة متوسطة واحدة بواسطة مستخدم واحد آخر غير معروضة) | |||
سطر 1: | سطر 1: | ||
<noinclude>{{DISPLAYTITLE: تبديل الشرطيات بالتعدديّة الشكليّة (Replace Conditional with Polymorphism)}}</noinclude> | |||
== المشكلة == | == المشكلة == | ||
وجود | وجود شروط تنفِّذ إجراءات مختلفة اعتمادًا على نوع الكائن أو خصائصه. | ||
== الحل == | == الحل == | ||
إنشاء أصناف فرعية مطابقة لفروع الشرطية. ويُنشأ فيها تابع مشترك وتُنقل إليه الشيفرة البرمجية من الفرع المقابل من الشرطية. ثم تُستبدل الشرطية باستدعاء التابع المناسب. وينتج عن ذلك تنفيذ سليم يتحقق من خلال التعدديّة الشكليّة اعتمادًا على صنف الكائن. | إنشاء أصناف فرعية مطابقة لفروع البنية الشرطية. ويُنشأ فيها تابع مشترك وتُنقل إليه الشيفرة البرمجية من الفرع المقابل من الشرطية. ثم تُستبدل البنية الشرطية باستدعاء التابع المناسب. وينتج عن ذلك تنفيذ سليم يتحقق من خلال التعدديّة الشكليّة اعتمادًا على صنف الكائن. | ||
== مثال == | |||
=== | === قبل إعادة التصميم === | ||
الصنف <code>Bird</code> يحتوي على التابع <code>getSpeed</code> الذي باستعمال البنية الشرطية <code>switch</code> من النوع <code>type</code> لحساب السرعة بناءً على قيمته: | |||
في لغة Java:<syntaxhighlight lang="java"> | في لغة Java:<syntaxhighlight lang="java"> | ||
class Bird { | class Bird { | ||
سطر 75: | سطر 75: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== بعد إعادة التصميم === | |||
حذفنا البنية الشرطية <code>switch</code> وعرَّفنا أصنافًا متفرعةً من الصنف <code>Bird</code> تعيد تعريف كل منها التابع <code>()getSpeed</code> بناءً على القيمة التي ستُستدعَى معه مستقبلًا: | |||
في لغة Java:<syntaxhighlight lang="java"> | في لغة Java:<syntaxhighlight lang="java"> | ||
abstract class Bird { | abstract class Bird { | ||
سطر 181: | سطر 183: | ||
== لم إعادة التصميم؟ == | == لم إعادة التصميم؟ == | ||
يمكن أن تساعد تقنية إعادة التصميم هذه إذا احتوت الشيفرة البرمجية | يمكن أن تساعد تقنية إعادة التصميم هذه إذا احتوت الشيفرة البرمجية على عوامل تُنفِّذ مهامًا مختلفةً تختلف استنادًا إلى: | ||
* صنف الكائن أو الواجهة التي ينفذها. | * صنف الكائن أو الواجهة التي ينفذها. | ||
* قيمة حقل الكائن. | * قيمة حقل الكائن. | ||
* نتيجة استدعاء أحد توابع الكائن. | * نتيجة استدعاء أحد توابع الكائن. | ||
إذا ظهرت خاصية أو نوع كائن جديدين، | إذا ظهرت خاصية أو نوع كائن جديدين، فستحتاج إلى البحث عن الشيفرة البرمجية وإضافتها في كل الشروط المتشابهة. وبالتالي ستتضاعف فائدة هذه التقنية إذا كان هناك العديد من الشروط المتناثرة في جميع توابع الكائن. | ||
== فوائد تطبيق الحل == | == فوائد تطبيق الحل == | ||
* تلتزم هذه التقنية بمبدأ قل | * تلتزم هذه التقنية بمبدأ ''قل ولا تسأل''؛ فبدلًا من سؤال كائن حول حالته ثمَّ تنفيذ الإجراءات استنادًا إليها، فإنَّه من الأسهل كثيرًا توجيه الكائن ببساطة إلى ما يحتاج إلى عمله، والسماح له بأنَّ يقرر لنفسه كيفية القيام بذلك. | ||
* إزالة الشيفرة البرمجية المكررة. إذ يمكنك التخلص من العديد من | * إزالة الشيفرة البرمجية المكررة. إذ يمكنك التخلص من العديد من الشروط التي تكاد أن تكون متطابقة. | ||
* إذا كنت بحاجة إلى إضافة متغير تنفيذ جديد، كل ما عليك القيام به هو إضافة صنف فرعي جديد دون المساس بالشيفرة البرمجية الموجودة (مبدأ مفتوح/مغلق). | * إذا كنت بحاجة إلى إضافة متغير تنفيذ جديد، كل ما عليك القيام به هو إضافة صنف فرعي جديد دون المساس بالشيفرة البرمجية الموجودة (مبدأ ''مفتوح/مغلق''). | ||
== آلية الحل == | == آلية الحل == | ||
=== التحضير لإعادة التصميم === | === التحضير لإعادة التصميم === | ||
بالنسبة لتقنية إعادة التصميم هذه، يجب أن يكون لديك تسلسل هرمي جاهز للأصناف التي ستحتوي على سلوكيات بديلة. إذا لم يكن لديك تسلسل هرمي مثل هذا، أنشئ | بالنسبة لتقنية إعادة التصميم هذه، يجب أن يكون لديك تسلسل هرمي جاهز للأصناف التي ستحتوي على سلوكيات بديلة. إذا لم يكن لديك تسلسل هرمي مثل هذا، أنشئ واحدًا. و ستساعدك تقنيات أخرى على تحقيق هذا منها: | ||
* [[Refactoring/replace type code with subclasses|تبديل رموز الأنواع بالأصناف الفرعية]] (Replace Type Code with Subclasses). ستُنشأ الأصناف الفرعية لجميع قيم خاصية معينة للكائن. هذا الأسلوب بسيط ولكنه أقل مرونة لأنه لا يمكنك إنشاء أصناف فرعية للخصائص الأخرى للكائن. | * [[Refactoring/replace type code with subclasses|تبديل رموز الأنواع بالأصناف الفرعية]] (Replace Type Code with Subclasses). ستُنشأ الأصناف الفرعية لجميع قيم خاصية معينة للكائن. هذا الأسلوب بسيط ولكنه أقل مرونة لأنه لا يمكنك إنشاء أصناف فرعية للخصائص الأخرى للكائن. | ||
* [[Refactoring/replace type code with subclasses|تبديل رموز الأنواع بالحالة/الاستراتيجية]] (Replace Type Code with State/Strategy). | * [[Refactoring/replace type code with subclasses|تبديل رموز الأنواع بالحالة/الاستراتيجية]] (Replace Type Code with State/Strategy). سيُخصِّص صنف لخاصية معينة للكائن وستُنشَأ أصناف فرعية منه لكل قيمة من الخاصية. سيحتوي الصنف الحالي على مراجع إلى الكائنات من هذا النوع وسيفوض إليها التنفيذ. | ||
تفترض الخطوات التالية أنك أنشأت التسلسل الهرمي بالفعل. | تفترض الخطوات التالية أنك أنشأت التسلسل الهرمي بالفعل. | ||
=== خطوات إعادة التصميم === | === خطوات إعادة التصميم === | ||
# إذا كانت الشرطية في تابع ينفذ إجراءات أخرى، | # إذا كانت البنية الشرطية في تابع ينفذ إجراءات أخرى، فقم بإجراء [[Refactoring/extract method|استخراج التوابع]]. | ||
# لكل صنف تسلسل هرمي فرعي، أعِد تعريف التابع الذي يحتوي على الشرطية وانسخ الشيفرة البرمجية للفرع الشرطي المقابل لذلك الموقع. | # لكل صنف تسلسل هرمي فرعي، أعِد تعريف التابع الذي يحتوي على البنية الشرطية وانسخ الشيفرة البرمجية للفرع الشرطي المقابل لذلك الموقع. | ||
# احذف هذا الفرع من الشرطية. | # احذف هذا الفرع من البنية الشرطية. | ||
# كرر الاستبدال حتى تصبح | # كرر الاستبدال حتى تصبح فروع البنية الشرطية فارغةً. ثم احذف هذه البنية وعرِّف نبذة مختصرة عن التابع. | ||
== انظر أيضًا == | == انظر أيضًا == | ||
سطر 214: | سطر 216: | ||
== مصادر == | == مصادر == | ||
* [https://refactoring.guru/replace-conditional-with-polymorphism صفحة توثيق تبديل الشرطيات بالتعدديّة الشكليّة في موقع refactoring.guru]. | * [https://refactoring.guru/replace-conditional-with-polymorphism صفحة توثيق تبديل الشرطيات بالتعدديّة الشكليّة في موقع refactoring.guru]. | ||
[[تصنيف:Refactoring]] | |||
[[تصنيف:Refactoring Techniques]] | |||
[[تصنيف:Refactoring Simplifying Conditional Expressions]] |
المراجعة الحالية بتاريخ 08:59، 19 ديسمبر 2018
المشكلة
وجود شروط تنفِّذ إجراءات مختلفة اعتمادًا على نوع الكائن أو خصائصه.
الحل
إنشاء أصناف فرعية مطابقة لفروع البنية الشرطية. ويُنشأ فيها تابع مشترك وتُنقل إليه الشيفرة البرمجية من الفرع المقابل من الشرطية. ثم تُستبدل البنية الشرطية باستدعاء التابع المناسب. وينتج عن ذلك تنفيذ سليم يتحقق من خلال التعدديّة الشكليّة اعتمادًا على صنف الكائن.
مثال
قبل إعادة التصميم
الصنف Bird
يحتوي على التابع getSpeed
الذي باستعمال البنية الشرطية switch
من النوع type
لحساب السرعة بناءً على قيمته:
في لغة Java:
class Bird {
//...
double getSpeed() {
switch (type) {
case EUROPEAN:
return getBaseSpeed();
case AFRICAN:
return getBaseSpeed() - getLoadFactor() * numberOfCoconuts;
case NORWEGIAN_BLUE:
return (isNailed) ? 0 : getBaseSpeed(voltage);
}
throw new RuntimeException("Should be unreachable");
}
}
في لغة C#:
public class Bird
{
//...
public double GetSpeed()
{
switch (type)
{
case EUROPEAN:
return GetBaseSpeed();
case AFRICAN:
return GetBaseSpeed() - GetLoadFactor() * numberOfCoconuts;
case NORWEGIAN_BLUE:
return isNailed ? 0 : GetBaseSpeed(voltage);
default:
throw new Exception("Should be unreachable");
}
}
}
في لغة PHP:
class Bird {
...
function getSpeed() {
switch ($this->type) {
case EUROPEAN:
return $this->getBaseSpeed();
case AFRICAN:
return $this->getBaseSpeed() - $this->getLoadFactor() * $this->numberOfCoconuts;
case NORWEGIAN_BLUE:
return ($this->isNailed) ? 0 : $this->getBaseSpeed($this->voltage);
}
throw new Exception("Should be unreachable");
}
...
}
في لغة Python:
class Bird:
#...
def getSpeed(self):
if self.type == EUROPEAN:
return self.getBaseSpeed();
elif self.type == AFRICAN:
return self.getBaseSpeed() - self.getLoadFactor() * self.numberOfCoconuts;
elif self.type == NORWEGIAN_BLUE:
return 0 if isNailed else self.getBaseSpeed(self.voltage)
else:
raise Exception("Should be unreachable")
بعد إعادة التصميم
حذفنا البنية الشرطية switch
وعرَّفنا أصنافًا متفرعةً من الصنف Bird
تعيد تعريف كل منها التابع ()getSpeed
بناءً على القيمة التي ستُستدعَى معه مستقبلًا:
في لغة Java:
abstract class Bird {
//...
abstract double getSpeed();
}
class European extends Bird {
double getSpeed() {
return getBaseSpeed();
}
}
class African extends Bird {
double getSpeed() {
return getBaseSpeed() - getLoadFactor() * numberOfCoconuts;
}
}
class NorwegianBlue extends Bird {
double getSpeed() {
return (isNailed) ? 0 : getBaseSpeed(voltage);
}
}
// Somewhere in client code
speed = bird.getSpeed();
في لغة C#:
public abstract class Bird
{
//...
public abstract double GetSpeed();
}
class European: Bird
{
public override double GetSpeed()
{
return GetBaseSpeed();
}
}
class African: Bird
{
public override double GetSpeed()
{
return GetBaseSpeed() - GetLoadFactor() * numberOfCoconuts;
}
}
class NorwegianBlue: Bird
{
public override double GetSpeed()
{
return isNailed ? 0 : GetBaseSpeed(voltage);
}
}
// Somewhere in client code
speed = bird.GetSpeed();
في لغة PHP:
abstract class Bird {
...
abstract function getSpeed();
...
}
class European extends Bird {
function getSpeed() {
return $this->getBaseSpeed();
}
}
class African extends Bird {
function getSpeed() {
return $this->getBaseSpeed() - $this->getLoadFactor() * $this->numberOfCoconuts;
}
}
class NorwegianBlue extends Bird {
function getSpeed() {
return ($this->isNailed) ? 0 : $this->getBaseSpeed($this->voltage);
}
}
// Somewhere in Client code.
$speed = $bird->getSpeed();
في لغة Python:
class Bird:
#...
def getSpeed(self):
pass
class European(Bird):
def getSpeed(self):
return self.getBaseSpeed()
class African(Bird):
def getSpeed(self):
return self.getBaseSpeed() - self.getLoadFactor() * self.numberOfCoconuts
class NorwegianBlue(Bird):
def getSpeed():
return 0 if self.isNailed else self.getBaseSpeed(self.voltage)
# Somewhere in client code
speed = bird.getSpeed()
لم إعادة التصميم؟
يمكن أن تساعد تقنية إعادة التصميم هذه إذا احتوت الشيفرة البرمجية على عوامل تُنفِّذ مهامًا مختلفةً تختلف استنادًا إلى:
- صنف الكائن أو الواجهة التي ينفذها.
- قيمة حقل الكائن.
- نتيجة استدعاء أحد توابع الكائن.
إذا ظهرت خاصية أو نوع كائن جديدين، فستحتاج إلى البحث عن الشيفرة البرمجية وإضافتها في كل الشروط المتشابهة. وبالتالي ستتضاعف فائدة هذه التقنية إذا كان هناك العديد من الشروط المتناثرة في جميع توابع الكائن.
فوائد تطبيق الحل
- تلتزم هذه التقنية بمبدأ قل ولا تسأل؛ فبدلًا من سؤال كائن حول حالته ثمَّ تنفيذ الإجراءات استنادًا إليها، فإنَّه من الأسهل كثيرًا توجيه الكائن ببساطة إلى ما يحتاج إلى عمله، والسماح له بأنَّ يقرر لنفسه كيفية القيام بذلك.
- إزالة الشيفرة البرمجية المكررة. إذ يمكنك التخلص من العديد من الشروط التي تكاد أن تكون متطابقة.
- إذا كنت بحاجة إلى إضافة متغير تنفيذ جديد، كل ما عليك القيام به هو إضافة صنف فرعي جديد دون المساس بالشيفرة البرمجية الموجودة (مبدأ مفتوح/مغلق).
آلية الحل
التحضير لإعادة التصميم
بالنسبة لتقنية إعادة التصميم هذه، يجب أن يكون لديك تسلسل هرمي جاهز للأصناف التي ستحتوي على سلوكيات بديلة. إذا لم يكن لديك تسلسل هرمي مثل هذا، أنشئ واحدًا. و ستساعدك تقنيات أخرى على تحقيق هذا منها:
- تبديل رموز الأنواع بالأصناف الفرعية (Replace Type Code with Subclasses). ستُنشأ الأصناف الفرعية لجميع قيم خاصية معينة للكائن. هذا الأسلوب بسيط ولكنه أقل مرونة لأنه لا يمكنك إنشاء أصناف فرعية للخصائص الأخرى للكائن.
- تبديل رموز الأنواع بالحالة/الاستراتيجية (Replace Type Code with State/Strategy). سيُخصِّص صنف لخاصية معينة للكائن وستُنشَأ أصناف فرعية منه لكل قيمة من الخاصية. سيحتوي الصنف الحالي على مراجع إلى الكائنات من هذا النوع وسيفوض إليها التنفيذ.
تفترض الخطوات التالية أنك أنشأت التسلسل الهرمي بالفعل.
خطوات إعادة التصميم
- إذا كانت البنية الشرطية في تابع ينفذ إجراءات أخرى، فقم بإجراء استخراج التوابع.
- لكل صنف تسلسل هرمي فرعي، أعِد تعريف التابع الذي يحتوي على البنية الشرطية وانسخ الشيفرة البرمجية للفرع الشرطي المقابل لذلك الموقع.
- احذف هذا الفرع من البنية الشرطية.
- كرر الاستبدال حتى تصبح فروع البنية الشرطية فارغةً. ثم احذف هذه البنية وعرِّف نبذة مختصرة عن التابع.
انظر أيضًا
- تبديل رموز الأنواع بالأصناف الفرعية.
- تبديل رموز الأنواع بالحالة/الاستراتيجية.
- استخراج التوابع.
- استخدام التعليمة Switch.