820 likes | 948 Views
ISD. Curso 2003/04. Módulo 3: Diseño orientado a objetos. Contenidos. Heurísticas de diseño orientado a objetos. noción de heurística clasificación catálogo de heurísticas Patrones de diseño. noción de patrón de diseño clasificación catálogo de patrones Refactorización.
E N D
ISD. Curso 2003/04 Módulo 3:Diseño orientado a objetos
Contenidos • Heurísticas de diseño orientado a objetos. • noción de heurística • clasificación • catálogo de heurísticas • Patrones de diseño. • noción de patrón de diseño • clasificación • catálogo de patrones • Refactorización. • noción de refactorización • situaciones susceptibles de refactorización • catálogo de refactorizaciones
Parte I. Heurísticas de diseño • Recomendaciones para obtener un diseño orientado a objetos adecuado: • eliminando la complejidad accidental, • favoreciendo la reutilización, • facilitando el mantenimiento, • mejorando el paso a la implementación. • No siempre es posible aplicarlas. Bibliografía: A. J. Riel, Object-Oriented Design Heuristics, Addison Wesley, 1996
Heurísticas de diseño. Clasificación • Los bloques básicos: clases y objetos • independencia modular, cohesión y acoplamiento. • Topología de las aplicaciones • orientadas a objetos vs. orientadas a acciones • Relaciones entre objetos • composición, agregación, asociación... • La herencia • ... y la herencia múltiple • Otras heurísticas de diseño
Punto Punto Punto abscisa() x + x, y: float - x, y: float - rho, theta: float + trasladar(float, float) + distancia(Punto) + abscisa() : float + ordenada(): float + trasladar(float, float) + distancia(Punto) : float + abscisa() : float + ordenada(): float + trasladar(float, float) + distancia(Punto) : float Los bloques básicos: clases y objetos (I) • Los atributos deben declararse ocultos. • el acceso debe realizarse a través de métodos, y sólo cuando sea necesario. - Se evita la dependencia de los clientes. - Se facilita el mantenimiento.
AlarmClock alarm_off Person -alarm : time TimeClockSafe alarm_on -alarm_status : boolean -age : int -money : int -now : time -name : String -openingTime : time +alarm_off() +alarm_on() Los bloques básicos: clases y objetos (II) • Los clientes de una clase deben depender de la interfaz pública de esta, pero una clase no debe depender de sus clientes. • debe aclararse la dirección de las asociaciones • un modelo no debe depender de la interfaz de usuario - Se facilita la reutilización.
Los bloques básicos: clases y objetos (III) • El número de métodos de la interfaz de una clase debe ser reducido. • Si el tamaño de la interfaz es excesivo, se debe revisar el diseño, considerando la posibilidad de realizar una distribución de la clase en subclases. - Se facilita la comprensión y el uso de la clase.
Los bloques básicos: clases y objetos (IV) • Las clases deben ofrecer una interfaz mínima que todos sus potenciales clientes comprendan: • construcción (manteniendo integridad) • copia (profunda vs. superficial) • igualdad (de objetos vs. de referencias) • representación (impresión) • serialización (transmisión, almacenamiento) • autocomprobación • etc.
Los bloques básicos: clases y objetos (V) • Evitar incluir en la interfaz pública de una clase detalles de implementación. • métodos auxiliares privados. • métodos que deberían figurar protegidos. • Las clases sólo deberían utilizar detalles públicos del comportamiento de otras clases. • no abusar de mecanismos de exportación selectiva • “friend” de C++. • paquetes de Java. - Disminuye el acoplamiento.
Los bloques básicos: clases y objetos (VI) • Una clase debe reflejar la abstracción de una única entidad dentro del modelo del dominio. • si una clase represente varias entidades, estamos creando un sistema excesivamente centralizado • la clase debe partirse en varias, o bien • la clase debe subclasificarse de forma adecuada. • si una entidad esta representada por varias clases, éstas corresponden probablemente a funciones. • las funciones deben agruparse en una clase.
Los bloques básicos: clases y objetos (y VII) • Los datos y comportamiento relacionados deben mantenerse en una misma clase. • evitar implementar fuera de una clase comportamiento que le corresponde a ella. • Una clase no debe incluir información disjunta (comportamiento asilado). • será necesario dividir la clase en varias. • Vigilar que las abstracciones que modelan varias clases no se correspondan simple-mente con roles que juegan los objetos. • el criterio ha de ser que el comportamiento sea distinto. • un objeto puede utilizar sólo un subconjunto de su comportamiento.
Topología de las aplicaciones (I) • Aplicaciones orientadas a acciones: • Descomposición funcional • Control centralizado • Aplicaciones orientadas a objetos: • Descomposición de datos • y sus funciones asociadas • Control descentralizado El desarrollo orientado a objetos permite abordar mejor la complejidad esencial, pero implica un cambio de paradigma y un proceso de aprendizaje.
Topología de las aplicaciones (II) • No deben crearse clases/objetos excesiva-mente protagonistas (God classes). • Clases como Controller,Manager,System suelen resultar peligrosas. • a menudo estas clases manejan información disjunta • Vigilar las clases con muchas funciones de acceso (get/set). • los datos y el comportamiento relacionados deben figurar en la misma clase. • Es conveniente distribuir la inteligencia del sistema de la forma más uniforme posible.
Topología de las aplicaciones (III) • Modelar el mundo real siempre que sea posible. Esta heurística es a menudo incompatible con: • distribuir la inteligencia del sistema • no definir clases “protagonistas” • mantener datos y comportamiento asociado en una misma clase. • Favorece la comprensión del sistema y el mantenimiento.
Topología de las aplicaciones (y IV) • Eliminar clases irrelevantes • clases que son externas al sistema, • clases que sólo incluyen métodos como “get”, “set” pueden ser sustituidas por atributos en otras clases. • No considerar acciones como clases • clases con nombres que son verbos o derivados, • clases con una única acción. • Determinar qué “actores” del análisis deben mantenerse en el diseño: • eliminar las clases que modelan agentes irrelevantes para el diseño.
Relaciones entre objetos (I) Grados de la relación de uso entre objetos: • Composición • un objeto contiene una instancia de la clase usada • Agregación • un objeto contiene una referencia al objeto que usa • Acceso global • todos los objetos conocen al objeto usado • Acceso temporal • la referencia al objeto usado se obtiene como parámetro • Creación local • un objeto crea localmente otro para usarlo
Relaciones entre objetos (II) • Una clase no debe contener más objetos de los que se puedan recordar. • algunos estudios fijan el máximo en seis. • Minimizar el número de clases con las que se colabora. • se puede conseguir usando clases contenedoras. • Minimizar el número de mensajes intercambiados. • tanto correspondientes a la misma operación, • como a operaciones distintas.
Relaciones entre objetos (y III) • Si una clase contiene objetos de otras, debería enviar mensajes a estos objetos. • la contención debe implicar una relación de uso. • Los objetos que comparten ámbito no deben tener relaciones de uso entre ellos. • la relación se debe establecer a través del continente. • Un objeto debe saber qué objetos contiene, pero no quién lo contiene a él. • la relación de contención es unidireccional.
La herencia (I) • La herencia debe usarse para modelar la especialización. • debe evitarse la herencia de implementación. • Es beneficioso potenciar la profundidad en la jerarquía de herencia • Las características comunes deben identificarse lo más alto posible. • si varias clases comparten estado y comportamiento, estos se incluirán en una clase, de la que aquéllas heredarán. • Si varias clases comparten sólo datos, estos deben definirse en una clase aparte, • contenida (no heredada) en cada una de ellas.
La herencia (II) • Las clases derivadas pueden conocer sus clases base. • pero no al revés. • Toda clase abstracta debe tener clases herederas. • la raíz de una jerarquía de herencia suele ser abstracta. • No confundir instancias de una clase con clases derivadas. • No es habitual necesitar crear clases en tiempo de ejecución. • lo que se deben crear son objetos.
La herencia (III) • Evitar hacer análisis de casos explícito sobre el tipo de un objeto. • se debe utilizar para ello el polimorfismo. • El análisis de casos sobre el valor de un atributo constituye a menudo un error. • se puede aplicar el patrón Estado.
La herencia (y IV) • No se debe confundir una relación de contención opcional con la necesidad de heredar. • esto suele derivar en una proliferación de clases. • el patrón Decorador suele ser una solución. • Cuando se construya una jerarquía de herencia, se debe construir marcos reutilizables, en vez de componentes reutilizables. • esta es la idea detrás de los patrones de diseño.
... y la herencia múltiple • La herencia múltiple es bastante inusual. • debemos asegurarnos de que ninguna de las superclases es heredera de otra. • evitar utilizar herencia cuando en realidad se está modelando una relación de composición. • Incluso cuando forme parte del modelo en el análisis, puede ser conveniente evitar la herencia múltiple en el diseño. • combinando herencia simple y composición. • utilizando implementación múltiple de interfaces.
Otras heurísticas de diseño • Siempre que sea posible decidir entre una relación de agregación y otra de asociación, es mejor optar por la primera. • No se deben utilizar datos o funciones globales para mantener información entre los objetos de una clase. • este es el objetivo de los métodos y variables de clase. • Un diseñador no debe atender a cuestiones como el lenguaje de implementación, características físicas de las plataformas donde se va a implantar el sistema, y otras similares, si esto supone una corrupción del diseño lógico del sistema.
Parte II. Patrones de diseño • La noción de patrón de diseño. • Ventajas e inconvenientes. • Clasificación de patrones de diseño. • Ejemplos. Bibliografía: E. Gamma et al., Design Patterns, Addison Wesley, 1995
Patrones de diseño • Los L.O.O. facilitan la reutilización de código • pero un buen diseño es la clave para una reutilización efectiva. • Un diseñador experimentado producirá diseños más simples, robustos y generales • y más fácilmente adaptables a cambios. • Los patrones de diseño pretenden transmitir esa experiencia: • son soluciones efectivas a problemas comunes.
La noción de patrón • Un patrón es una solución probada • aplicable a un determinado tipo de problemas • que aparecen repetidamente en el desarrollo de software. • No son bibliotecas de clases • sino un “esqueleto” básico • que se debe adaptar a las peculiaridades de la aplicación. • Los patrones se describen en forma textual • acompañados de diagramas y pseudocódigo. • Dos niveles: • patrones de diseño y de arquitectura.
Patrones de arquitectura • Son esquemas de organización de un sistema. • Especifican una serie de subsistemas • y sus responsabilidades respectivas. • Incluyen reglas para organizar las relaciones entre ellos. • Ejemplos: • niveles • particiones • filtros y tuberías • cliente-servidor
Interfaz de usuario Constr. expresiones Ficheros Análisis semántico Ejecut. Op. Sustituir Racionalizar Evaluar Guardar Cargar Análisis semántico Patrones de arquitectura: ejemplo INTÉRPRETE DE COMANDOS SISTEMA OPERATIVO
Patrones de diseño • Tienen un nivel de abstracción menor. • Están más próximos a la implementación final. • Su uso no se refleja en la estructura global del sistema. • Ejemplos: • Adaptador • Estado • Singular • Iterador • Decorador
Ventajas de los patrones de diseño (I) • Son soluciones concretas: • un catálogo de patrones es un conjunto de recetas de diseño. • cada patrón es independiente del resto. • Son soluciones técnicas: • dada una situación, los patrones indican cómo resolverla. • existen patrones específicos para un lenguaje determinado. • Se aplican en situaciones muy comunes: • proceden de la experiencia. • han demostrado su utilidad.
Ventajas de los patrones de diseño (II) • Son soluciones simples: • indican cómo resolver un problema utilizando un pequeño número de clases relacionadas de forma determinada. • no indican cómo diseñar un sistema completo, sino sólo aspectos puntuales del mismo. • Facilitan la reutilización del código y del diseño: • los patrones favorecen la reutilización de clases ya existentes y la programación de clases reutilizables. • la propia estructura del patrón se reutiliza cada vez que se aplica.
Inconvenientes de los patrones de diseño • Su uso no se refleja claramente en el código: • a partir de la implementación es difícil determinar qué patrón de diseño se ha utilizado. • no es posible hacer ingeniería inversa. • Referencias a “this”: • a menudo los mensajes se resuelven mediante delegación. • Es difícil reutilizar la implementación del patrón: • el patrón describe roles genéricos, pero en la implementación aparecen clases y métodos concretos. • Suponen cierta sobrecarga: • se usan más clases de las estrictamente necesarias. • la delegación implica un nivel de indirección más.
MVC: Un ejemplo de patrón de diseño • En Smalltalk-80 se utiliza una relación entre clases denominada MVC (Modelo/Vista/Control), que se repite en otras muchas situaciones. • MVC se puede considerar como un ejemplo típico de patrón de diseño. • Se utiliza para construir interfaces de usuario distinguiendo tres tipos de objetos: • El modelo es el objeto que se desea evaluar. • La vista (o vistas) dan la información visual del objeto y permiten el acceso al mismo mediante una interfaz de usuario. • El controlador se encarga de atender las peticiones que el usuario realiza sobre la vista, invocando las acciones necesarias sobre el modelo.
Modelo-Vista-Controlador • Podemos disponer de: • Un modelo. • Varias vistas. • Varios controladores. • Vistas y Controladores están muy relacionados. • A veces puede disponerse de varias vistas para el mismo controlador.
La clase Cambio • Posteriormente la utilizaremos como modelo. • Permite realizar cambios de euros a pesetas. • El constructor decide el cambio de euros a pesetas. • Métodos para: • Pasar una cantidad de pesetas a euros. • Conocer los euros que han resultado. • Pasar una cantidad de monedas a pesetas. • Conocer las pesetas que han resultado. • Hay métodos para conocer: • Moneda origen, moneda destino, cantidad origen y cantidad destino.
La clase Cambio public class Cambio { final static public String EUROS = "euros"; final static public String PESETAS = "pesetas"; float factor; String monedaF; float valorF; float valorI; public Cambio(float fac) { monedaF = EUROS; factor = fac; // de paso de ptas a euros } public void aPesetas(float cantidad) { public float valorF(){ monedaF = PESETAS; return valorF; valorI = cantidad; } valorF = cantidad * factor; public float valorI(){ } return valorI; public void aEuros(float cantidad) { } monedaF = EUROS; public String monedaF(){ valorI = cantidad; return monedaF; valorF = cantidad / factor; } } public String monedaI() { return (monedaF.equals(EUROS)?PESETAS:EUROS); } }
Test para la clase Cambio public class TestCambioConsola { static public void main(String args []) { Cambio cpe = new Cambio(166.386f); System.out.println("Comienza"); System.out.println ("Le damos 4578 pesetas"); cpe.aEuros(4578); System.out.println ("Y devuelve "+ cpe.valorF()+" " + cpe.monedaF()); System.out.println ("Le damos 35.67 euros"); cpe.aPesetas(35.67f); System.out.println ("Y devuelve "+ cpe.valorF()+" " + cpe.monedaF()); } } Comienza Le damos 4578 pesetas Y devuelve 27.514334 euros Le damos 35.67 euros Y devuelve 5934.9883 pesetas
Ejemplo: Modelo-Vista-Controlador Cambio Controlador Vistas CtrCambio Modelo
Vistas: Vista1 y Vista2 • Interfaz para las vistas: public interface VistaCambio { void limpiar(); float valorEntrada(); void agregaLinea(String s); void controlador(CtrCambio crt); } • El método controlador(CtrCambio ctrl) • Pone al controlador ctrl como oyente de los componentes adecuados. • Creamos dos vistas distintas sobre el mismo controlador • Vista1 y Vista2
Controlador: CrtCambio • Mantiene dos variables de instancia: • El modelo: cpe • La vista : vc public CrtCambio(VistaCambio v, Cambio c ) { vc = v; // La vista cpe = c; // El modelo }
Aplicación import javax.swing.*; public class TestCambio { public static void main(String args[]) { System.out.println("Starting Cambio..."); VistaCambio vis = new Vista1(); // Vista2() Cambio mod = new Cambio(166.386f); CrtCambio crt = new CtrCambio(vis,mod); vis.controlador(crt); ((JFrame)vis).pack(); ((JFrame)vis).setTitle("Ejemplo Cambio"); ((JFrame)vis).setVisible(true); } }
Clasificación de los patrones de diseño • Según su propósito: • Patrones de creación. • problemas de creación de instancias. • Patrones estructurales. • problemas de relaciones entre clases y/u objetos. • Patrones de comportamiento. • forma en que los objetos interactúan y distribuyen sus responsabilidades. • Según su ámbito: • Patrones de clases. • se resuelven mediante relaciones de herencia entre clases. • Patrones de objetos. • se resuelven mediante relaciones de uso entre objetos.
Adaptador • Adapta la interfaz de una clase a la interfaz esperada por sus clientes. • Favorece la reutilización (de la clase adaptada) y permite la colaboración con interfaces incompatibles. • También se conoce como Wrapper. • Es un patrón estructural con una versión para clases y otra para objetos.
Adaptador: motivación • Se está desarrollando un editor de dibujos que permite realizar diagramas a partir de elementos gráficos como líneas, círculos, texto, etc. • Un elemento fundamental de dicho sistema es la clase ObjetoGráfico, que proporciona operaciones para modificar su forma (editar()) y para representarlo (dibujar()). • Esta clase se especializa para cada tipo de objeto gráfico:Línea, Círculo, etc., clases donde se han implementado adecuadamente dichas operaciones. • Sin embargo, la edición y representación de textos es una tarea complicada, por lo que se desea reutilizar la claseText de la biblioteca de clases del entorno de programación. • No obstante, la interfaz deText(con operaciones comoedit()ydraw()) no se corresponde con la declarada porObjetoGráfico. • Por este motivo, se necesita desarrollar una claseTexto(adaptador) que adapte la claseText(adaptada) a la interfaz declarada porObjetoGráfico(objetivo).
Adaptador: motivación ObjetoGráfico Text dibujar( ) draw( ) editar( ) edit( ) Línea Círculo dibujar( ) dibujar( ) editar( ) editar( )
ObjetoGráfico Text dibujar( ) draw( ) editar( ) edit( ) text Línea Círculo Texto dibujar( ) dibujar( ) dibujar( ) editar( ) editar( ) editar( ) dibujar( ) { text.draw( ); } editar( ) { text.edit( ); } Adaptador: versión para instancias
ObjetoGráfico Text dibujar( ) draw( ) editar( ) edit( ) Línea Círculo Texto dibujar( ) dibujar( ) dibujar( ) editar( ) editar( ) editar( ) dibujar( ) { draw( ); } editar( ) { edit( ); } Adaptador: versión para clases