Jest - Is-a

W reprezentacji wiedzy , programowaniu zorientowanym obiektowo i projektowaniu (patrz architektura programu zorientowanego obiektowo ), is-a ( is_a lub is a ) to relacja subsumpcji między abstrakcjami (np. Typami , klasami ), w której jedna klasa A jest podklasą innej Klasa B (a więc i B jest nadrzędna z a ). Innymi słowy, typ A jest podtypem typu B, gdy specyfikacja A implikuje specyfikację B. Oznacza to, że każdy obiekt (lub klasa), który spełnia specyfikację A, spełnia również specyfikację B, ponieważ specyfikacja B jest słabsza.

Is-a związek jest Kontrastuje z ma-a ( has_a lub ma ) związek pomiędzy typów (klas); mylenie relacji ma-a i jest-a jest częstym błędem podczas projektowania modelu (np. programu komputerowego ) relacji między obiektem a obiektem podrzędnym w świecie rzeczywistym. Is-a związek można również przeciwstawić instancji od relacji pomiędzy przedmiotami (wystąpienia) i typu (klas): patrz typu znak różnicy .

Podsumowując relacje, są:

  • hiperonim - hiponim ( nadtyp / nadklasa -podtyp / podklasa) relacje między typami (klasami) określające hierarchię taksonomiczną, gdzie
    • na subsumcji stosunku: a hyponym (podtyp podklasy) ma typ-o ( is-a ) związek z hyperonym (supertypem, nadklasy);
  • holonim - meronim (całość / jednostka / pojemnik-część / składnik / element) relacje między typami (klasami) określające hierarchię dzierżawczą, gdzie
    • dla relacji agregacji (tj. bez własności):
      • holonym (cały) ma HAS w związku z jego meronym (częściowo)
    • dla relacji układowej (tj. z prawem własności):
      • meronim (składnik) ma część związku ze swoim holonimem (podmiotem),
    • na szczelność względem:
      • meronim (członek) ma powiązanie członka ze swoim holonimem ( kontener );
  • relacje pojęcie – obiekt (typ – token) między typami (klasami) i obiektami (instancjami), gdzie
    • token (obiekt) ma relację instancji ze swoim typem (klasą).

Przykłady podtypów

Podtypowanie umożliwia zastąpienie danego typu innym typem lub abstrakcją. Subtyping mówi celu utworzenie is-a relacja między podtypu i jakimś istniejącym abstrakcji, bezpośrednio lub pośrednio, w zależności od wsparcia językowego. Zależność można wyrazić jawnie poprzez dziedziczenie w językach, które obsługują dziedziczenie jako mechanizm podtypów.

C ++

Poniższy kod C ++ ustanawia jawną relację dziedziczenia między klasami B i A , gdzie B jest zarówno podklasą, jak i podtypem A i może być używany jako A wszędzie tam, gdzie określono B (za pomocą odwołania, wskaźnika lub samego obiektu ).

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.
}

Pyton

Poniższy kod Pythona ustanawia jawną relację dziedziczenia między klasami B i A , gdzie B jest zarówno podklasą, jak i podtypem A i może być używany jako A wszędzie tam, gdzie jest wymagane 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.

W poniższym przykładzie typ (a) jest typem „zwykłym”, a typ (typ (a)) jest metatypem. Chociaż w przypadku dystrybucji wszystkie typy mają ten sam metatyp ( PyType_Type , który jest również jego własnym metatypem), nie jest to wymagane. Typ klas klasycznych, znany jako types.ClassType , można również uznać za odrębny metatyp.

>>> a = 0
>>> type(a)
<type 'int'>
>>> type(type(a))
<type 'type'>
>>> type(type(type(a)))
<type 'type'>
>>> type(type(type(type(a))))
<type 'type'>

Jawa

W Javie relacja is- między parametrami typu jednej klasy lub interfejsu a parametrami typu innej jest określana przez klauzule extends i implements .

Używając klas Collections, ArrayList <E> implementuje List <E>, a List <E> rozszerza Collection <E>. Więc ArrayList <String> jest podtypem List <String>, który jest podtypem Collection <String>. Relacja podtypów jest zachowywana automatycznie między typami. Podczas definiowania interfejsu PayloadList, który kojarzy opcjonalną wartość typu ogólnego P z każdym elementem, jego deklaracja może wyglądać następująco:

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

Następujące parametryzacje PayloadList są podtypami List <String>:

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

Zasada substytucji Liskova

Zasada podstawienia Liskova wyjaśnia właściwość: „Jeśli dla każdego obiektu o1 typu S istnieje obiekt o2 typu T taki, że dla wszystkich programów P zdefiniowanych w kategoriach T zachowanie P pozostaje niezmienione, gdy o1 jest zastępowane za o2, to S jest podtypem T, ” . Poniższy przykład pokazuje naruszenie 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));
}

Oczywiście funkcja DrawShape jest źle sformatowana. Musi wiedzieć o wszystkich klasach pochodnych klasy Shape. Należy to również zmienić za każdym razem, gdy tworzona jest nowa podklasa Shape. W projektowaniu zorientowanym obiektowo wielu postrzega tę strukturę jako anatemę.

Oto bardziej subtelny przykład naruszenia 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;
};

Działa to dobrze, ale jeśli chodzi o klasę Square, która dziedziczy klasę Rectangle, narusza LSP, mimo że relacja is-a zachodzi między Rectangle i Square. Ponieważ kwadrat jest prostokątny. Poniższy przykład zastępuje dwie funkcje, Setwidth i SetHeight, aby rozwiązać problem. Ale naprawienie kodu oznacza, że ​​projekt jest wadliwy.

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

Poniższy przykład, funkcja g działa tylko dla klasy Rectangle, ale nie dla Square, więc zasada open-closed została naruszona.

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

Zobacz też

Uwagi

Bibliografia