Descargar la presentación
La descarga está en progreso. Por favor, espere
Publicada porMarcos López Maestre Modificado hace 9 años
1
Técnicas avanzadas de programación Fundamentos de programación orientada a objetos
2
Índice Introducción Generalización o abstracción Encapsulamiento
Modularidad Jerarquía
3
Introducción
4
¿Qué es un método? Objetivos Principios (axiomas)
“No basta tener ingenio, lo principal es aplicarlo bien” (Descartes, Discurso del método) Objetivos Principios (axiomas) Reglas de formación (notación). Reglas de transformación (lo que se debe hacer para cumplir los principios). Modelo de proceso (metarreglas que indican el orden de actividades) “Primero, no admitir como verdadera cosa alguna que no supiese con evidencia [...]. No comprender en mis juicios nada más que lo que se presentase tan clara y distintamente, que no hubiese ninguna ocasión de ponerlo en duda” “Dividir cada una de las dificultades que examine en cuantas partes fuese posible y en cuantas requiere su mejor solución” “Conducir ordenadamente mis pensamientos empezando por los objetos más simples y más fáciles de conocer, para ir ascendiendo poco a poco, gradualmente, hasta el conocimiento de los más compuestos” “Hacer en todo unos recuentos tan integrales y unas revisiones tan generales que llegase a estar seguro de no omitir nada” (Descartes, Discurso del método)
5
Cambio de mentalidad Paradigma tradicional:
Procedimiento 1 Datos Paradigma tradicional: Procedimiento 2 Objeto Objeto Paradigma orientado a objetos: Métodos Métodos Datos Datos Objeto Métodos Datos
6
Objetivos de la ingeniería (I)
Corrección: el software realiza la especificación de requisitos Robustez: capacidad de reaccionar ante condiciones excepcionales Extensible (facilidad de mantenimiento): poder cambiarlo en función de modificaciones de los requisitos. Para ello, es muy importante usar principios de calidad en el diseño: modularidad, encapsulamiento, etc. Reutilizable: poder ser utilizado en otras aplicaciones
7
Objetivos de la ingeniería (II)
Facilidad de uso: tanto si el usuario es un consumidor final (se habla de “interfaces amistosos”), como si es otro programador. En este último sentido una buena especificación de servicios, API o Application Programing Interface, ahorra costes de mantenimiento y reutilización. Portabilidad: usable en diversos entornos. Integrable (compatibilidad): posibilidad de compartir datos con otras aplicaciones (integración de datos o integración por medio de conexión) Verificable: facilidad para poner a prueba la aplicación
8
Principios y reglas de la ingeniería OO
Para cumplir los objetivos tenemos que aplicar principios. Cada principio se realiza mediante la aplicación de reglas: 1. Generalización o abstracción. Reglas: Mantener la coherencia de clases Mantener la coherencia de métodos Generalizar los tipos de datos y métodos Amplia cobertura del servicio Minimizar la información global Gestión de excepciones Precaución con la optimización 2. Encapsulamiento. Reglas: Encapsular las clases Ocultar estructuras y algoritmos internos 3. Modularidad. Reglas: Minimizar el acoplamiento. Comprensibilidad y trazabilidad. Arquitectura en capas. 4. Jerarquía. Reglas: Definir orden de herencia sin abusar de la profundidad Definir orden de composición
9
Principios y objetivos
Generalización: Centrarnos en lo genérico y eliminar u ocultar detalles (el cómo) Menor coste Encapsulamiento: Utilizamos los servicios de un objeto a través de su interfaz externo CORRECCION ROBUSTEZ EXTENSIBILIDAD REUSABILIDAD Modularidad Descomponemos de forma lógica Jerarquía Ordenamos por niveles de herencia y composición Mayor calidad
10
Generalización o abstracción
11
Generalización: mantener la coherencia (I)
Toda clase debe aspirar a ser una representación genérica de una entidad del dominio del problema o del dominio de la aplicación. Genérica o abstracta no significa que hace de todo, sino que cumple su papel de forma genérica. Las siguientes reglas nos permiten hacer software lo más genérico posible: Mantener la coherencia o cohesión de la clase: una clase tiene un rol (papel o función) o un conjunto de roles lógicamente relacionados. Si tiene varios roles que no están relacionados resulta incoherente. Buenos ejemplos: class ventana_configuración { ....} class control_acceso_usuario { .... } Pero no: class calculador_e_interfaz_ de calculo { ... } class de_todo_un_poco { ... }
12
Generalización: mantener la coherencia (II)
Mantener la coherencia de los métodos: tratemos de hacer un componente de software que haga una cosa, para que la haga bien. Una parábola: Un pato se acerca a un delfín y le dice “Observa mi superioridad, soy capaz de volar andar y nadar”, a lo que el delfín responde “Bien, pero tu nadas lentamente, vuelas de manera pesada y andas con torpeza, mientras que yo nado muy bien”. La moraleja es que el software es más correcto, robusto y reutilizable si se parece al delfín de la parábola, no al pato. En el siguiente ejemplo el método hace tres cosas: pide por teclado el radio del circulo, calcula el área y la muestra: void funcion_incoherente() throws IOException { float radio, area; String cadena_teclado; BufferedReader entrada = new BufferedReader(new InputStreamReader(System.in)); System.out.println( "Teclee el radio del círculo:"); cadena_teclado = entrada.readLine(); radio = Float.parseFloat( cadena_teclado ); area = radio * radio * F; System.out.println( "El área es " + area); } ¿Cómo se haría de manera coherente o cohesiva?
13
Generalización: generalizar datos y métodos
Generalizar tipos de datos y métodos: la clase debe cumplir su rol en una enorme variedad de contextos. Se trata de generalizar respecto a los métodos y respecto a los tipos de datos: Respecto a los métodos, los parámetros pueden ser valores nulos, extremos o situados fuera de los limites. Aún así, el comportamiento (método) de la clase debe ser correcto y robusto. Respecto a los tipos de datos. Por ejemplo, una clase que ordena un vector de figuras en función de su extensión o de su perímetro puede ser: class organizador_figuras { .... } Un ejemplo de escasa generalización sería el siguiente: class ordenador_perímetro_círculos { ... } class ordenador_extensión_rectángulo { ... } Para generalizar nos apoyamos en la gestión de excepciones, la herencia y el polimorfismo. ¿Cuál de estas dos clases representa de forma genérica (abstracta) su posición? class FIGURA { int pos_x; int pos_y; ..... } class FIGURA { Point Posicion; ..... }
14
Generalización: amplia cobertura del servicio
Amplia cobertura del servicio. Cuando decimos que una clase tiene un rol estamos diciendo que proporciona un servicio. Trataremos de hacer que el servicio sea de amplio espectro, no limitándose al contexto presente. Esta es una situación muy común, por ejemplo nuestros equipos de música tienen numerosas funciones, aunque la mayoría de los usuarios sólo usan una parte de ellas. Sería absurdo que los creadores de estos equipos los hubieran diseñado para usuarios que fueran como ellos. Lo mismo ocurre con las clases, darán un servicio por medio de numerosas funciones, aunque lo normal es que la mayoría de los usuarios no las usen todas. Una de las formas de implementar una cobertura amplia es usar la sobrecarga de métodos: void imprimir( String cadena ) {....} void imprimir( Figura fig ) {....} void imprimir( Imagen img ) {....}
15
Generalización: minimizar información global
Minimizar la información global. Tratemos de minimizar las referencias externas. El uso de objetos globales impone restricciones a la reutilización del software. Resulta más adecuado manejar argumentos. En este pequeño ejemplo el método usa float base, que se le pasa como argumento: public float calcular_cuadrado( float base ) { return (base * base); } Una ventaja de esta versión es que resulta evidente que el interfaz del método, public float calcular_cuadrado( float base ), usa un tipo float para el cálculo. La dependencia es evidente. En este otro ejemplo el método usa datos globales (concretamente son atributos static de la clase calculador). Pero este uso no aparece en el interfaz del método: public void calcular_cuadrado( ) { calculador.resultado = calculador.base * calculador. base; Esta forma de programar hace que la dependencia del método respecto a los datos no sea explicita.
16
Generalización: gestión de excepciones
Una excepción es una situación anómala que normalmente interrumpe el flujo lógico del programa. Programar con generalidad también implica hacer aplicaciones que superen estas situaciones. En el siguiente ejemplo protegemos a la aplicación de: Un error en la entrada por teclado. Un error de conversión de texto a tipo numérico. Gracias a este tratamiento de excepciones la aplicación no se paraliza, sino que reacciona ante el problema: try { /* Creo el objeto 'entrada', es un lector de entradas por teclado */ BufferedReader entrada = new BufferedReader(new InputStreamReader(System.in)); System.out.print("Escriba el radio: "); c = entrada.readLine(); // Leo un String de la entrada de teclado radio = Double.parseDouble(c); // Convierto el String en double System.out.print("El área es: " * radio * radio); } catch ( NumberFormatException e ) { System.out.print( "Error en el formato del número" ); } catch ( IOException e ) { System.out.print( "Error de entrada" ); }
17
Generalización: optimizar con precaución
Como norma elemental conviene optimizar una vez que funciona el programa. Con frecuencia los programadores invierten demasiado esfuerzo intentando optimizar partes del código que se usan con poca frecuencia. Se trata de comparar los costes con los posibles (y normalmente escasos) beneficios. La optimización generalmente amenaza la comprensibilidad, extensibilidad y reusabilidad del software. Como norma general: es más importante hacer software legible y sensato que software optimizado e ilegible. Ejemplo de código JDBC optimizado y poco legible: ResultSet rs = DriverManager.getConnection( "jdbc:mysql://localhost/prueba","root", "palabra“). createStatement().executeQuery( sentencia );
18
Encapsulamiento
19
Encapsulamiento: introducción (I)
El principio de encapsulamiento es una forma moderna de denominar al tradicional principio de ocultamiento de la información. El principio nos indica que debemos hacer que los detalles de la clase estén ocultos para el exterior (también se denomina implementación encapsulada). Es un principio de la ingeniería en general. Si estamos diseñando un equipo de música no parece adecuado que permitamos al usuario operar sobre las placas para cambiar el volumen o el tono. Los botones del equipo son el interfaz del sistema, la implementación queda encapsulada (oculta).
20
Encapsulamiento: introducción (II)
Visión externa (interfaz): definición del mensaje Visión interna (implementación): operaciones concretas class CUBO { .... Rotar( GRADO g); }; CUBO::Rotar( GRADO g) { .... } (C++)
21
Encapsulamiento: introducción (III)
Algunas definiciones: “Consiste en separar los aspectos externos del objeto, a los cuales puede acceder otro objeto, de los detalles internos de implementación, que quedan ocultos para los demás” (RUMBAUGH, 1991) “El encapsulamiento es el proceso de almacenar en un mismo compartimento los elementos de una abstracción que constituyen su estructura y comportamiento; sirve para separar el interfaz contractual de una abstracción y su implementación” (BOOCH, 1994)
22
Encapsulamiento de clases
Siguiendo la metáfora del equipo de música debemos diferenciar en nuestras clases lo que es público de lo que es privado. Una aplicación general de este principio implica que los atributos serán privados y sólo se puede acceder a ellos desde los métodos públicos de la clase. Ventaja: desde el código de la clase controlamos el acceso a los atributos. En caso de fallo o modificación del código, el programador comprueba los métodos de la clase que modifican ese dato y no tiene que revisar todo el código de los otros archivos. Además evitamos accesos indeseados que puedan modificarlos de forma no coherente con otros datos. Los métodos públicos son el intermediario (interfaz) entre los datos y los otros objetos: Objeto Los métodos públicos de la clase son el interfaz de la clase Método 1 Datos Mensajes Método 2
23
Encapsulamiento: ocultar estructuras y algoritmos internos
Las estructuras de datos que utiliza una clase internamente (por ejemplo un vector o un árbol) no deben ser accesibles para otras clases. Por la misma razón los algoritmos internos deben quedar ocultos. Por ejemplo, la mayor parte de las modernas librerías de software tienen métodos para ordenar listas. Desde fuera el programador solo debe conocer el interfaz del método y lo que hace. No accedemos al interior del algoritmo: /* Llamo al método static sort() de la clase Collections, le paso como primer arg la lista y como segundo arg un objeto comparador */ Collections.sort( lista, new comparador() );
24
Modularidad
25
Modularidad: introducción (I)
Un diseño modular se basa en la conocida estrategia de “divide y vencerás”. Descomponer de forma lógica y robusta el dominio de la aplicación. Para conseguir la modularidad también debemos aplicar las reglas de coherencia de métodos y clases que vimos con el principio de generalidad, es decir, hacer que nuestros componentes sean especialistas. Fondo Inversión Analizador Fondos obtener datos análisis estudiar alternativas Recomendación Fondos de Inversión datos para presentación Interfaz Usuario
26
Modularidad: introducción (II)
La modularidad facilita la reusabilidad y reduce costes, pero usada con prudencia Coste Número de módulos Coste por interfaces Coste por módulo
27
Modularidad: introducción (III)
Hay diversas condiciones que permiten llegar a la modularidad: Descomposición funcional. Ver cada módulo como un especialista (aplicación de la regla de la coherencia). Bajo acoplamiento. Además de ser especialistas tienen la mayor autonomía posible. Comprensibles para un observador externo. Esto es obvio, pero no es tan común como parece. El objetivo es que cada módulo sea comprensible de forma independiente (cómo mucho obligar a un somero repaso de otros módulos), para ello los módulos deben obedecer a la lógica del dominio, ser legibles y estar bien documentados. De 1 y 3 se deduce la condición de la trazabilidad.
28
Modularidad: bajo acoplamiento (I)
Cuando hablamos de un diseño modular hablamos de bajo acoplamiento o poca dependencia entre módulos. La regla del bajo acoplamiento implica: Interfaces pequeños: que los métodos públicos que intercambian información con otros módulos sean: El menor número posible. Pequeños. Pocos argumentos. Si un método tiene muchos parámetros, probablemente hay revisar el diseño o encapsular los parámetros en una clase. En el siguiente ejemplo tenemos dos versiones de un método. El segundo método no sólo tiene menos argumentos, además resulta más comprensible: fabrica.solicitar_pedido( Fec, Hora, CodComer, Cantidad, PVP, ModoEntrega ) fabrica.solicitar_pedido( pedido nuevo_pedido )
29
Modularidad: bajo acoplamiento (II)
Regla de la continuidad modular: debemos diseñar y programas de tal forma que pequeños cambios en un módulo producen a lo sumo pequeños cambios en otro. Evidentemente, esto facilita la extensibilidad y minimiza los costes de mantenimiento. Regla de la protección modular: los errores en un módulo afectan como mucho a módulos adyacentes. Regla de la comunicación explicita: la comunicación entre módulos es clara. Normalmente por medio de interfaces con pocos parámetros y sin usar información global (que hace que la interacción quede oculta). Encapsular la implementación. También en los módulos hay que diferenciar lo público de lo privado. Un cambio en la parte privada no debería afectar al interfaz del módulo.
30
Modularidad: comprensibilidad
Antes dijimos que si si los módulos no obedecen a la lógica del dominio, no son legibles o no están bien documentados, entonces la descomposición es inútil y no permite la reusabilidad. Cuando se dice que la descomposición de los módulos obedece a la lógica del dominio, se quiere decir que la mayoría de cada uno de módulos debe ser una representación natural de entidades o relaciones del dominio (por ejemplo: cuenta, beneficiario, oficina, control de riesgo, etc.). Decimos la mayoría: ya que habrá módulos que no modelizan entes del dominio (pertenecen al dominio de la aplicación exclusivamente: interfaz gráfico, sentencia SQL, etc.).
31
Modularidad: trazabilidad
Se debe representar la continuidad entre los conceptos del proceso de desarrollo. La regla de la trazabilidad indica que cada conjunto lógico de entidades y relaciones del dominio tiene su correlato modular. Esto facilita la extensibilidad y la reusabilidad. Especificación de requisitos Análisis Diseño Programación Los métodos de Diseño orientado a facetas, orientados a aspectos o temas hacen énfasis en mejorar la trazabilidad de requisitos.
32
Modularidad: arquitectura en capas
De todo lo anterior (coherencia, bajo acoplamiento, etc.) se deduce que gran parte de nuestras aplicaciones se descomponen en módulos que reflejan su especialización funcional: Interfaz con el usuario. Implementación. Se aplican cálculos y algoritmos específicos al dominio. Control. También implica conocimiento específico del dominio. Componentes que especifican el orden de las tareas y las condiciones de disparo (Si-entonces). Comprueban estados y errores. No tienen cálculos o algoritmos complejos. Por ejemplo, reglas de negocio del tipo: “Si el solicitante es cliente, entonces producir pedido; si no, solicitar registro de nuevo cliente”. Almacenamiento y conectividad. Módulos responsables de la interacción con la base de datos y otras aplicaciones.
33
Modularidad: arquitectura en tres capas
En la práctica, sobre todo en aplicaciones que no son muy grandes, se suelen reunir en un módulo las clases de implementación y de control. Un ejemplo es la arquitectura en tres capas, tan usual en aplicaciones Web: Presentación Reglas de negocio Acceso a datos (almacenamiento y conectividad)
34
Jerarquía
35
Jerarquía: introducción
Las dos formas más usuales de jerarquía de clases son: Jerarquía de tipos o herencia Jerarquía de partes o agregación: Composición Contenedor La jerarquía por herencia supone un orden de abstracción/concreción, donde una excesiva cantidad de niveles (aprox. más de cuatro) hace difícil el mantenimiento. La jerarquía de agregación. También el exceso de niveles produce confusión. Persona 1 Coche Motor Alumno Profesor
36
Jerarquía: herencia y redefinición
La herencia es una: Forma de abstracción. Primero nos centramos en el vehículo y después en sus subtipos (coche, autobus, etc.) Forma de modularizar. Permite la redefinición de métodos. En la redefinición se mantiene el interfaz del método y cambia la implementación; esto es un ejemplo simple de polimorfismo (un interfaz y múltiples implementaciones). Tipos: Redefinición completa: no uso el servicio base Redefinición parcial: uso el servicio de la clase base CLASE EMPLEADO (redefinición completa) public void mostrar() { System.out.print( nombre + ", " ); System.out.println( salario_bruto); } empleado nombre salario_bruto mostrar() profesor horas_lectivas CLASE PROFESOR (redefinición parcial) public void mostrar() { super.mostrar(); System.out.println( “Horas lectivas: " + horas_lectivas); }
37
Jerarquía: herencia y polimorfismo
Un nombre puede denotar instancias de muchas clases diferentes, siempre que estas clases hereden de una clase común. Emplear el mismo interfaz con implementaciones diferentes empleado[] personal = new empleado[x]; personal[0] = new administrativo(...); personal[1] = new profesor(...); personal[0].mostrar(); personal[1].mostrar();
38
Jerarquía: herencia y ligadura dinámica
Un ejemplo de polimorfismo en el que la llamada (ligadura) al método se resuelve en tiempo de ejecución. Imaginemos un vector de figuras: figura vector[] = new figura[x]; Damos la opción al usuario de crear libremente el tipo de figura que quiera y el programa la almacena en el vector: vector[y] = new círculo(); ... vector[y] = new rectángulo(); ¿A qué método llama? vector[z].dibujar(); Gracias a la ligadura dinámica el programa llama al objeto adecuado. rectángulo void dibujar() circulo figura
39
Jerarquía: herencia y clases abstractas
En nuestro ejemplo de las figuras el programador no ha incluido la implementación del método obt_area() para la clase figura. Es lógico, podemos calcular el área de los rectángulos o de los círculos, pero no podemos calcular el área de un concepto tan abstracto como el de figura. Esto no es raro en una clase madre, en muchas ocasiones las clases más elevadas en una jerarquía de herencia especifican un interfaz (declaración de función), sin definir una implementación. En nuestro ejemplo no se puede calcular el área de una figura. Denominamos clases abstractas a aquellas que tienen o heredan un método abstracto, es decir, métodos declarados pero no implementados. No puede haber instancias de las clases abstractas. En nuestro ejemplo la clase madre puede ser: abstract class figura { protected punto origen; figura( punto origen) { this.origen = origen; } figura(int x, int y) { origen = new punto(x,y); abstract double obt_area();
40
Clases abstractas en diferentes lenguajes
Eiffel deferred class Figura feature dibujar is deferred end ... Java abstract class Figura { abstract void dibujar(); ...} C++ class Figura { public: virtual void dibujar() = 0;
41
Jerarquía: agregación
Encontrar clases agregadoras es: Forma de abstracción. Primero nos centramos en el Coche y después en sus componentes (motor, etc.). Forma de modularizar. Tipos: Composición. La parte (Plan estratégico) desaparece cuando desaparece el todo (Empresa). Otro ejemplo: casa/cocina. Contenedor. El componente (Ratón) no desaparece con el contenedor (Computadora). Otro ejemplo: cesta/manzanas.
42
Ejemplos de agregación
Un ejemplo de composición con la librería Swing para hacer applets (clase JApplet). La parte (botones y etiquetas) desaparecen cuando desaparece el todo (applet): public class mi_japplet extends JApplet { /*** Creo los componentes ***/ JLabel etiqueta_titulo = new JLabel(); JButton boton1 = new JButton(); JButton boton2 = new JButton(); Un ejemplo de contenedor. El panel de contenidos que tiene el JApplet se llama contenedor. Debemos añadir (add) los botones y etiquetas a este panel para que aparezcan por pantalla: private void jbInit() throws Exception { Container contenedor = getContentPane(); contenedor.setLayout( new FlowLayout() ); /*** Añadir componentes al contenedor ***/ contenedor.add(etiqueta_titulo); contenedor.add(boton1); contenedor.add(boton2); }
Presentaciones similares
© 2025 SlidePlayer.es Inc.
All rights reserved.