Это - Is-a

В представлении знаний , объектно-ориентированном программировании и дизайне (см. Объектно -ориентированную программную архитектуру ) 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 можно также противопоставить отношениям instance-of между объектами (экземплярами) и типами (классами): см. Различие между типом и маркером .

Подводя итог отношениям, можно выделить:

  • гипероним - гипоним (супертип / суперкласс – подтип / подкласс) отношения между типами (классами), определяющие таксономическую иерархию, где
    • для отношения подчинения : гипоним (подтип, подкласс) имеет отношение типа ( является ) со своим гиперонимом (супертип, суперкласс);
  • холоним - мероним (целое / сущность / контейнер – часть / составляющая / член) отношения между типами (классами), определяющие притяжательную иерархию, где
  • понятие – объект (тип – токен) отношения между типами (классами) и объектами (экземплярами), где
    • токен (объект) имеет отношение экземпляра со своим типом (классом).

Примеры подтипов

Подтипирование позволяет заменить данный тип другим типом или абстракцией. Подтипирование сказано учредить есть- связь между подтипом и некоторыми существующими абстракциями, явно или неявно, в зависимости от языковой поддержки. Отношения могут быть явно выражены через наследование на языках, которые поддерживают наследование как механизм выделения подтипов.

C ++

Следующий код 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.
}

Python

Следующий код Python устанавливает явное отношение наследования между классами B и 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.

В следующем примере type (a) - это «обычный» тип, а type (type (a)) - это метатип. Хотя распределенные типы имеют один и тот же метатип ( PyType_Type , который также является его собственным метатипом), это не является обязательным требованием. Тип классических классов, известный как types.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, это-а соотношение между параметрами типа одного класса или интерфейса и параметров типа другого определяются расширяется и реализует положения.

Используя классы Collections, ArrayList <E> реализует List <E>, а List <E> расширяет Collection <E>. Итак, ArrayList <String> является подтипом List <String>, который является подтипом Collection <String>. Связь подтипов сохраняется между типами автоматически. При определении интерфейса PayloadList, который связывает необязательное значение универсального типа P с каждым элементом, его объявление может выглядеть так:

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

Следующие параметризации PayloadList являются подтипами List <String>:

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

Принцип подстановки Лискова

Принцип подстановки Лискова объясняет свойство: «Если для каждого объекта 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<Square&>(s));
  else if (typeid(s) == typeid(Circle))
    DrawCircle(static_cast<Circle&>(s));
}

Очевидно, функция DrawShape плохо отформатирована. Он должен знать обо всех производных классах класса Shape. Кроме того, его следует изменять всякий раз, когда создается новый подкласс Shape. В объектно-ориентированном дизайне многие рассматривают эту структуру как анафему.

Вот более тонкий пример нарушения 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;
};

Это хорошо работает, но когда дело доходит до класса Square, который наследует класс Rectangle, он нарушает LSP, хотя между Rectangle и Square сохраняется связь is-a . Потому что квадрат прямоугольный. В следующем примере для устранения проблемы переопределяются две функции, 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 работает только для класса Rectangle, но не для Square, поэтому принцип открыт-закрытость был нарушен.

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

Смотрите также

Заметки

Рекомендации