Diseño SOLID OCW
Diseño SOLID OCW
Diseño SOLID OCW
del So.ware II
A. Goñi, J. Iturrioz
Introducción
“A branch of engineering must take several basic steps in order to
become an established profession, highlighting understanding the
nature of its knowledge” [1]
[1] M.Shaw. Prospects for an engineering discipline of software. IEEE Software 7(6),15-24. 1990
2
¿Dónde está el conocimiento del DOO?
HEURISTICAS
Diverso
PRINCIPIOS
GRASP
Antipatterns
basadoEN
Diseño Orientado GoF (Gamma)
Uso General
a Objetos
Tiempo real
Patrones de
Aplicación Comunicaciones
diseño Integración
Tecnología J2EE
.NET
Java
BAD SMELLS
Refactorización
CATALOGO REFACTORIZACIÓN
3
Principios SOLID
4
EL cambio
”Los sistemas software cambian durante su ciclo de vida”
• Tanto los buenos como los malos diseños se ven afectados por este
requisito.
• Los buenos diseños son estables.
5
Open-Closed Principle (OCP)
• “”Los sistemas software cambian durante su ciclo de vida”
• Tanto los buenos como los malos diseños se ven afectados por este requisito.
• Los buenos diseños son estables.
MotorGasolina
Coche +arrancar()
+acelerar()
+frenar
7
... ¡Pero mantenerla cerrada!
Coche MotorAbstracto
+arrancar()
+acelerar()
+frenar
8
Open-Closed Principle (OCP)
Diseño abierto/cerrado
9
Ejemplo
public class Persona {
String nombre;
String apellidos;
10
Solución OCP Composición
11
El patrón OCP Composición
public final class ClosedClass {
private IMyExtension myExtension;
public ClosedClass(IMyExtension myExtension) {
this.myExtension = myExtension;
}
// métodos que usan el objeto de tipo IMyExtension
public void doMethod() {
myExtension.doStuff();
}
}
12
Ejemplo Coche OCP Composición
public final class Coche{
private IMotor miMotor;
public Coche (IMotor myExtension) {
this.miMotor= myExtension;
}
// métodos que usan el objeto de tipo IMotor
public void arrancar() {
miMotor.arrancar();
}
}
13
Otra alternativa….. herencia
Coche Persona
+arrancar() +visualizaDatos
+acelerar()
+frenar
14
Solución OCP Herencia
15
Herencia vs. composición
16
Herencia vs. composición
Herencia
17
Herencia vs. composición
Composición
18
Ejemplo Composición-Herencia
• Un banco gestiona cuentasCorrientes (Account)
(operaciones ingresar, retirar).
• Tenemos 3 tipos de cuentas (credito, debito, monedero).
• Tenemos 3 tipos de clientes (joven, adulto, dorada)
19
Solución herencia
20
Solución composición
22
Herencia vs. composición
“Una entidad debe ser abierta para su extensión y parametrización,
pero cerrada para su modificación” [3]
23
Del OCP al Siguiente principio
24
Principio: Single Responsability Principle
“Una clase debe tener una unica razón para cambiar” [5]
[5] R. Martin. The Single Responsability Principle. Imagen bajo licencia CC-BY-2.0.
https://drive.google.com/file/d/0ByOwmqah_nuGNHEtcU5OekdDMkk/view https://www.flickr.com/photos/pennuja/
5364128968/
25
Hombre orquesta vs. trompetista
26
La gran orquesta
27
Principio: Single Responsability Principle
28
Ejemplo
Necesitamos una clase que descargue un fichero (puede estar
en formato csv/json/xml), parsee el fichero y finalmente
actualice el contenido en una base de datos o un fichero. Una
implementación podría ser:
public class Tarea {
public void descargarFichero(String ubicacion) {
// descarga un fichero
}
public void parsearFichero(String fichero) {
// parsear el contenido del fichero fichero a XML
}
public void guardarFichero(String fichero) {
// almacenar el fichero en la BD
}
}
29
Cuestiones
public class Tarea {
public void descargarFichero(String ubicacion) {
// descarga un fichero
}
public void parsearFichero(String fichero) {
// parsear el contenido del fichero fichero a XML
}
public void guardarFichero(String fichero) {
// almacenar el fichero en la BD
}
}
Cuestiones:
• ¿Qué sucede con la reusabilidad de “descargar”, “parsear” o
“guardar”?
• ¿Cómo modificarías las clases, de forma que sigas manteniendo en la
clase Tarea todas las funcionalidades?
30
¿Qué sucede con la reusabilidad?
Solución: Crear una clase por reponsabilidad
31
¿Cómo mantener todas las resp. en Tarea?
Solución: Manteniendo un objeto para cada responsabilidad.
public class Tarea {
Downloader downloader = new Downloader();
Parser parser = new Parser();
Storer storer = new Storer();
public void descargarFichero(String ubicacion) {
// descarga un fichero
downloader.descargarFichero(ubicación);
}
public void parsearFichero(String fichero) {
// parsea el contenido del fichero a XML
parser.parsearFichero(fichero);
}
public void guardarFichero(String fichero) {
// almacena el fichero en la BD
storer.guardarFichero(fichero);
}
}
32
Liskov Substitution Principle (LSP)
§ Las claves del OCP: Abstracción y Polimorfismo
! Implementado por herencia
! ¿Cómo podemos medir la calidad de la herencia?
33
La herencia Parece simple
34
Los pingüinos no vuelan
class Pinguino extends Pajaro {
public void vuela() {
new Exception(“no puedo volar!”); }
};
35
Diseño por contrato
• Comportamiento esperado de un método:
• Requisitos esperados (Precondiciones)
• Promesas ofrecidas (Postcondiciones)
Base
int f(int x);
// REQUIERE: x es impar SUBCLASS SUPERCLASS
// PROMETE: return par PRECONDITION POSTCONDITION
SUPERCLASS SUBCLASS
Derived PRECONDITION POSTCONDITION
int f(int x);
// REQUIRE: x is int
// PROMISE: return par>50
37
Diseño por contrato
• Dada la clase Base
class Base {
// REQUIERE: x es impar
// PROMETE: return par
public void f (int x) ;
}
38
¿Cuadrado IS-A Rectángulo?
Rectángulo Cuadrado
- alto
- ancho
?
+ setAlto(double al)
+ setAncho(double an)
+ getAlto():double
+ getAncho():double
39
La respuesta es ...
• Sobrescribir setAlto y setAncho
class Cuadrado extends Rectángulo {
public void setAlto (double al){
ancho=al;
alto=al;
}
}
El problema
void calcArea(Rectángulo r) {
r.setAncho(5); r.setAlto(4);
// ¿Cuánto es el área?
}
• 20!
... ¿Estás seguro? ;-)
40
LSP tiene que ver con la semántica y el
reemplazo
41
¿Es un Rectángulo un Cuadrado?
42
Liskov y el reemplazamiento
• Los métodos de una clase (Clase Servicio) invocados
por un cliente.
• Pueden ser substituidos por una subclase sin afectar al cliente
que le invoca.
Clase Servicio
Cliente
Subclase
Inesperada
El cliente no debe
conocer las subclases
43
Heurístico obtenido del LSP
Es incorrecto que una subclase,
sobrescriba un método con un método
NOP(Nothing Operation)
• Solución 1: Relación de herencia inversa
• ¿Es un Rectángulo un Cuadrado?
Rectángulo Cuadrado
- alto
- ancho
? + setAlto(double al)
+ setAncho(double an)
+ getAlto():double
+ getAncho():double
44
Posibles soluciones I
“Extraer a otra clase padre las características comunes y hacer que la
antigua clase padre y su hija hereden de ella” (*)
45
Posibles soluciones II
Podemos solventar esta situación simplemente usando inmutabilidad.
Consiste en que una vez que se ha creado un objeto, el estado del
mismo no puede volver a modificarse.
46
Interface Segregation Principle (ISP)
“Los clientes no deben ser forzados a depender de
interfaces que no usan” [9]
• Consecuencia:
• La posibilidad de que cambie una interfaz es
proporcional al número de métodos. Cuanto
menor sea, mejor.
• Interface pollution (Bad Smell, Fowler)
47
Ejemplo
interface Animal {
public void alimentar();
public void acariciar();
}
implementa implementa
48
Ejemplo
interface IMascota{ interface Animal {
public void acariciar(); public void alimentar();
} }
implementa implementa
49
Dependency Inversion Principle (DIP)
I. Los módulos de alto nivel no deben depender de los módulos de
bajo nivel.
La dependencia debe basarse en abstracciones.
50
Arquitectura Procedural vs. OO
Arquitectura
Procedural
Arquitectura
Orientada a
objetos
51
Ejemplo aplicando enfoque procedural
void Copy(){
int c;
while ((c = readKeyboard()) != EOF)
writePrinter(c);
}
52
Aplicando el Principio de Inversión de
Dependencia (DIP)
class Reader {
Copy public abstract int read(){};
}
53
Heurísticos relacionados con el DIP
Interface
(clase abstracta)
Cliente
Implementación
(clase concreta)
54
Diseña para la interfaz
• Clases abstractas/interfaces:
• Tienden a cambiar menos frecuentemente.
• Las abstracciones son “puntos bisagra” donde es más sencillo
extender/modificar.
• En general no debería modificarse la clase/interfaz que
representa la abstracción (Principio OCP).
• Excepciones
• Algunas clases nunca van a cambiar, por ejemplo
• Clase String. No tiene sentido añadir una capa de abstracción
• En estos caso es más interesante utilizar la clase directamente
• Como en Java o C++.
55
Heurísticos relacionados con DIP
Depende de Depende de
Nivel de Nivel de Nivel de
Presentación Lógica Negocio Datos
56
Solución a las dependencias transitivas
Clase
Nivel Datos
57
Conclusiones SOLID
• Los sistemas software cambian durante su ciclo de vida, y los
diseños software deben acomodar estos cambios.
• El coste del cambio aumenta con el tiempo
58
Conclusiones SOLID
• Los principios SOLID facilitan el diseño OO evolutivo.
59
Para saber más
• Principios SOLID
• http://www.slideshare.net/bbossola/geecon09-solid
• http://www.desarrolloweb.com/manuales/programacion-orientada-
objetos-dotnet.html
• Principio OCP
• http://www.utopicainformatica.com/2010/09/principio-abierto-
cerrado.html
• http://danielmazzini.blogspot.com/2010/10/principio-abierto-cerrado-
ocp.html
• Principio SRP
• http://carlospeix.com/2010/11/principios-solid-1-ejemplo-con-srp-dip-y-
ocp/
• http://theartoftheleftfoot.blogspot.com/2010/06/solid-el-principio-de-
la.html
• http://joelabrahamsson.com/entry/the-open-closed-principle-a-real-
world-example
60
Para saber más
• Principio LSK
• http://javaboutique.internet.com/tutorials/JavaOO/
• Principio DIP
• http://martinfowler.com/articles/injection.html
• http://www.slideshare.net/MarcoManga/dependency-inversion-
principle
• Principio ISP
• http://www.oodesign.com/interface-segregation-principle.html
61