É um - Is-a

Na representação do conhecimento , programação e design orientado a objeto (ver arquitetura de programa orientada a objeto ), is-a ( is_a ou is a ) é uma relação de subsunção entre abstrações (por exemplo , tipos , classes ), em que uma classe A é uma subclasse de outra classe B (e então B é uma superclasse de A ). Em outras palavras, o tipo A é um subtipo do tipo B quando a especificação de A implica a especificação de B. Ou seja, qualquer objeto (ou classe) que satisfaça a especificação de A também satisfaz a especificação de B, porque a especificação de B é mais fraca.

A é-um relacionamento é para ser contrastado com o tem-um ( has_a ou tem um ) relação entre tipos (classes); confundir as relações has-a e is-a é um erro comum ao projetar um modelo (por exemplo, um programa de computador ) da relação do mundo real entre um objeto e seu subordinado. O relacionamento é-um também pode ser contrastado com o relacionamento instância-de entre objetos (instâncias) e tipos (classes): consulte Distinção de tipo-token .

Para resumir as relações, existem:

  • hiperônimo - relações de hipônimo (supertipo / superclasse-subtipo / subclasse) entre tipos (classes) definindo uma hierarquia taxonômica, onde
    • para uma relação de subsunção : um hipônimo (subtipo, subclasse) tem uma relação tipo-de ( é-a ) com seu hiperônimo (supertipo, superclasse);
  • holônimo - relações meronímicas (todo / entidade / contêiner-parte / constituinte / membro) entre os tipos (classes) definindo uma hierarquia possessiva, onde
    • para uma relação de agregação (ou seja, sem propriedade):
      • um holônimo (todo) tem uma relação tem-uma com seu merônimo (parte),
    • para uma relação de composição (ou seja, com propriedade):
      • um merônimo (constituinte) tem uma relação parcial com seu holônimo (entidade),
    • para uma relação de contenção :
      • um merônimo (membro) tem uma relação de membro de com seu holônimo ( contêiner );
  • relações conceito-objeto (tipo-token) entre tipos (classes) e objetos (instâncias), onde
    • um token (objeto) tem um relacionamento de instância com seu tipo (classe).

Exemplos de subtipagem

A subtipagem permite que um determinado tipo seja substituído por outro tipo ou abstração. É Subtipagem disse para estabelecer um é-a relação entre o subtipo e alguma abstração existente, implícita ou explicitamente, dependendo suporte ao idioma. O relacionamento pode ser expresso explicitamente por meio de herança em linguagens que suportam herança como um mecanismo de subtipagem.

C ++

O código C ++ a seguir estabelece uma relação de herança explícita entre as classes B e A , onde B é uma subclasse e um subtipo de A e pode ser usado como A sempre que um B for especificado (por meio de uma referência, um ponteiro ou o próprio objeto )

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

Pitão

O código python a seguir estabelece uma relação de herança explícita entre as classes B e A , onde B é uma subclasse e um subtipo de A , e pode ser usado como A sempre que um B for necessário.

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.

No exemplo a seguir, o tipo (a) é um tipo "regular" e o tipo (tipo (a)) é um metatipo. Embora, como distribuídos, todos os tipos tenham o mesmo metatipo ( PyType_Type , que também é seu próprio metatipo), isso não é um requisito. O tipo de classes clássicas, conhecidas como types.ClassType , também pode ser considerado um metatipo distinto.

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

Em Java, é uma relação entre os parâmetros de tipo de uma classe ou interface e os parâmetros de tipo de outra são determinados pelas cláusulas extends e implements .

Usando as classes de coleções, ArrayList <E> implementa List <E> e List <E> estende a coleção <E>. Portanto, ArrayList <String> é um subtipo de List <String>, que é um subtipo de Coleção <String>. A relação de subtipagem é preservada entre os tipos automaticamente. Ao definir uma interface, PayloadList, que associa um valor opcional do tipo genérico P a cada elemento, sua declaração pode ser semelhante a:

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

As seguintes parametrizações de PayloadList são subtipos de List <String>:

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

Princípio de substituição de Liskov

O princípio de substituição de Liskov explica uma propriedade: "Se para cada objeto o1 do tipo S houver um objeto o2 do tipo T, para todos os programas P definidos em termos de T, o comportamento de P é inalterado quando o1 é substituído por o2, então S é um subtipo de T, " . O exemplo a seguir mostra uma violação do 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));
}

Obviamente, a função DrawShape está formatada incorretamente. Ele deve saber sobre todas as classes derivadas da classe Shape. Além disso, deve ser alterado sempre que uma nova subclasse de Forma for criada. No design orientado a objetos , muitos veem a estrutura disso como um anátema.

Aqui está um exemplo mais sutil de violação do 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;
};

Isso funciona bem, mas quando se trata da classe Square, que herda a classe Rectangle, ela viola o LSP, embora a relação é-a seja mantida entre Rectangle e Square. Porque quadrado é retangular. O exemplo a seguir substitui duas funções, Setwidth e SetHeight, para corrigir o problema. Mas consertar o código implica que o design está com defeito.

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

O exemplo a seguir, a função g funciona apenas para a classe Rectangle, mas não para Square e, portanto, o princípio aberto-fechado foi violado.

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

Veja também

Notas

Referências