Está en la página 1de 18

Principio de substitución

de Liskov
(LSP)
José Galaviz
Facultad de Ciencias
UNAM
Liskov Substitution
Principle
Sub

Super
Donde quiera que se usaba la
superclase, se puede usar la
subclase
Como fue enunciado en 1988

“Methods that use references to the


base classes must be able to use the
objects of the derived classes without
knowing it”
Extender una clase...

Significa especializarla, añadirle atributos,


hacer más específico su comportamiento,
añadir métodos, sobreponer (overriding)
métodos respetando la firma precisa,
sobrecargar (overloading) métodos
especializando el servicio.
Así que...

Para respetar el LSP:


● La subclase no puede prohibir algo que se permitía en
la superclase.
● Los métodos de la subclase deben entregar el mismo
tipo (o uno derivado) como resultados.
● Los métodos de la subclase deben recibir el mismo tipo
(o uno derivado) de parámetros.
● Donde quiera que se usaba una referencia a un objeto
de la superclase, se debe poder usar una a un objeto
de la subclase y todo debe funcionar igual.
Ejemplo: Ave

class Ave {
public void volar(){}
public void comer(){}
}
Ejemplo: subclases
class Cuervo extends Ave {}

class Guajolote extends Ave {


volar(){
throw new
UnsupportedOperationException();
}
}
Ejemplo: prueba main
public AveTest {
public static void main(String[] args){
List<Ave> aves = new ArrayList<Ave>();
aves.add(new Ave());
aves.add(new Cuervo());
aves.add(new Guajolote());
hagamosQueVuelen(aves);
}
Ejemplo: prueba
hagamosQueVuelen
static void hagamosQueVuelen(List<Ave>
lstaves){
for (Ave pajaro : lstaves) {
pajaro.vuela();
}
}
} // Fin de AveTest
Por qué no jala

Guajolote no vuela. Prohibe hacer algo que se


podía en la superclase.
¿Cómo se resuelve?
Factorizando:

Lo común a todas las instancias de Ave es que


comen. Así que Ave sólo come.
De ella derivan dos subclases: las que vuelan y
las que no.
Cuervo es subclase de la las que vuelan.
Guajolote es subclase de las que no vuelan.
Es decir...
class Ave{
public void comer(){}
}

class AveVoladora extends Ave {


public void vuela()()
}

class AveNoVoladora extends Ave{}


Otro
class Rectangle {
protected int m_width;
protected int m_height;
public void setWidth(int width) {
m_width = width;
}
public void setHeight(int height) {
m_height = height;
}
public int getWidth() {
return m_width;
}
Otro
class Rectangle {
… // lo anterior
public int getHeight() {
return m_height;
}
public int getArea() {
return m_width * m_height;
}
}
Cuadrado
class Square extends Rectangle {
public void setWidth(int width) {
m_width = width;
m_height = width;
}
public void setHeight(int height) {
m_width = height;
m_height = height;
}
}
Prueba
class LspTest {
private static Rectangle getNewRectangle() {
// it can be an object returned by some factory ...
return new Square();
}
Prueba
public static void main (String args[]) {
Rectangle r = LspTest.getNewRectangle();
r.setWidth(5);
r.setHeight(10);
// user knows that r it's a rectangle.
// It assumes that he's able to set
// the width and height as for the base class
System.out.println(r.getArea());
// now he's surprised to see that the
// area is 100 instead of 50.
}
} // Fin de Prueba
¿Qué salió mal?

● Todo cuadrado ES UN rectángulo, ¿qué no?


● Es que la relación “ES UN” es un poco menos
superficial.
● Lo de “ES UN” debe estar referido al
COMPORTAMIENTO, no sólo a los atributos.
● Un cuadrado es un caso particular de rectángulo, pero
NO puedo usarlo siempre que usaba un rectángulo.
● Si tengo que modificar el programa para adecuarlo
cuando uso una subclase particular, estoy violando el
principio Open/Close, que generaliza el LSP.