La Programación Orientada a Objetos: Introducción Prof. Franco Guidi Polanco Escuela de Ingeniería Industrial Pontificia Universidad Católica de Valparaíso, Chile fguidi@ucv.cl De Problemas a Soluciones “Half the battle is knowing what problem to solve” Data Structures and Algorithms (A.A. Aho, J.E. Hopcroft & J.D. Ullman) De http://pbfcomics.com/ Franco Guidi Polanco (PUCV-EII) 02-06-14 2 De Problemas a Soluciones v Análisis: entender el problema y sus requerimientos v Diseño: determinar un modelo que nos permita resolver el problema. v Implementación: determinar la forma de representar el modelo que describe el problema (y la solución): Franco Guidi Polanco (PUCV-EII) 02-06-14 3 Los Requerimientos v Los requerimientos: § son incompletos § usualmente están equivocados § son engañosos (y los usuarios también) § no cuentan la historia completa v Por lo tanto, los requerimientos siempre cambian Franco Guidi Polanco (PUCV-EII) 02-06-14 4 El Desarrollo de Proyectos que propuso el 1 Lo comité de proyecto que se especificó 2 Lo como requerimiento que desarrollaron 4 Lo los programadores 5 Franco Guidi Polanco (PUCV-EII) Lo que se instaló al usuario 02-06-14 3 Lo que diseñó el analista que necesitaba 6 Lo el usuario 5 Metodologías y Modelos La Ingeniería de Software provee: v Metodologías de desarrollo. § Desarrollo en cascada § Proceso Unificado § etc. v Modelos para soportar el desarrollo Se estudiarán en detalle en curso de Modelamiento de Sistemas § Especificación estructurada (DFDs, MER, etc.) § UML § etc. v Lenguajes para la implementación § C, C++ § Java § etc. Franco Guidi Polanco (PUCV-EII) 02-06-14 6 Antes de la Programación Orientada a Objetos Divide et Impera: Descomposición Funcional v “Divide et Impera” v Descomposición en funciones: el analista descompone el problema en una serie de pasos que permiten resolverlo. v Los pasos son refinados sucesivamente, hasta llegar a funciones elementales. v ¿Costo? Franco Guidi Polanco (PUCV-EII) 02-06-14 8 Descomposición Funcional ... ingresarDatos(datos); calcularMedia(datos, media); calcularDE(datos, de); mostrarResultados(media, de); ... procedimiento ingresarDatos(datos:arreglo) { i=0; mientras i <= 10 hacer { leer datos[i] i = i + 1; } } procedimiento calcularMedia(datos:arreglo, media:float) { i=0; suma = 0; mientras i <= 10 hacer { suma = suma + datos[i] i = i + 1; } media = suma / 10 } ... Franco Guidi Polanco (PUCV-EII) 02-06-14 9 La Encapsulación como Agrupación de Funciones v La encapsulación permite la agrupación de ideas relacionadas en una unidad, la que posteriormente puede ser referida por un solo nombre. v El concepto surgió en la década del ’40 con la invención de la subrutina: los programadores se dieron cuenta de que un mismo patrón de instrucciones se podía repetir muchas veces en un programa. Franco Guidi Polanco (PUCV-EII) 02-06-14 10 Descomposición Funcional: Reutilización de Código Programa original . . i=1 mientras i<=10 { si a[i]=10 i=i+1 } . . i=1 mientras i<=10 { si b[i]=10 i=i+1 } . . i=1 mientras i<=10 { si c[i]=10 i=i+1 } . Programa con subrutina . . HacerCeros(a) . . . HacerCeros(b) . . . HacerCeros(c) . . . hacer entonces a[i]=0 hacer entonces b[i]=0 Procedimiento HacerCeros(x:arreglo) i=1 mientras i<=10 hacer { si x[i]=10 entonces x[i]=0 i=i+1 hacer entonces c[i]=0 Franco Guidi Polanco (PUCV-EII) } 02-06-14 11 Ventajas de Funciones o Subrutinas v Ahorro de memoria de computador (código fuente de programas más corto). v Código fuente más “entendible”: una subrutina agrupa un conjunto de instrucciones en un “concepto” que una persona puede considerar y manejar como una sola idea (en el ejemplo, HacerCeros). Franco Guidi Polanco (PUCV-EII) 02-06-14 12 Lenguajes de Programación v Antes de la POO los lenguajes de programación eran procedimentales. NOTA: NO LO INTENTE Aproximación: Imagine programar en Java, usando sólo métodos estáticos, con instancias de clases que no tengan métodos. Franco Guidi Polanco (PUCV-EII) 02-06-14 13 Reutilización antes de la POO: Tipos Abstractos de Datos ADT (Abstract Data Type) Tipo de dato (No provisto por el lenguaje de programación) + Operaciones Por separado, pero asociadas. Franco Guidi Polanco (PUCV-EII) 02-06-14 14 La Programación Orientada a Objetos La Programación Orientada a Objetos (POO) v El paradigma de la Orientación a Objetos es sucesor de la descomposición funcional v Se centra en el concepto de Objeto: Objeto = Datos + Métodos (Definición tradicional) v Los objetos: § § § § son responsables de si mismos. “saben” de qué tipo son. conocen su propio estado (datos). contienen el código que les permite actuar. Franco Guidi Polanco (PUCV-EII) 02-06-14 16 POO versus Descomposición Funcional v El modelo de programación funcional mantenía centralizadas las responsabilidades: § Una cena en la cual un garzón pide a cada comensal lo que se servirá, y luego les trae los platos solicitados. v El modelo de programación OO provee delegación de responsabilidades: § Una cena en la cual a los comensales se les indica la distribución del buffet. Ellos se sirven a su propio gusto. Franco Guidi Polanco (PUCV-EII) 02-06-14 17 ¿Cómo identificar Objetos? v En problemas pequeños una técnica sencilla se basa en la identificación de sustantivos y verbos : § Los sustantivos pueden ser objetos § Los verbos pueden ser métodos Franco Guidi Polanco (PUCV-EII) 02-06-14 18 Objetos v Una buena forma de concebir un objeto es pensar en él como una entidad con responsabilidades. Las responsabilidades determinan el comportamiento del objeto. v Debe existir una forma para comunicar a un objeto qué debe hacer. v Esta comunicación se logra por medio del conjunto de métodos que un objeto ofrece para que otros puedan invocar. v El conjunto de estos métodos se denomina interfaz pública del objeto. Franco Guidi Polanco (PUCV-EII) 02-06-14 19 Visión de Objetos v Martin Fowler identifica tres perspectivas para describir los objetos: § Nivel conceptual: un objeto es un conjunto de responsabilidades. § Nivel especificación: un objeto es un conjunto de métodos (comportamientos), que pueden ser invocados por otros objetos o por si mismos. § Nivel implementación: un objeto es código y datos, e interacciones computaconales entre ellos. Martin Fowler Franco Guidi Polanco (PUCV-EII) 02-06-14 20 Perspectivas para Describir Sistemas OO Nivel Conceptual Análisis Nivel Especificación Codificación (Lenguajes de programación e.g. Java, C++, C#, etc.) Franco Guidi Polanco (PUCV-EII) 02-06-14 Diseño (Lenguajes de modelamiento e.g. UML) Nivel Implementación 21 Perspectivas para Describir Sistemas OO: Ejemplo v Sistema de docencia, que mantiene datos de alumnos y registra sus inscripciones en cursos. Nivel Conceptual Nivel Especificación Nivel Implementación Franco Guidi Polanco (PUCV-EII) 02-06-14 22 Ejemplo: Nivel Conceptual Responsabilidades: v Alumno: § Mantener datos de un alumno (rol y nombre). Debe validar el rol del alumno. v Curso: § Mantener datos de un curso (nombre) § Mantener la lista de los alumnos que se inscriben en el curso, verificando que estos no se repitan al momento de agregarlos. § Retornar alumnos, buscándolos por rol del alumno. Franco Guidi Polanco (PUCV-EII) 02-06-14 23 Ejemplo: Nivel Especificación v Clase Alumno: § § § § public public public public setRol(numero: int, verificador: int) setNombre( nombre: String) getRol(): String getNombre(): String v Clase Curso: § public setNombre(nombre: String) § public addAlumno(alumno:Alumno):boolean § public getAlumno(rol: String):Alumno Franco Guidi Polanco (PUCV-EII) 02-06-14 24 Ejemplo: Nivel Implementación public class Alumno{ private String rol, nombre; public void setRol(int numero, int verificador){ if( sumaDigitos(numero) == verificador ) this.rol = rol +”-”+verificador; } public void setNombre(Sting nombre){ this.nombre = nombre; public class Curso{ } private String nombre; public String getRol(){ private Vector alumnos; return rol; } public void setNombre(String nombre){ public String getNombre(){ this.nombre = nombre; return nombre; } } public boolean addAlumno(Alumno alumno){ private int sumaDigitos(int numero){if( getAlumno( alumno.getRol() )== null ){ ... alumnos.add( alumno ); } return true; } } return false; } public Alumno getAlumno(String rol){ ... } } Franco Guidi Polanco (PUCV-EII) 02-06-14 25 ¿Es necesario tomar precauciones, si el análisis inicial está bien hecho? ! “All systems change during their life cycles. This must be borne in mind when developing systems expected to last longer than the first version.” Ivar Jacobson. “Object Oriented Software Engineering a Use Case Driven Approach”, Addison Wesley, 1992, Franco Guidi Polanco (PUCV-EII) 02-06-14 26 POO y Encapsulación v Tradicionalmente se asocia a “ocultamiento de datos” (estado) de un objeto. v Sin embargo, se refiere además a ocultamiento de: § § § § implementación de métodos tipo, clases derivadas detalles de diseño reglas de instanciación Franco Guidi Polanco (PUCV-EII) 02-06-14 27 Encapsulación como Ocultamiento de Datos v Ejemplo: public class Punto{ private int coordA, coordB; public void setXY(int x, int y){ coordA = x; coordB = y; } public int getX(){ return coordA; } public int getY(){ return coordB; } } Franco Guidi Polanco (PUCV-EII) 02-06-14 El “contexto” de la clase Punto no tiene visibilidad de cómo ésta almacena sus datos. Consecuencia: Puede modificarse el conjunto de variables de la clase Punto, sin que esto afecte su contexto. 28 Encapsulación en la Implementación de Métodos v Ejemplo: public class Angle{ private double angle; public void setAngle(double a){ angle = a; } public double getSin(){ // Cálculo mediante series de // Taylor ... } } Franco Guidi Polanco (PUCV-EII) 02-06-14 El “contexto” de la clase Angle no tiene visibilidad del algoritmo de cálculo del seno del ángulo Consecuencia: Puede modificarse la implementación del método sin afectar al contexto. 29 Encapsulación del Tipo v Ejemplo El “contexto” de las figuras no tiene visibilidad de cuál de ellas está exactamente utilizando. Figura Contexto Rombo +dibujar() +rellenar() +ocultar() Franco Guidi Polanco (PUCV-EII) +dibujar() +rellenar() +ocultar() Cuadrado Círculo +dibujar() +rellenar() +ocultar() +dibujar() +rellenar() +ocultar() 02-06-14 Consecuencia: El contexto puede implementar una lógica común para utilizar cualquiera de las figuras (o cualquier nueva subclase). 30 Encapsulación de Detalles de Diseño v Ejemplo Banco Contexto +recibirCliente() +atenderSiguiente() Cola Cajero +agregar() +sacar() +atender() Franco Guidi Polanco (PUCV-EII) 02-06-14 El “contexto” de la clase Banco no tiene visibilidad de las clases que soportan sus operaciones. Consecuencia: Puede modificarse la “arquitectura” del Banco sin afectar el contexto. 31 Encapsulación de Reglas de Instanciación v Ejemplo public class LeonardoDaVinci{ private static LeonardoDaVinci instance; private LeonardoDaVinci(){} public static LeonardoDaVinci getLeonardo(){ if( instance == null ) instance = new LeonardoDaVinci(); } return instance; } public String writeName(){ return “odranoeL”; } ... El “contexto” de la clase no tiene visibilidad de la lógica de instanciación de objetos LeonardoDaVinci. ¿Consecuencias? } Franco Guidi Polanco (PUCV-EII) 02-06-14 32 Encapsulación y Diseño v Ante especificaciones variables y sistemas que evolucionan... ... ¿podemos lograr un buen diseño? v Sí, encontrando qué cosas pueden variar en el diseño y Franco Guidi Polanco (PUCV-EII) encapsulándolas 02-06-14 ? 33 Encapsulación y Diseño v Muchos patrones de diseño (soluciones a problemas comunes de diseño) utilizan la encapsulación de tipos para crear capas de separación entre objetos. v La separación se crea asignando referencias a clases abstractas o interfaces. v Esto permite modificar alguno de los “lados” de la capa de separación, sin afectar a la otra. Franco Guidi Polanco (PUCV-EII) 02-06-14 34 El principio “abierto-cerrado” v En el libro Object Oriented Software Construction de 1988, Bertrand Meyer propuso el “Open-Closed Principle” (OCP) v “Las entidades de software (clases, módulos, funciones, etc.) deberían estar abiertos para extensión y cerrados para modificaciones.” Bertrand Meyer v En otras palabras, el software debe ser diseñado para soportar la adición de nuevas funcionalidades, sin que esto comporte modificaciones en aquellas existentes. v No es siempre posible seguir completamente este principio. Franco Guidi Polanco (PUCV-EII) 02-06-14 35 El principio “abierto-cerrado” v Síntomas de un mal diseño (cuando no se cumple este principio): § Al modificar un módulo de software, los cambios se propagan a otros módulos. v Por lo tanto: § Se deben diseñar módulos que nunca cambiarán. § Si los requerimientos cambian, se debe extender el comportamiento de tales módulos, agregando nuevo código, no modificando aquél existente. v La base de este principio está en los conceptos de: § Abstracción § Polimorfismo Franco Guidi Polanco (PUCV-EII) 02-06-14 36 El principio “abierto-cerrado” v Los módulos desarrollados bajo este principio tienen dos características: § Están “abiertos para extensión”: el comportamiento del módulo puede ser extendido, a fin de lograr un nuevo comportamiento, impusto por nuevos requerimientos de la misma o de otra aplicación. § Están “cerrados para modificaciones”: el código fuente de un módulo existente no debe ser alterado. Franco Guidi Polanco (PUCV-EII) 02-06-14 37 El principio “abierto-cerrado” v Aquí no se respeta el principio: Personaje public void pinta(Personaje p){ if( seRequierePintar ){ if( p instance of Héroe) pintaHeroe((Héroe) p); else if(p instance of Enemigo) pintaEnemigo((Enemigo) p); } … } Héroe Enemigo Dibujador pinta(Personaje p) pintaHéroe(Héroe p) pintaEnemigo(Enemigo p) v ¿Qué pasa si es necesario agregar un nuevo tipo de personaje (ej. un Mago)? Franco Guidi Polanco (PUCV-EII) 02-06-14 38 El principio “abierto-cerrado” v Aquí si es respetado el principio: Personaje Dibujador pinta() pinta(Personaje p) Héroe public void pinta(Personaje p){ if( seRequierePintar ) p.pinta(); … } pinta() Enemigo pinta() v Es posible agregar nuevos objetos a pintar (agregando una subclase de Personaje), sin modificar el código ya existente Franco Guidi Polanco (PUCV-EII) 02-06-14 39 Consecuencias del principio “abierto–cerrado” v “Regla” de diseño: Establecer visibilidad “privada” a variables de instancia. v Los conceptos de abstracción y polimorfismo del principio abierto-cerrado están asociados a la especificación de jerarquías de herencia. v ¿Hay algo que decir respecto de las jerarquías de herencia? (Veamos el principio de sustitución de Liskov) Franco Guidi Polanco (PUCV-EII) 02-06-14 40 El principio de sustitución de Liskov v Barbara Liskov, en “Data Abstraction and Hierarchy”, SIGPLAN Notices, 23, 5 (May 1988) estableció lo que hoy se conoce como el “Liskov Substitution Principle” (LSP): v “Las funciones que utilizan punteros o referencias a clases de base, deben ser capaces de utilizar subclases de éstas, sin necesidad de conocerlas” v Esto es: § Cualquier propiedad que sea cierta para una súperclase, debe serlo también para sus subclases. § Un cliente de una clase debe funcionar correctamente con cualquier subclase de ésta última. Franco Guidi Polanco (PUCV-EII) 02-06-14 Barbara Liskov 41 El principio de sustitución de Liskov v Ejemplo: Programador Vehículo Terrestre Aéreo Bus Tren v La clase Programador debe funcionar correctamente con la clase Vehículo, o con cualquier subclase de ella Franco Guidi Polanco (PUCV-EII) 02-06-14 42 El principio de sustitución de Liskov v El LSP se violaría ante la presencia de situaciones como la siguiente: Programador Vehículo programa(vehiculo v) Terrestre Aéreo public void programa(Vehículo v){ if( v instance of Bus ){ throw new InvalidException(); else programaAutomático(v); } } Franco Guidi Polanco (PUCV-EII) 02-06-14 Bus Tren 43 El principio de “inversión de dependencia” v El “Dependency Inversion Principle” (DIP) establece cómo implementar los objetivos enunciados por el OCP y el LSP. v “Los módulos de alto nivel no deberían depender de los módulos de bajo nivel. Ambos deberían depender de abstracciones. v Las abstracciones no deberían depender de detalles. Los detalles deberían depender de abstracciones.” Kandinsky Franco Guidi Polanco (PUCV-EII) Klee 02-06-14 44 El principio de “inversión de dependencia” v Propone la estategia de depender de abstracciones (interfaces, funciones abstractas y/o clases abstractas), en vez de depender de funciones y clases concretas. Franco Guidi Polanco (PUCV-EII) 02-06-14 45 El principio de “inversión de dependencia” v Aquí no se cumple el DIP: public void copiar(LectorTeclado l, EscritorDisco e){ while( !l.eof){ byte b = l.leer(); e.escribir( b ); } } Copiador LectorTeclado Franco Guidi Polanco (PUCV-EII) EscritorDisco 02-06-14 v La lógica general del copiador tiene “cableada” la acción sobre un LectorTeclado y un EscritorDisco v ¿Posibilidad de reutilizar el código del Copiador? 46 El principio de “inversión de dependencia” v Aquí sí se cumple: public void copiar(Lector l, Escritor e){ while( !l.eof){ byte b = l.leer(); e.escribir( b ); } } Copiador Lector LectorTeclado Franco Guidi Polanco (PUCV-EII) Escritor EscritorDisco 02-06-14 47 Después de la OO... ¿qué? v Los objetos ofrecen una interfaz pública por medio de la cual otros objetos invocan sus comportamientos. v Los objetos son inherentemente reactivos: su comportamiento es gatillado por la acción (externa) de otro objeto. v Nuevo paradigma: agentes de software. v Los agentes de software son unidades de software con objetivos y responsabilidades. v Los agentes de software exhiben comportamiento proactivo: un agente actúa por cuenta propia sobre el ambiente y sobre otros agentes para alcanzar sus objetivos. Franco Guidi Polanco (PUCV-EII) 02-06-14 48