В представление знаний, объектно-ориентированное программирование и дизайн (см. объектно-ориентированная программная архитектура ), is-a (is_a или is a ) является подчинением связь между абстракциями (например, типы, классы ), при этом один класс A является подклассом из другой класс B (и поэтому B является суперклассом класса A). Другими словами, тип A является подтипом типа B, когда спецификация A подразумевает спецификацию B. То есть любой объект (или класс), который удовлетворяет спецификации A, также удовлетворяет спецификации B, потому что спецификация B слабее.
Отношение is-a должно контрастировать с has-a (has_a или имеет) связь между типами (классами); путаница в отношениях has-a и is-a - распространенная ошибка при разработке модели (например, компьютерной программы ) реальных отношений между объектом и его подчиненным. Отношение is-a также можно противопоставить отношениям instance-of между объектами (экземплярами) и типами (классами): см. «различие токена типа » и «отношения типа-лексема."
Чтобы обобщить отношения, существуют:
Подтипы позволяют заданному типу быть заменен другим типом или абстракцией. Говорят, что подтипирование устанавливает связь is-a между подтипом и некоторой существующей абстракцией, неявно или явно, в зависимости от поддержки языка. Отношения могут быть явно выражены через наследование на языках, которые поддерживают наследование как механизм подтипа.
Следующий код C ++ устанавливает явное отношение наследования между классами B и A, где B - оба подкласс и подтип A, и могут использоваться как A везде, где указан B (через ссылку, указатель или сам объект).
класс A {public: void DoSomethingALike () const {}}; класс B: общественность A {public: void DoSomethingBLike () const {}}; void UseAnA (A const some_A) {some_A.DoSomethingALike (); } void SomeFunc () {B b; UseAnA (b); // b можно заменить на A.}
Следующий код Python устанавливает явное отношение наследования между классами Bи A, где Bявляется подклассом и подтипом Aи может использоваться как Aвезде, где требуется B.
класс 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 можно заменить на A.
В следующем примере type (a)является «обычным» типом, а type (type (a))является метатип. В то время как распределенные все типы имеют один и тот же метатип (PyType_Type, который также является его собственным метатипом), это не является требованием. Тип классических классов, известный как types.ClassType, также можно рассматривать как отдельный метатип.
>>>a = 0>>>type (a)>>>type ( type (a)) >>>type (type (type (a))) >>>type (type (type (type (a))))
В Java - это связь между параметрами типа одного класса или интерфейса и параметрами типа другого, которые определяются предложениями extends и реализует.
Используя классы Collections, ArrayList
interface PayloadListextends List {void setPayload (int index, P val);...}
Следующие параметризации PayloadList являются подтипами List
PayloadListPayloadList PayloadList
Принцип замещения Лискова объясняет свойство: «Если для каждому объекту o1 типа S соответствует объект o2 типа T, так что для всех программ P, определенных в терминах T, поведение P не меняется, когда o1 заменяется на o2, тогда S является подтипом T, ". В следующем примере показано нарушение LSP.
void DrawShape (const Shape s) {if (typeid (s) == typeid (Square)) DrawSquare (static_cast(s)); иначе, если (typeid (s) == typeid (Circle)) DrawCircle (static_cast (s)); }
Очевидно, функция DrawShape плохо отформатирована. Он должен знать обо всех производных классах класса Shape. Кроме того, его следует менять всякий раз, когда создается новый подкласс Shape. В объектно-ориентированном дизайне многие рассматривают структуру этого как анафему.
Вот более тонкий пример нарушения LSP:
class Rectangle {public: void SetWidth (double w) {itsWidth = w; } void SetHeight (двойной h) {itsHeight = h; } double GetHeight () const {return itsHeight; } double GetWidth () const {return itsWidth; } private: удвоить itsWidth; удвоить itsHeight; };
Это работает хорошо, но когда дело доходит до класса Square, который наследует класс Rectangle, он нарушает LSP, даже несмотря на то, что отношение is-a сохраняется между Rectangle и Square. Потому что квадрат прямоугольный. В следующем примере переопределяются две функции, Setwidth и SetHeight, для устранения проблемы. Но исправление кода означает, что конструкция неисправна.
открытый класс Square: Rectangle {public: virtual void SetWidth (double w); виртуальная пустота SetHeight (двойной h); }; void Square :: SetWidth (двойной ш) {Прямоугольник :: SetWidth (ш); Прямоугольник :: SetHeight (ш); } void Square :: SetHeight (двойной h) {Rectangle :: SetHeight (h); Прямоугольник :: SetWidth (h); }
В следующем примере функция g работает только для класса Rectangle, но не для Square, поэтому принцип открыт-закрытость был нарушен.
void g (Rectangle r) {r.SetWidth (5); r.SetHeight (4); assert (r.GetWidth () * r.GetHeight ()) == 20); }