ISD. Curso 2003/04
Download
1 / 82

ISD. Curso 2003/04 - PowerPoint PPT Presentation


  • 68 Views
  • Uploaded on
  • Presentation posted in: General

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.

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha

Download Presentation

ISD. Curso 2003/04

An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -

Presentation Transcript


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


Patrones de arquitectura: ejemplo


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.


Catálogo de Gamma et al. (1995)


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


Decorador

  • Permite añadir responsabilidades adicionales a un objeto de forma dinámica.

  • Los decoradores proporcionan una alternativa flexible a la subclasificación para añadir funcionalidad.


ComponenteConcreta

+

operación

(

)

:

DecoradorConcreto1

DecoradorConcreto2

+

operación

(

)

:

+

operación

(

)

:

Decorador: solución

Componente

operación() {

componente

+

operación

(

)

:

componente.operación();

}

operación(){

Decorador

super.operacióÿ();

comportamientoAdicional();

}

+

operación

(

)

:

DecoradorConcretoN

+

operación

(

)

:

+

comportamientoAdicional

(

)

:


Decorador: ejemplos de aplicación (I)

  • Las herramientas de desarrollo de interfaces gráficas de usuario (GUI) proporcionan mecanismos para la creación y gestión de ventanas con diversas características:

    • gráficas, de texto, coloreadas, con barras de desplazamiento, etc.

    • el usuario de la herramienta puede crear ventanas con cualquier combinación de estas características.

  • En Java se utiliza la clase Component del paquete java.awt, y una clase Container que puede estar compuesta de un número indeterminado de componentes, que pueden ser:

    • simples (como JButton, JLabel, JTextField, etec.), o

    • componentes complejos (como los proporcionados por el paquete javax.swing – por ejemplo, JScrollPane, JTabbedPane o JSplitPane).

  • Otro ejemplo (también en Java) lo constituye la jerarquía de clases para la entrada y salida (java.io).


Decorador: ejemplos de aplicación (II)

*

Component

2

Container

JComponent

JScrollPane

1

JSplitPane

JTabbedPane

JComboBox

JLabel


Estado

  • Es un patrón de comportamiento para objetos.

  • Se debe usar si el comportamiento de un objeto

    • depende de su estado, y

    • se ve modificado en tiempo de ejecución.

  • Permite que un objeto cambie su comportamiento

    • cuando cambia su estado interno,

    • tal y como si el objeto cambiase de clase.

  • Permite emular el método becomes: de Smalltalk

    • en aquellos lenguajes donde no está disponible.


get / exception

vacío

get,

reset

put

lleno

put / exception

Estado: motivación (I)

  • Supongamos una clase Buffer1 que representa un buffer de tamaño 1, con métodos:

    • put y get para introducir y extraer elementos,

    • reset para vaciar el buffer.

  • Supongamos que la extracción de elementos de un buffer vacío y la inserción en un buffer lleno produce una excepción.


Estado: motivación (II)

class Buffer1<Elem> {

enum Estado {vacío,lleno);

private Estado est;

...

public void reset() {

if (est == Estado.lleno) get();

}

public void put(Elem e) {

if (est == Estado.vacío) {

almacenar(e);

est = Estado.lleno;

} else throw new RuntimeException(“Buffer lleno”);

}

... // De forma similar para get()

}


get / exception

put

vacío

uno

get, reset

reset

get

lleno

put

put / exception

Estado: motivación (III)

  • Supongamos una clase Buffer2

    • con comportamiento similar a la anterior

    • con capacidad de almacenar hasta dos elementos.


Estado: motivación (IV)

class Buffer2<Elem> extends Buffer1<Elem> {

enum Estado {vacío,uno,lleno);

private Estado est;

...

public void reset() {

if (est == Estado.uno) get();

else if (est == Estado.lleno) { get(); get(); }

}

public void put(Elem e) {

if (est == Estado.vacío) { almacenar(e);

est = Estado.uno; }

else if (est == Estado.uno) { almacenar2(e);

est = Estado.lleno; }

else throw new RuntimeException(“Buffer lleno”);

}

... // De forma análoga para get()

}


Estado: motivación (y V)

  • Los métodos deben ser codificados de nuevo

    • sin poder reutilizar las versiones de la superclase

    • incluso el método reset() debe ser redefinido.

  • El problema viene codificar el estado con un valor

    • lo que da lugar a un análisis de casos.

  • La solución consiste en codificarlo con una clase.

    • lo que permite usar polimorfismo.

    • cada rama de comportamiento condicional se corresponderá con una subclase.


EstadoN

Estado1

+método(…,Contexto)

+método(…,Contexto)

Estado: solución

Contexto

Estado

1

estado

1

+método(args)

contexto

+método(…,Contexto)

+cambiarEstado(Estado)

void método(args) {

estado.método(args,this);

}

void cambiarEstado(Estado est) {

estado = est;

}

void método(…, Contexto ctx) {

ctx.cambiarEstado(new EstadoM());

}


Buffer1

Estado

1

estado

1

contexto

+

get

(

)

:

void

+

get

(

)

:

void

+

put

(

)

:

void

+

put

(

)

:

void

Vacío

Lleno

void put(...) {

estado.put(…);

+

get

(

)

:

void

+

get

(

)

:

void

}

+

put

(

)

:

void

+

put

(

)

:

void

Buffer2

Lleno2

Vacío2

Uno

Estado: ejemplo de aplicación


Estado: algunas consideraciones

  • Los métodos de las subclases de Estado deben acceder a los atributos del objeto Contexto:

    • en particular, será necesario ir cambiando de estado.

    • hay que añadir un argumento adicional (this) para hacer referencia al objeto Contexto.

  • Toda la interfaz del Contexto debe delegarse al objeto Estado:

    void put(Elem e) {estado.put(e,this);}

  • Los cambios de estado implican la continua creación de instancias de las subclases de Estado.

    • si dichas subclases no tienen atributos, se puede aplicar el patrón Singular.


Singular

  • Es un patrón de creación para objetos.

  • Asegura que una clase tiene una única instancia

    • se puede generalizar a un número fijo de instancias.

  • Proporciona un mecanismo global de acceso a dicha instancia.

    Ejemplos:

    • contadores para asignar identificadores.

    • controladores de colas de impresión.

    • clases que representen tipos univaluados.

    • representación de clases como instancias de metaclases.


Singular: motivación

  • En ocasiones es necesario utilizar clases

    • que posean una única instancia

    • que sea accesible desde el resto del sistema.

  • Para ello se puede utilizar una variable global

    • pero esto no evita que se puedan crear varias instancias.

  • Una solución mejor es que la propia clase

    • controle la creación de esa única instancia

    • proporcione un acceso global a la misma.


Singular: solución

  • Para asegurar que se crea una sola instancia

    • el constructor de la clase no debe ser público.

  • El almacenamiento de la instancia

    • se hará en una variable de clase (estática).

  • El acceso a dicha variable

    • se hará a través de un método de clase (estático),

    • que devolverá una referencia a la instancia.

  • La creación de la instancia

    • se hará la primera vez que se invoque este método.


Instancia (...) {

if (instanciaÚnica == null)

instanciaÚnica = new Singular(...);

Singular

return instanciaÚnica;

-

instanciaÚnica

:

Singular

}

+

Instancia() :

Singular

-

Singular()

El constructor debe ser privado.

Singular: solución


Singular: implementación en Java

class Singular {

private static Singular instanciaÚnica;

... // declaración de atributos

private Singular(...) {

... // inicialización de atributos

}

public static Singular Instancia() {

if (instanciaÚnica == null)

instanciaÚnica = new Singular(...);

return instanciaÚnica;

}

... // métodos de instancia

}


Iterador

  • Proporciona una forma de acceder secuencialmente a los elementos de una colección sin mostrar la representación interna de la misma.

  • Permite definir diversos recorridos sobre una estructura, admitiendo el uso simultáneo de la misma por parte de varios clientes.


Iterador: solución

  • Cada cliente tendrá acceso a la colección mediante una instancia de la clase Iterador.

    • Dicha instancia será proporcionada por la colección a la que se quiere acceder.

  • La clase que implemente el iterador deberá tener acceso a la implementación de la colección sobre la que itera.

  • Se podrán proporcionar tantos iteradores como se desee, implementando diversos recorridos:

    • del principio al final (o al revés),

    • según el orden de los elementos almacenados,

    • de forma aleatoria, etc.


Iterador: ejemplo de aplicación

  • En Java se utiliza este patrón para iterar sobre colecciones.

Cliente

Collection

Iterator

+

add

(

elem

:

Object

)

:

boolean

+

next

(

)

:

Object

+

isEmpty

(

)

:

boolean

+

hastNext

(

)

:

boolean

+

contains

(

elem

:

Object

)

:

boolean

+

remove

(

)

:

void

List

ListIterator

<<

implementation

>>

<<

implementation

>>

+

previous

(

)

:

Object

BCSIterator

BeanContextSupport


Observador

  • Permite definir dependencias entre objetos de forma que si un objeto cambia de estado todos los dependientes de él son notificados y actualizados automáticamente


3.3. Refactorización de código

  • Cambios realizados en la estructura interna de un producto software:

    • para facilitar su comprensión

    • hacer menos costosa su modificación

    • sin cambiar su comportamiento observable.

  • Una refactorización es una pequeña modificación

    • pero la refactorización puede implicar varios cambios.

      Bibliografía:

      M. Fowler, Refactoring: improving the design of existing codeAddison Wesley, 1999


Ventajas de la refactorización

  • La refactorización:

    • mejora el diseño de software,

    • facilita la comprensión del software,

    • ayuda a encontrar errores,

    • hace más “ágil” el paso a la implementación.

  • Aplicar refactorización cuando:

    • se añada funcionalidad al sistema,

    • se necesite arreglar un error,

    • se haga una revisión de código.


Limitaciones de la refactorización

  • En sistemas centrados en una base de datos

    • puede suponer cambios en el esquema de la BD

    • fuerzan a migrar los datos (tarea larga y tediosa).

  • Puede suponer un cambio en las interfaces:

    • mantener las interfaces originales junto con las nuevas.

  • A veces es mejor diseñar un sistema desde cero que aplicar refactorizaciones.

  • Puede suponer una disminución de prestaciones

    • p.ej. eficiencia.


Situaciones susceptibles de refactorización

  • Clases grandes:

    • con muchas variables de instancia,

    • con métodos (parcialmente) duplicados.

  • Duplicación de código:

    • en la misma clase,

    • en distintas subclases de una clase dada,

    • en clases no relacionadas directamente.

  • Métodos excesivamente grandes:

    • con muchas variables temporales,

    • con muchos bucles.

  • Listas de parámetros muy amplias:

    • difíciles de comprender, inconsistentes y complejos de utilizar.


Situaciones susceptibles de refactorización (II)

  • Cambio divergente:

    • una clase necesita ser modificada de distintas formas, atendiendo a razones independientes;

    • suele ser conveniente dividir la clase en varias.

  • Un simple cambio produce pequeños cambios en muchas clases distintas.

  • Métodos que acceden en exceso a datos de clases ajenas.

  • Grupos cohesionados de datos que pueden constituir clases independientes.

  • Instrucciones de selección de casos.

  • Jerarquías de herencia paralelas.


Situaciones susceptibles de refactorización (y III)

  • Clases innecesarias.

    • si una clase y su subclase no difieren

  • Generalización especulativa:

    • previsión de métodos que puedan necesitarse en el futuro.

  • Uso excesivo de objetos intermediarios.

  • Alto grado de acoplamiento entre clases:

    • a menudo motivado por un mal uso de la herencia.

  • Bibliotecas de clases incompletas.


Catálogo de refactorizaciones

  • Renombrar método:

    • si el nombre del método no ofrece información sobre su propósito, cambiarlo de nombre.

  • Mover método:

    • si un método usa características de una clase distinta a la que lo incluye, cambiarlo a dicha clase.

  • Extraer método:

    • si un fragmento de código se puede agrupar atendiendo a algún criterio, se debe definir un método que encapsule este fragmento.

  • Introducir asertos:

    • cuando una sección de código supone alguna propiedad sobre el estado del programa, ésta debe hacerse explícita con un aserto.


Catálogo de refactorizaciones (II)

  • Encapsular atributo:

    • si una clase incluye un atributo público, debe definirse privado y proporcionar funciones de acceso.

  • Extracción de clases:

    • cuando una clase hace el trabajo que deberían hacer dos, crear una nueva clase y mover allí los campos y métodos necesarios.

  • Promoción de métodos:

    • cuando dos métodos están definidos de forma similar en dos subclases, subirlos a la clase padre común.

  • Construcción de un patrón de método:

    • si dos métodos en subclases distintas realizan pasos similares y en el mismo orden, cada paso se encapsula en un método con la misma signatura en ambas clases. Entonces, dichos métodos pueden subir en la jerarquía.


Catálogo de refactorizaciones (III)

  • Preservar la unidad de los objetos:

    • si se obtienen atributos de un objeto y después se pasan como parámetros en una invocación de método, es conveniente enviar el objeto completo.

  • Reemplazar herencia con delegación:

    • si una subclase utiliza solo parte de la interfaz de una superclase, o no quiere heredar atributos se debe delegar los métodos en la superclase, eliminando la subclasificación.

  • Cambiar asociación bidireccional a unidireccional:

    • si se tiene una asociación bidireccional pero una clase no necesita características de la otra, eliminar el extremo innecesario de la asociación.

  • Ocultar delegados:

    • si un cliente invoca una clase delegada de un objeto, es conveniente crear métodos sobre el servidor, para ocultar el delegado.


Catálogo de refactorizaciones (y IV)

  • Reemplazar condicional con polimorfismo:

    • si una estructura condicional selecciona el comportamiento de un objeto dependiendo del valor de un atributo, mover cada rama del condicional a un método redefinido en una subclase.

  • Introducir objeto nulo:

    • en presencia de comprobaciones reiteradas sobre un valor “null”, reemplazar el valor “null” con un objeto nulo.

  • Eliminar parámetros:

    • si se detecta que un parámetro es innecesario, hay que eliminarlo.


ad
  • Login