علاقة Is-a

في تمثيل المعرفة، البرمجة والتصميم الكينوني (انظر بنية البرنامج الكينوني)، ((**is_a** or **is a**)) هي علاقة من العلاقات الفرعية خاصية التجريد كما في (الكلاسات، الفئات)، حيث يكون كلاس A هو فئة فرعية من كلاس B أخرى (وبالتالي فإن B هي فئة فائقة من A). بمعنى آخر، النوع A هو نوع فرعي من النوع B عندما تتضمن مواصفات A مواصفات B. أي أن أي كائن (أو فئة) يفي بمواصفات A يفي أيضًا بمواصفات B  في نفس الوقت، لأن مواصفات B أضعف.

يجب أن تكون العلاقة is-a متناقضة في مع علاقة (*has_a* or *has a*) بين أنواع (الكلاسات)؛ إن الخلط بين العلاقات *هو has-a* و *is-a* هو خطأ شائع عند تصميم نموذج (على سبيل المثال، برنامج كمبيوتر) للعلاقة الواقعية بين كائن ما ومرؤوسه. ويمكن لهذه العلاقة ان تتعارض مع مثيلاتها من العلاقات الباقية والتي يمكن أن تكون بين الكائنات (حالات) وأنواع (الكلاسات): انظر «من نوع رمزي التمييز» و «علاقات من نوع رمزي».

لتلخيص العلاقات لدينا:

* Hyperonym - الاختصار (supertype – subtype) العلاقات بين الكلاسات التي تحدد التسلسل الهرمي للتصنيف، حيث
** بالنسبة لعلاقة الاستيعاب: الاختصار (النوع الفرعي، الفئة الفرعية) له علاقة من *نوع* (*is-a*) ويكون الاختصار (supertype ، superclass)؛
* holonym - العلاقات المرادفة وتأتي بالتسلسل الهرمي التالي (كامل / الكيان / جزء الحاوية / المكون / العضو) بين الأنواع (الفئات) التي تحدد التسلسل الهرمي التملك، حيث تكون
** لعلاقة تجميع في التسلسل الهرمي وهي:
*** وholonym (كامل) meronym لها (جزء)،
** لعلاقة تكوين (أي بالملكية):
*** المرادف (المكون) له *[[اسم جزء|جزء من]]* العلاقة مع الاسم المستعار (الكيان)،
** لعلاقة احتواء:
*** مرادف (عضو) له علاقة *عضو* مع اسمه المختصر (الحاوية)؛
* العلاقات بين المفهوم والكائن (النوع المميز) بين الأنواع (الفئات) والكائنات (الحالات)، حيث
** الرمز المميز (كائن) له علاقة *مثيل* بنوعه (الفئة).

يتيح التصنيف الفرعي استبدال نوع معين بنوع أو تجريد النوع الآخر. التصنيف الفرعي يكون لإنشاء علاقة is-a بين نوع فرعي وبعض التجريد القائم، إما علنيا أو ضمنيا، اعتمادا على دعم اللغة. يمكن التعبير عن العلاقة بشكل صريح عبر الوراثة باللغات التي تدعم الوراثة كآلية تصنيف فرعي.

ينشئ كود C ++ التالي علاقة وراثية صريحة بين الفئتين **B** و **A** ، حيث **B** هي فئة فرعية ونوع فرعي اخر من **A** ، ويمكن استخدامها ك **A** حيثما يتم تحديد **B** (عبر مرجع أو مؤشر أو الكائن نفسه)). 
```cpp
class A
{ public:
   void DoSomethingALike() const {}
};

class B : public A
{ public:
   void DoSomethingBLike() const {}
};

void UseAnA(A const& some_A)
{
   some_A.DoSomethingALike();
}

void SomeFunc()
{
   B b;
   UseAnA(b); // b can be substituted for an A.
}
```

ينشئ رمز بايثون التالي علاقة وراثية صريحة بين الفئتين **B** و **A** ، حيث يكون **B** فئة فرعية ونوعًا فرعيًا من **A** ، ويمكن استخدامه ك **A** حيثما كان مطلوبًا **B.** 
```python
class A:
    def do_something_a_like(self):
        pass

class B(A):
    def do_something_b_like(self):
        pass

def use_an_a(some_a):
    some_a.do_something_a_like()

def some_func():
    b = B()
    use_an_a(b)  # b can be substituted for an A.
```

المثال التالي، النوع (أ) هو نوع «عادي»، والنوع (النوع (أ)) هو نوع من بيانات. على الرغم من توزيع جميع الأنواع على نفس نوع البيانات الوصفية (PyType_Type ، وهو أيضًا نوعها الخاص)، إلا أن هذا ليس شيء متطلب. نوع الفصول الكلاسيكية، والمعروف باسم الأنواع. ClassType ، يمكن أيضًا اعتباره نوعًا متميزًا. 
```pycon
>>> a = 0
>>> type(a)
<type 'int'>
>>> type(type(a))
<type 'type'>
>>> type(type(type(a)))
<type 'type'>
>>> type(type(type(type(a))))
<type 'type'>
```

في Java ، **هي** علاقة بين معلمات لفئة أو واجهة واحدة، ومعلمات النوع لفئة أخرى يتم تحديدها بواسطة الجمل الموسعة وتنفذ.

باستخدام فئات المجموعات، المصفوفة ، ترث القائمة ووبالتالي توسع المجموعة . لذا فإن المصفوفة من نوع string هو نوع فرعي من القائمة  ، وهو نوع فرعي من المجموعة . يتم الحفاظ على علاقة الأنواع الفرعية والأنواع تلقائيًا. عندما نحدد واجهة، PayloadList ، تربط قيمة اختيارية من النوع العام P بكل عنصر، قد يبدو إعلانه كما يلي: 
```java
interface PayloadList<E, P> extends List<E> {
    void setPayload(int index, P val);
    ...
}
```

المعلمات التالية من PayloadList هي أنواع فرعية من القائمة : 
```java
PayloadList<String, String>
PayloadList<String, Integer>
PayloadList<String, Exception>
```

يشرح مبدأ استبدال Liskov خاصية، *«إذا كان لكل كائن o1 من النوع S يوجد كائن o2 من النوع T بحيث لا يتغير سلوك P لجميع البرامج P المحددة من حيث T ، عندما يتم استبدال o1 بـ o2 ثم S هو نوع فرعي من T»*. يوضح المثال التالي انتهاك LSP. 
```cpp
void DrawShape(const Shape& s)
{
  if (typeid(s) == typeid(Square))
    DrawSquare(static_cast<Square&>(s));
  else if (typeid(s) == typeid(Circle))
    DrawCircle(static_cast<Circle&>(s));
}
```

من الواضح أن الدالة DrawShape تم تنسيقها بشكل سيئ. يجب أن تعرف كل فئة مشتقة من فئة الشكل. أيضا، يجب تغييره كلما تم إنشاء فئة فرعية جديدة من الشكل. في التصميم الموجه للكائنات

فيما يلي مثال أكثر دقة على انتهاك LSP: 
```cpp
class Rectangle
{
  public:
    void   SetWidth(double w)  { itsWidth = w; }
    void   SetHeight(double h) { itsHeight = h; }
    double GetHeight() const   { return itsHeight; }
    double GetWidth() const    { return itsWidth; }
  private:
    double itsWidth;
    double itsHeight;
};
```

يعمل هذا بشكل جيد ولكن عندما يتعلق الأمر بالفئة المربعة، التي ترث فئة المستطيل، فإنها تنتهك LSP على الرغم من أن العلاقة **هي** علاقة بين المستطيل والمربع. لأن المربع مستطيل. يتجاوز المثال التالي وظيفتين، Setwidth و SetHeight ، لإصلاح المشكلة. لكن إصلاح الرمز يعني أن التصميم معيب. 
```cpp
public class Square : Rectangle
{
  public:
    virtual void SetWidth(double w);
    virtual void SetHeight(double h);
};
void Square::SetWidth(double w)
{
    Rectangle::SetWidth(w);
    Rectangle::SetHeight(w);
}
void Square::SetHeight(double h)
{
    Rectangle::SetHeight(h);
    Rectangle::SetWidth(h);
}
```

المثال التالي، الوظيفة g تعمل فقط لفئة المستطيل ولكن ليس لـ Square ، وبالتالي تم انتهاك مبدأ الإغلاق المفتوح. 
```cpp
void g(Rectangle& r)
{
  r.SetWidth(5);
  r.SetHeight(4);
  assert(r.GetWidth() * r.GetHeight()) == 20);
}
```


## المراجع

تصنيف:برمجة كائنية التوجه
تصنيف:تجريد
تصنيف:تمثيل المعرفة