Es un - Is-a

En representación del conocimiento , programación y diseño orientados a objetos (ver arquitectura de programas orientados a objetos ), is-a ( is_a o is a ) es una relación de subsunción entre abstracciones (por ejemplo , tipos , clases ), donde una clase A es una subclase de otra clase B (por lo que B es una superclase de A ). En otras palabras, el tipo A es un subtipo del tipo B cuando la especificación de A implica la especificación de B. Es decir, cualquier objeto (o clase) que satisfaga la especificación de A también satisface la especificación de B, porque la especificación de B es más débil.

La es-a relación debe ser contrastado con el tiene-a ( has_a o tiene una ) relación entre los tipos (clases); confundir las relaciones tiene-a y es-a es un error común al diseñar un modelo (por ejemplo, un programa de computadora ) de la relación del mundo real entre un objeto y su subordinado. La relación es-a también puede contrastarse con la relación instancia-de entre objetos (instancias) y tipos (clases): consulte Distinción de tipo-token .

Para resumir las relaciones, hay:

  • hiperónimo - hipónimo (supertipo / superclase-subtipo / subclase) relaciones entre tipos (clases) que definen una jerarquía taxonómica, donde
    • para una relación de subsunción : un hipónimo (subtipo, subclase) tiene una relación tipo-de ( es-a ) con su hiperónimo (supertipo, superclase);
  • holónimo - merónimo (todo / entidad / contenedor-parte / constituyente / miembro) relaciones entre tipos (clases) que definen una jerarquía posesiva, donde
    • para una relación de agregación (es decir, sin propiedad):
      • un holónimo (todo) tiene una relación tiene-a con su merónimo (parte),
    • para una relación de composición (es decir, con propiedad):
      • un merónimo (constituyente) tiene una relación parcial con su holónimo (entidad),
    • para una relación de contención :
      • un merónimo (miembro) tiene una relación de miembro de con su holónimo ( contenedor );
  • relaciones concepto-objeto (tipo-token) entre tipos (clases) y objetos (instancias), donde
    • un token (objeto) tiene una relación de instancia con su tipo (clase).

Ejemplos de subtipificación

La subtipificación permite sustituir un tipo determinado por otro tipo o abstracción. Se dice que el subtipo establece una relación es-a entre el subtipo y alguna abstracción existente, ya sea implícita o explícitamente, dependiendo del soporte del lenguaje. La relación se puede expresar explícitamente mediante herencia en lenguajes que admiten la herencia como mecanismo de subtipificación.

C ++

El siguiente código de C ++ establece una relación de herencia explícita entre las clases B y A , donde B es una subclase y un subtipo de A , y se puede usar como A siempre que se especifique una B (a través de una referencia, un puntero o el objeto mismo ).

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

El siguiente código de Python establece una relación de herencia explícita entre las clases B y A , donde B es una subclase y un subtipo de A , y se puede usar como A siempre que se requiera una 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.

En el siguiente ejemplo, el tipo (a) es un tipo "regular" y el tipo (tipo (a)) es un metatipo. Si bien todos los tipos distribuidos tienen el mismo metatipo ( PyType_Type , que también es su propio metatipo), esto no es un requisito. El tipo de clases clásicas, conocidas como types.ClassType , también se puede considerar un 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

En Java, es una relación entre los parámetros de tipo de una clase o interfaz y los parámetros de tipo de otra están determinados por las cláusulas extend e implementa .

Usando las clases de Colecciones, ArrayList <E> implementa List <E> y List <E> extiende Collection <E>. Entonces ArrayList <String> es un subtipo de List <String>, que es un subtipo de Collection <String>. La relación de subtipo se conserva entre los tipos automáticamente. Al definir una interfaz, PayloadList, que asocia un valor opcional de tipo genérico P con cada elemento, su declaración podría verse así:

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

Las siguientes parametrizaciones de PayloadList son subtipos de List <String>:

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

Principio de sustitución de Liskov

El principio de sustitución de Liskov explica una propiedad: "Si para cada objeto o1 de tipo S hay un objeto o2 de tipo T tal que para todos los programas P definidos en términos de T, el comportamiento de P no cambia cuando o1 se sustituye por o2 entonces S es un subtipo de T, " . El siguiente ejemplo muestra una violación de 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, la función DrawShape está mal formateada. Tiene que conocer todas las clases derivadas de la clase Shape. Además, debe cambiarse siempre que se cree una nueva subclase de Shape. En el diseño orientado a objetos , muchos ven la estructura de esto como un anatema.

Aquí hay un ejemplo más sutil de violación de 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;
};

Esto funciona bien, pero cuando se trata de la clase Square, que hereda la clase Rectangle, viola LSP aunque la relación is-a se mantiene entre Rectangle y Square. Porque el cuadrado es rectangular. El siguiente ejemplo anula dos funciones, Setwidth y SetHeight, para solucionar el problema. Pero arreglar el código implica que el diseño es defectuoso.

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

En el siguiente ejemplo, la función g solo funciona para la clase Rectangle pero no para Square, por lo que se ha violado el principio de abierto-cerrado.

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

Ver también

Notas

Referencias