Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% encontró este documento útil (0 votos)
71 vistas61 páginas

Diseño SOLID OCW

Descargar como pdf o txt
Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1/ 61

Ingeniería

del So.ware II

Tema 3: Diseño So.ware


3.1. Principios SOLID

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]

Una rama de la ingeniería debe seguir varios pasos básicos para


convertirse en una profesión consolidada, destacando la comprensión
de la naturaleza de su conocimiento.

¿Cuál es y dónde está el enorme conocimiento práctico en el Diseño


Orientado a Objetos (DOO), basado en la experiencia acumulada en el
desarrollo de sistemas software durante todos estos años y aplicable
en la mayor parte de proyectos?

[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

Conocimiento BEST PRACTICES

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

•  Single-Responsability Principle (SRP)


•  Open-Close Principle (OCP)
•  Liskov Substitution Principle (LSP)
•  Interface Segregation Principle (ISP)
•  Dependency Inversion Principle (DIP)

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.

No importa dónde estés trabajando, qué estés construyendo o


qué lenguaje de programación estés usando, siempre habrá
una constante que va a estar con nosotros: el cambio.

No importa lo bien diseñes tu aplicación, a medida que pase el


tiempo una aplicación deberá crecer y cambiar o
inevitablemente irá quedando obsoleta.

Lo único que es constante es el cambio.

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.

Las entidades Software deben ser abiertas para extensiones,


pero cerradas para modificaciones. [2]

§  Abierto para extensiones


!  El comportamiento del módulo puede ser extendido
§  Cerrado para modificaciones
!  El código fuente del módulo no debe ser modificado
§  Los módulos deben diseñarse para que puedan ser extendidos sin
tener que ser modificados

[2] Meyer, Bertrand. Object-Oriented Software Construction. Prentice Hall. 1988


6
Abrir la puerta ...

MotorGasolina
Coche +arrancar()
+acelerar()
+frenar

•  ¿Cómo hacer que un Coche utilice un MotorGasoil o MotorSolar?


•  Únicamente cambiando la clase Coche!
•  ...en este diseño

7
... ¡Pero mantenerla cerrada!

Coche MotorAbstracto
+arrancar()
+acelerar()
+frenar

MotorGasolina MotorDiesel MotorSolar


+arrancar() +arrancar() +arrancar()
+acelerar() +acelerar() +acelerar()
+frenar +frenar +frenar

•  Una clase no debe depender de una clase concreta


•  Debe depender de una clase abstracta... o una interfaz
•  ...utililizando dependencias polimórficas (llamadas)

8
Open-Closed Principle (OCP)

•  La dependencia “uno a uno” se Clase A Clase B


transforma en una dependencia
de “uno a muchos”. Diseño cerrado/cerrado
•  Programa para la interfaz, no para
la implementación.
•  Establece una relación a una Clase A Clase abs B
clase abstracta (o interfaz) sólo en
los puntos de variabilidad del
programa.
Clase B1 Clase B2

Diseño abierto/cerrado

9
Ejemplo
public class Persona {
String nombre;
String apellidos;

public String getPersona(){


String s;
s=nombre+" "+apellidos;
return s;
}
}

¿Qué sucede si la representación de la Persona pudiese cambiar


a HTML, XML u otro formato?

10
Solución OCP Composición

public interface IFormateable {


public String getFormato(Persona p);
}

public class Persona { public class FormatoString implements IFormateable{


String nombre; public String getFormato(Persona p){
String apellidos; return p.nombre+" "+p.apellidos;
}
public String getFormato(IFormateable iformat){ }
String s=iformat.getFormato(this);
return s; public class FormatoXML implements IFormateable{
} public String getFormato(Persona p){
} String xml=”<xml> <nombre> ” + p.nombre+”</nombre>”;
xml=xml+”<apellido>”+p.apellidos+”</apellido></xml>”;
return xml;
}
}

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

public interface IMyExtension {


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

is_a is_a is_a is_a is_a

CocheGasolina CocheDiesel CocheSolar PersonaString PersonaXML


+arrancar() +arrancar() +arrancar() +visualizaDatos +visualizaDatos
+acelerar() +acelerar() +acelerar()
+frenar +frenar +frenar

14
Solución OCP Herencia

public abstract class Persona {


String nombre;
String apellidos;

public abstract String getFormato();


}

public class PersonaString extends Persona{


public String getFormato(){
String s;
s=nombre+" "+apellidos;
return s;
}
}

15
Herencia vs. composición

16
Herencia vs. composición

Herencia

•  Extensión de Caja-blanca (herencia):


•  No se puede cambiar el comportamiento heredado en run-
time (enlace estático)
•  La clase padre define en parte la representación física de las
subclases (La herencia rompe la encapsulación)
•  No se puede reutilizar únicamente la subclase.

17
Herencia vs. composición

Composición

•  Extensión de Caja-negra (composición) :


•  La composición se define dinámicamente en run-time a
través de la adquisión de referencias.
•  Requiere que los objetos tengan interfaces bien definidas.
•  No son visibles los detalles de los objetos.
•  La composición ayuda a mantener cada clase centrada en
una tarea.
•  Más objetos, menos complejos.

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)

Para más detalles ver: http://www.comscigate.com/JDJ/archives/0702/knoernschild

19
Solución herencia

•  ¿Cumple el principio de OCP?


•  ¿Puedo reutilizar el comportamiento de Saving en otra clase?
•  ¿Qué sucede si añadimos otro comportamiento (int discount())
que no depende del criterio con el que se ha creado la jerarquía
(p.ej tipo=joven,normal,mas65)?

20
Solución composición

•  ¿Cumple el principio de OCP?


•  ¿Puedo reutilizar el comportamiento de Saving en otra clase?
•  ¿Qué sucede si añadimos otro comportamiento (int discount())
que no depende del criterio con el que se ha creado la jerarquía
(p.ej tipo=joven,normal,mas65)?
21
Cambios en las cuentas existentes
•  ¿Qué sucede si…
•  ¿tenemos cuentas diferentes para miembros de la CEE y para los
demás?
•  ¿la cuenta pasa de débito a crédito?
•  ¿la cuenta pasa de joven a adulto?

22
Herencia vs. composición
“Una entidad debe ser abierta para su extensión y parametrización,
pero cerrada para su modificación” [3]

•  Los patrones de diseño se han definido para soportar la


variabilidad de manera flexible.

•  Los patrones de diseño hacen uso extensivo de la composición.

•  Usar la composición para aquellos puntos de variabilidad del


sistema.

[3] B.Appleton. Introducing Demeter and its Laws, http://www.bradapp.com/docs/demeter-intro.html

23
Del OCP al Siguiente principio

Ningún programa significativo puede ser 100% cerrado. [4]

PRINCIPIO DE RESPONSABILIDAD UNICA (SRP, Single


Responsibility Principle)

[4] R. Martin. The Open-Closed Principle. https://www2.cs.duke.edu/courses/fall07/cps108/papers/ocp.pdf

24
Principio: Single Responsability Principle

“Una clase debe tener una unica razón para cambiar” [5]

También conocido como High Cohesion (GRASP)


Cohesión es la medida en la que los comportamientos del objeto están
relacionados. Un elemento con baja cohesión realiza muchas tareas de
diversa índole. La alta cohesión es una característica deseable de los
diseños OO.

[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

•  El trompetista es un especialista. (Cohesión)

•  El trompetista puede trabajar fácilmente en varias orquestas,


el hombre orquesta es la orquesta. (Reusabilidad)
Imagen bajo licencia CC-BY-SA 2.0.
h t t p s : / / w w w. f l i c k r. c o m / p h o t o s /
84773840@N00/8489287609/ •  Siempre esperamos lo mismo de él, que toque la trompeta.
(Reducción de Side effects)

•  Si en las interpretaciones de la orquesta algo está mal con la


trompeta le pedimos que mejore o lo cambiamos, ¿al hombre
orquesta cómo lo cambiamos? Si se acaba la orquesta se
acaba el negocio. (Mantenibilidad)
Imagen bajo licencia CC-BY-SA 2.0.
https://www.flickr.com/photos/jikatu/
8356118191/

26
La gran orquesta

Imagen bajo licencia CC-BY-SA 2.0.


h t t p s : / / w w w. f l i c k r. c o m / p h o t o s /
buenosairesprensa/7983428800/

“Una orquesta sinfónica, igual que un sistema, se compone de un


grupo cohesionado de músicos especializados en un sólo
instrumento y mas aún, responsables de tocar una sola partitura
de cada pieza musical. Un solo trompetista no puede ejecutar
una sinfonía, el hombre orquesta tampoco o con mucha
dificultad lo lograría, entonces lo que debemos procurar es tener
clases con las funciones necesarias, y que estas a su vez sean
"pequeñas", con una única razón para cambiar en la medida de
lo posible (no siempre es posible tener clases con una sola
razón para cambiar) y orquestadas (es decir, diseñadas) para
componer un gran sistema, para interpretar una gran sinfonía.”

27
Principio: Single Responsability Principle

•  Cada responsabilidad debe residir en una clase


separada, ya que cada responsabilidad es una “fuente”
de cambio.
•  Una clase debe tener un solo motivo de cambio.
•  Las clases y métodos que siguen el principio de SRP
son más pequeñas y fáciles de entender y mantener.
Otra ventaja es que los métodos son más fáciles de
testear.

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

public class Downloader {


public void descargarFichero(String ubicacion) {
// descarga un fichero
}
}

public class Parser {


public void parsearFichero(String fichero) {
// parsea el contenido del fichero a XML
}
}

public class Storer {


public void guardarFichero(String fichero) {
// almacena el fichero en la BD
}
}

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?

“La herencia debe garantizar que, cualquier propiedad


probada para cualquier objeto de la superclase, debe ser
válida para cualquier objeto de las subclases” [6]

Los métodos que usan punteros (o referencias) a otras


clases, deben ser capaces de utilizar objetos de clases
derivadas (de la clase) sin saberlo.

[6] B. Liskov. The Liskov Substitution. https://en.wikipedia.org/wiki/Liskov_substitution_principle

33
La herencia Parece simple

class Pajaro { // tiene plumas, alas,.


Pajaro
public void vuela(); // Los pajaros pueden volar
}; + vuela()

class Loro extends Pajaro { // Un loro es un pajaro


public void imita(); // Puede repetir palabras.. is_a
};
// ... Loro
Loro miMascota=new Loro();
+ imita()
miMascota.imita(); // Mi mascota siendo loro puede imitar()
miMascota.vuela(); // mi mascota “es un” pajaro, puede volar

34
Los pingüinos no vuelan
class Pinguino extends Pajaro {
public void vuela() {
new Exception(“no puedo volar!”); }
};

void vueloComoPajaro (Pajaro pajaro) {


pajaro.vuela(); // OK si loro.
// Que pasa si pájaro es pingüino...OOOPS!!
}

§  No modela: “Los pingüinos no pueden volar”


§  Modela “Los pingüinos pueden volar, pero si lo intenta es un error”
§  Run-time error si intentan volar → no deseable
§  Piensa acerca de la sustituibilidad – Falla el principio de Liskov

35
Diseño por contrato
•  Comportamiento esperado de un método:
•  Requisitos esperados (Precondiciones)
•  Promesas ofrecidas (Postcondiciones)

“Cuando redefines un método en una subclase, sólo puedes


reemplazar su precondición por una más débil, y la postcondición
por una más estricta” [7]

⇒  Los métodos de las subclases no deben exigir más y no prometer menos

int Base::f(int x); int Derived::f(int x);


// REQUIERE: x es impar // REQUIERE: x es entero
// PROMETE: return par int // PROMETE: par int >50

[7] Meyer, Bertrand. Object-Oriented Software Construction. Prentice Hall. 1988


36
Diseño por contrato
•  La subclase conoce mejor la solución que la superclase
(más específica). Por tanto al usuario:
•  Le pedirá igual o menos (específico)
•  Le dará igual o más (específico)

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

•  Cualquier subclase que especialice Base tiene que tener


una precondición menos estricta y una postcondición más
acotada.
class ClaseInvocadora{
public int metodo(Base b){
int r=b.f(8); // el valor enviado puede que no sea impar
// r tiene que ser par sino la subclase está mal diseñada
}
}

38
¿Cuadrado IS-A Rectángulo?

Rectángulo Cuadrado

-  alto
-  ancho
?
+ setAlto(double al)
+ setAncho(double an)
+ getAlto():double
+ getAncho():double

¿Debería heredar Cuadrado de Rectángulo?

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

Las subclases pueden extender a las superclases sin modificar el


comportamiento

40
LSP tiene que ver con la semántica y el
reemplazo

•  Debe quedar claramente documentado el


siginificado y propósito de cada clase y método.
•  La falta de comprensión induce “de facto” a la violación
del LSP
•  El reemplazo es crucial
•  Siempre que cualquier clase sea referenciada en
cualquier código del sistema, cualquier subclase existente
o futura debe ser 100% reemplazable.
•  Porque, más pronto que tarde, alguien sustituirá la
subclase.
.
•  Es casi inevitable

41
¿Es un Rectángulo un Cuadrado?

Un Cuadrado podría ser un Rectángulo, pero un objeto


Cuadrado no es un objeto Rectángulo. ¿Por qué?

El comportamiento de un objeto Cuadrado no es


consistente con el comportamiento de un objeto
Rectángulo. Y en el software, el comportamiento es una
parte esencial.

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.

Cliente Clase Servicio

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” (*)

(*) Para más información consultar https://devexperto.com/principio-de-sustitucion-de-liskov/

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]

•  Varias interfaces específicas son mejores que una


interfaz de propósito general.

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

[9] R. Martin. Interface Segregation Principle. https://en.wikipedia.org/wiki/Interface_segregation_principle

47
Ejemplo
interface Animal {
public void alimentar();
public void acariciar();
}

implementa implementa

class Perro implements Animal { class Escorpion implements Animal {


public void alimentar() { public void alimentar() {
// alimentar al perro // alimentar al escorpión
} }
public void acariciar() { public void acariciar() {
// acariciar al perro // !!! Pero que haces!!!
} }
} }

48
Ejemplo
interface IMascota{ interface Animal {
public void acariciar(); public void alimentar();
} }

implementa implementa

class Perro implements Animal, IMascota { class Escorpion implements Animal {


public void alimentar() { public void alimentar() {
// alimentar al perro // alimentar al escorpión
} }
public void acariciar() { }
// acariciar al perro
}
}

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.

II. Las abstracciones no deben depender de los detalles.


Los detalles deben depender de las abstracciones

§  OCP indica el objetivo; DIP indica el mecanismo.


§  Una superclase no debe conocer ninguna de sus subclases.
§  Los módulos con detalles de implementación no son interdependientes,
sino que su dependencia se define en base a abstracciones.

[8] R. Martin. Dependency Inversion Principle. https://en.wikipedia.org/wiki/Dependency_inversion_principle

50
Arquitectura Procedural vs. OO

Arquitectura
Procedural

Arquitectura
Orientada a
objetos

51
Ejemplo aplicando enfoque procedural

enum OutputDevice {printer, disk};


Copy void Copy(OutputDevice dev){
int c;
while((c = ReadKeyboard())!= EOF)
Read Write if(dev == printer)
Keyboard Printer WritePrinter(c);
else
Write
Disk WriteDisk(c);
}

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

Reader Writer class Writer {


public abstract void write(int i);
};

void copy(Reader r, Writer w){


Keyboard Printer Disk
Reader Writer Writer int c;
while((c = r.read()) != EOF)
w.write(c);
}

53
Heurísticos relacionados con el DIP

Diseña para la interfaz, no para la implementación!

Utiliza la herencia para evitar en enlace directo entre clases.

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

Evita dependencias transitivas

•  Evita estructuras en las que las abstracciones de


alto nivel dependen de abstracciones de bajo nivel:
•  En el ejemplo, el nivel de presentación depende finalmente
del nivel de datos.

Depende de Depende de
Nivel de Nivel de Nivel de
Presentación Lógica Negocio Datos

56
Solución a las dependencias transitivas

Utiliza la herencia y las clases abstractas para eliminar las


dependencias transitivas:

Nivel depende de Interface


Presentación Lógica Negocio
implementa

Clase depende de Interface


Lógica Negocio Nivel Datos
implementa

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

•  A la vez que se reduce la productividad.

58
Conclusiones SOLID
•  Los principios SOLID facilitan el diseño OO evolutivo.

•  Los principios SOLID son el primer paso para realizar


diseños complejos, sin embargo se necesitan
técnicas más sofisticadas.

Los patrones de diseño

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

También podría gustarte