---
title: "استبدال الجمل الشرطية بتعدد الأشكال (Replace Conditional with Polymorphism)"
type: "refactoring-technique"
slug: "replace-conditional-with-polymorphism"
url: "http://localhost:3000/ar/replace-conditional-with-polymorphism.md"
category: "تبسيط التعبيرات الشرطية"
description: "المشكلة: لديك جملة شرطية تؤدي إجراءات مختلفة اعتماداً على نوع الكائن أو خصائصه. الحل: أنشئ فئات فرعية (Subclasses) تطابق فروع الجملة الشرطية. وفيها، أنشئ تابعاً مشتركاً وانقل الكود من الفرع المقابل للجملة الشرطية إليه. ثم استبدل الجملة الشرطية باستدعاء التابع المعني. وتكون النتيجة هي الوصول للتطبيق الصحيح عبر تعدد الأشكال (Polymorphism) اعتماداً على فئة الكائن."
languages: ["java", "csharp", "php", "python", "typescript"]
---
# استبدال الجمل الشرطية بتعدد الأشكال (Replace Conditional with Polymorphism)

> المشكلة: لديك جملة شرطية تؤدي إجراءات مختلفة اعتماداً على نوع الكائن أو خصائصه. الحل: أنشئ فئات فرعية (Subclasses) تطابق فروع الجملة الشرطية. وفيها، أنشئ تابعاً مشتركاً وانقل الكود من الفرع المقابل للجملة الشرطية إليه. ثم استبدل الجملة الشرطية باستدعاء التابع المعني. وتكون النتيجة هي الوصول للتطبيق الصحيح عبر تعدد الأشكال (Polymorphism) اعتماداً على فئة الكائن.

## المشكلة

لديك جملة شرطية تؤدي إجراءات مختلفة اعتماداً على نوع الكائن أو خصائصه.

## الحل

أنشئ فئات فرعية تطابق فروع الجملة الشرطية. وفيها، أنشئ تابعاً مشتركاً وانقل الكود من الفرع المقابل للجملة الشرطية إليه. ثم استبدل الجملة الشرطية باستدعاء التابع المعني. وتكون النتيجة هي الوصول للتطبيق الصحيح عبر تعدد الأشكال (Polymorphism) اعتماداً على فئة الكائن.

## دواعي إعادة الهيكلة

يمكن أن تساعد تقنية إعادة الهيكلة هذه إذا كان الكود الخاص بك يحتوي على معاملات تؤدي مهاماً متنوعة تختلف بناءً على:

* فئة (Class) الكائن أو الواجهة (Interface) التي يطبقها.
* قيمة حقل الكائن.
* نتيجة استدعاء أحد توابع الكائن.

إذا ظهرت خاصية أو نوع كائن جديد، فستحتاج إلى البحث عن الكود وإضافته في جميع الجمل الشرطية المماثلة. وبالتالي، تتضاعف فائدة هذه التقنية إذا كان هناك العديد من الجمل الشرطية المنتشرة في جميع توابع الكائن.

## الفوائد

* تلتزم هذه التقنية بمبدأ _"أخبره، لا تسأله" (Tell-Don't-Ask)_: فبدلاً من سؤال الكائن عن حالته ثم اتخاذ إجراءات بناءً عليها، من الأسهل بكثير إخبار الكائن بما يجب عليه فعله وترك القرار له في كيفية تنفيذ ذلك.
* يزيل تكرار الكود. حيث تتخلص من العديد من الجمل الشرطية المتطابقة تقريباً.
* إذا كنت بحاجة إلى إضافة خيار تنفيذ جديد، فكل ما عليك فعله هو إضافة فئة فرعية جديدة دون لمس الكود الحالي (_مبدأ المفتوح/المغلق - Open/Closed Principle_).

## كيفية إعادة الهيكلة

#### التحضير لإعادة الهيكلة

بالنسبة لتقنية إعادة الهيكلة هذه، يجب أن يكون لديك تسلسل هرمي جاهز للفئات التي ستحتوي على السلوكيات البديلة. إذا لم يكن لديك تسلسل هرمي كهذا، فأنشئ واحداً. ستساعدك التقنيات الأخرى في تحقيق ذلك:

* [استبدال رمز النوع بفئات فرعية (Replace Type Code with Subclasses)](/replace-type-code-with-subclasses). حيث سيتم إنشاء فئات فرعية لجميع قيم خاصية كائن معينة. هذا النهج بسيط ولكنه أقل مرونة لأنه لا يمكنك إنشاء فئات فرعية للخصائص الأخرى للكائن.
* [استبدال رمز النوع بالحالة/الاستراتيجية (Replace Type Code with State/Strategy)](/replace-type-code-with-state-strategy). حيث سيتم تخصيص فئة لخاصية كائن معينة وسيتم إنشاء فئات فرعية منها لكل قيمة من قيم الخاصية. وستحتوي الفئة الحالية على إشارات مرجعية لكائنات من هذا النوع وتفوض التنفيذ إليها.

تفترض الخطوات التالية أنك قمت بالفعل بإنشاء التسلسل الهرمي.

#### خطوات إعادة الهيكلة

1. إذا كانت الجملة الشرطية في تابع يؤدي إجراءات أخرى أيضاً، فقم بـ [استخراج التابع (Extract Method)](/extract-method).
2. لكل فئة فرعية في التسلسل الهرمي، أعد تعريف التابع الذي يحتوي على الجملة الشرطية وانسخ كود فرع الشرط المقابل إلى ذلك الموقع.
3. احذف هذا الفرع من الجملة الشرطية.
4. كرر عملية الاستبدال حتى تصبح الجملة الشرطية فارغة. ثم احذف الجملة الشرطية وصرح بالتابع كتابع مجرد (Abstract).
## Relations

**Similar techniques**

- [استبدال الجمل الشرطية بتعدد الأشكال (Replace Conditional with Polymorphism)](/ar/replace-conditional-with-polymorphism.md)

**Eliminates smells**

- [عبارات Switch](/ar/smells/switch-statements.md)

## Code Examples

### java

```java
// Before
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");
  }
}

// After
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);
  }
}

// في مكان ما في كود العميل
speed = bird.getSpeed();
```

### csharp

```csharp
// Before
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");
    }
  }
}

// After
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);
  }
}

// في مكان ما في كود العميل
speed = bird.GetSpeed();
```

### php

```php
// Before
class Bird {
  // ...
  public 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");
  }
  // ...
}

// After
abstract class Bird {
  // ...
  abstract function getSpeed();
  // ...
}

class European extends Bird {
  public function getSpeed() {
    return $this->getBaseSpeed();
  }
}
class African extends Bird {
  public function getSpeed() {
    return $this->getBaseSpeed() - $this->getLoadFactor() * $this->numberOfCoconuts;
  }
}
class NorwegianBlue extends Bird {
  public function getSpeed() {
    return ($this->isNailed) ? 0 : $this->getBaseSpeed($this->voltage);
  }
}

// في مكان ما في كود العميل.
$speed = $bird->getSpeed();
```

### python

```python
// Before
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 self.isNailed else self.getBaseSpeed(self.voltage)
        else:
            raise Exception("Should be unreachable")

// After
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(self):
        return 0 if self.isNailed else self.getBaseSpeed(self.voltage)

# في مكان ما في كود العميل
speed = bird.getSpeed()
```

### typescript

```typescript
// Before
class Bird {
  // ...
  getSpeed(): number {
    switch (type) {
      case EUROPEAN:
        return getBaseSpeed();
      case AFRICAN:
        return getBaseSpeed() - getLoadFactor() * numberOfCoconuts;
      case NORWEGIAN_BLUE:
        return (isNailed) ? 0 : getBaseSpeed(voltage);
    }
    throw new Error("Should be unreachable");
  }
}

// After
abstract class Bird {
  // ...
  abstract getSpeed(): number;
}

class European extends Bird {
  getSpeed(): number {
    return getBaseSpeed();
  }
}
class African extends Bird {
  getSpeed(): number {
    return getBaseSpeed() - getLoadFactor() * numberOfCoconuts;
  }
}
class NorwegianBlue extends Bird {
  getSpeed(): number {
    return (isNailed) ? 0 : getBaseSpeed(voltage);
  }
}

// في مكان ما في كود العميل
let speed = bird.getSpeed();
```

