في تمثيل المعرفة، البرمجة والتصميم الكينوني (انظر بنية البرنامج الكينوني)، ((**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** (عبر مرجع أو مؤشر أو الكائن نفسه)). 

```
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.** 

```
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 ، يمكن أيضًا اعتباره نوعًا متميزًا. 

```
>>> 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 ، **هي** علاقة بين معلمات لفئة أو واجهة واحدة، ومعلمات النوع لفئة أخرى يتم تحديدها بواسطة الجمل الموسعة وتنفذ.

باستخدام فئات المجموعات، المصفوفة <E>، ترث القائمة ووبالتالي توسع المجموعة <E>. لذا فإن المصفوفة من نوع string هو نوع فرعي من القائمة <String> ، وهو نوع فرعي من المجموعة <String>. يتم الحفاظ على علاقة الأنواع الفرعية والأنواع تلقائيًا. عندما نحدد واجهة، PayloadList ، تربط قيمة اختيارية من النوع العام P بكل عنصر، قد يبدو إعلانه كما يلي: 

```
interface PayloadList<E, P> extends List<E> {
 void setPayload(int index, P val);
 ...
}

```

المعلمات التالية من PayloadList هي أنواع فرعية من القائمة <سلسلة>: 

```
PayloadList<String, String>
PayloadList<String, Integer>
PayloadList<String, Exception>

```

يشرح مبدأ استبدال Liskov خاصية، *«إذا كان لكل كائن o1 من النوع S يوجد كائن o2 من النوع T بحيث لا يتغير سلوك P لجميع البرامج P المحددة من حيث T ، عندما يتم استبدال o1 بـ o2 ثم S هو نوع فرعي من T»*. يوضح المثال التالي انتهاك LSP. 

```
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: 

```
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 ، لإصلاح المشكلة. لكن إصلاح الرمز يعني أن التصميم معيب. 

```
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 ، وبالتالي تم انتهاك مبدأ الإغلاق المفتوح. 

```
void g(Rectangle& r)
{
 r.SetWidth(5);
 r.SetHeight(4);
 assert(r.GetWidth() \* r.GetHeight()) == 20);
}

```

المراجع
-------

1. ↑  تمييز نوع الرمز المميز
2. ↑  See also Containment (computer programming).
3. ↑  Guido van Rossum. "Subtyping Built-in Types". مؤرشف من الأصل في 2020-05-14. اطلع عليه بتاريخ 2012-10-02.
4. ↑  Liskov، Barbara (مايو 1988). *Data Abstraction and Hierarchy* (PDF). SIGPLAN Notices. مؤرشف من الأصل (PDF) في 2019-07-08.

* أيقونة بوابةبوابة علم الاجتماع