Programación Orientada a Objetos

Anuncio
Tesis Héctor César Lazzarini
UNIVERSIDAD NACIONAL
DE MISIONES
FACULTAD DE
CIENCIAS ECONOMICAS
MAESTRIA EN
INFORMÁTICA Y COMPUTACIÓN
CONVENIO CON LA UNIVERSIDAD DE
CANTABRIA
TESIS
PROGRAMACION ORIENTADA A
OBJETOS
D
Diirre
ecctto
orra
a:: M
Ma
ag
giisstte
err G
Glla
ad
dyyss N
No
oe
em
míí D
Da
ap
po
ozzo
o
M
a
e
s
t
r
a
n
d
o
:
H
é
c
t
o
r
C
é
s
a
r
L
a
z
z
a
r
i
n
i
Maestrando: Héctor César Lazzarini
Dirección Postal: Moreno 155 – P3600KAC Formosa – Formosa – Argentina
Dirección electrónica: lazzariniylazzarini@arnet.com.ar
1
Tesis Héctor César Lazzarini
TABLA DE CONTENIDOS
TABLA DE CONTENIDOS........................................................................................................ 1
Agradecimientos:........................................................................................................................... 7
Prólogo .......................................................................................................................................... 9
Capitulo 1 .................................................................................................................................... 11
INTRODUCCIÓN A LA PROGRAMACIÓN ORIENTADA A OBJETOS (POO) ..................... 11
Un poco de historia de la POO........................................................................................... 11
Ideas básicas de la POO .................................................................................................... 14
Ventajas e inconvenientes de la orientación a objetos ...................................................... 15
El modelo OO ..................................................................................................................... 15
Definiciones básicas ........................................................................................................... 16
Diferencia entre el modelado por descomposición funcional y el OO................................ 17
Descomposición OO........................................................................................................... 18
Capitulo 2. ................................................................................................................................... 19
PROGRAMACIÓN ORIENTADA A OBJETOS – FIJANDO CONCEPTOS ........................... 19
Introducción. ....................................................................................................................... 19
Información ......................................................................................................................... 19
Sistemas de información. ................................................................................................... 20
Profundizando el concepto de Objeto. ............................................................................... 20
Demonios............................................................................................................................ 23
Abstracción. ........................................................................................................................ 24
Modularidad. ....................................................................................................................... 24
Encapsulación de objetos................................................................................................... 24
Reutilización. ...................................................................................................................... 25
Clase................................................................................................................................... 25
Herencia.............................................................................................................................. 26
Polimorfismo. ...................................................................................................................... 27
Jerarquías. .......................................................................................................................... 27
Mensajes............................................................................................................................. 28
Capitulo 3. ................................................................................................................................... 29
JAVA Y LA PROGRAMACIÓN ORIENTADA A OBJETOS.................................................... 29
Introducción. ¿Porqué Java?.............................................................................................. 29
Origen de Java. .................................................................................................................. 29
Características de Java ...................................................................................................... 31
Capitulo 4. ................................................................................................................................... 37
PROFUNDIZANDO CONCEPTOS DE POO – ejemplos con JAVA ...................................... 37
1. Herencia.......................................................................................................................... 38
2. Sobrescritura de métodos............................................................................................... 38
3. La clase base y la clase derivada................................................................................... 38
En Java disponemos de los paquetes básicos: ............................................................. 39
La clase base object. ...................................................................................................... 45
Métodos que pueden ser redefinidos por el programador. ............................................ 45
Concretando: .................................................................................................................. 48
4. Redefinición de métodos heredados .............................................................................. 48
Modificador final:............................................................................................................. 49
5. Clases y métodos abstractos ......................................................................................... 49
6. Polimorfismo ................................................................................................................... 49
Métodos (funciones miembro). ....................................................................................... 52
Métodos sobrecargados (overloaded)............................................................................ 54
7. Vinculación, Ligadura o Binding ..................................................................................... 49
8. Redefinición .................................................................................................................... 55
Sobrecarga de Constructores......................................................................................... 55
Objetos como parámetros .............................................................................................. 56
Paso de argumentos. ..................................................................................................... 56
El operador static............................................................................................................ 57
Las variables static ......................................................................................................... 57
Los métodos static.......................................................................................................... 58
3
Tesis Héctor César Lazzarini
El operador final.............................................................................................................. 59
9. Interfases. ....................................................................................................................... 59
¿Qué diferencia hay entre una interfase y una clase abstract?..................................... 59
Definición de interfases. ................................................................................................. 60
Utilización de interfases.................................................................................................. 60
Conceptos Útiles............................................................................................................. 61
La llamada a super. ........................................................................................................ 61
10. Permisos de acceso en java......................................................................................... 61
Accesibilidad de los Packages (paquetes)..................................................................... 61
Accesibilidad de las Variables y Métodos Miembros de una Clase. .............................. 62
Resumen de los permisos de acceso de Java............................................................... 62
11. Transformaciones de tipo: casting................................................................................ 63
Conversión de tipos primitivos........................................................................................ 63
12. Sincronización de threads (hilos). ................................................................................ 63
Capítulo 5. ................................................................................................................................... 65
Implementación de los conceptos de oo con JAVA................................................................ 65
Implementado el concepto Jerarquía ................................................................................. 65
La clase Figura ............................................................................................................... 66
La clase Rectángulo ....................................................................................................... 66
La clase Círculo .............................................................................................................. 67
Uso de la jerarquía de clases. ............................................................................................ 67
Enlace dinámico o binding.................................................................................................. 68
Ejemplo de polimorfismo. ............................................................................................... 68
Ampliación de la jerarquía de clases.................................................................................. 70
La clase Triangulo. ......................................................................................................... 71
El operador instanceof........................................................................................................ 72
En resumen:........................................................................................................................ 72
Clases y Métodos Finales................................................................................................... 73
Interfases. ........................................................................................................................... 74
Diferencias entre una Interfase y una Clase Abstracta. ..................................................... 75
Las interfases y el Polimorfismo......................................................................................... 76
Herencia simple. ................................................................................................................. 76
Composición. ...................................................................................................................... 79
La clase punto ................................................................................................................ 80
La clase Rectangulo ....................................................................................................... 81
Definiendo Clases............................................................................................................... 83
Uso de Atributos. ................................................................................................................ 85
Uso de la Definición de Clase............................................................................................. 86
Diseño e Implementación de Clases. ................................................................................. 86
Diseño de la clase: ............................................................................................................. 87
Implementación de la clase: ............................................................................................... 88
Primer Ejemplo Completo: Clase para Entrada/Salida Interactiva (InteractiveIO)......... 88
Segundo Ejemplo Completo: Clase Nombre.................................................................. 92
Programa Hello world ! Revisado ................................................................................. 100
Capítulo 6. ................................................................................................................................. 101
Calidad del Software......................................................................................................... 102
Objetivo de la Calidad en los Sistemas ............................................................................ 103
Control de la Calidad ........................................................................................................ 103
Control de Calidad del Software ....................................................................................... 104
Administración de la Calidad ............................................................................................ 108
Calidad por Etapas ........................................................................................................... 110
Estándares y Modelos de Calidad en la Ingeniería de Software...................................... 111
La Norma ISO 9000.......................................................................................................... 112
El Modelo Tick IT .............................................................................................................. 114
El Modelo CMM ................................................................................................................ 115
El Modelo BOOTSTRAP................................................................................................... 118
El Modelo ISO/SPICE....................................................................................................... 120
Conclusiones......................................................................................................................... 123
4
Tesis Héctor César Lazzarini
La elección del Lenguaje de Programación ..................................................................... 123
Perspectivas a futuro ............................................................................................... 125
Bibliografía ............................................................................................................................ 131
PROGRAMAS EJEMPLO DE LOS TEMAS TRATADOS. ................................................... 133
Redefinición de métodos .................................................................................................. 133
/*Programa 1 - Versión 1*/ ........................................................................................ 133
/*Programa 1 - Versión 2*/ ........................................................................................ 134
/*Programa 1 - Versión 3*/ ........................................................................................ 135
/* Programa 2 */ .......................................................................................................... 137
Sobrecarga de métodos ................................................................................................... 138
/*Programa 1 - Versión 1*/ ........................................................................................ 138
/*Programa 1 - Versión 2*/ ........................................................................................ 139
/** Programa 2 */........................................................................................................... 141
/*Programa 3 */ ............................................................................................................. 143
Explicación del Algoritmo.................................................................................................. 144
/*Programa 4 */ ............................................................................................................. 145
Clases abstractas ............................................................................................................. 147
/** Programa 1 */........................................................................................................... 147
/**Programa 2 – versión 1*/.......................................................................................... 148
/**Programa 2 – versión 2*/.......................................................................................... 149
Polimorfismo ..................................................................................................................... 151
/*Programa 1 – versión1 */ ........................................................................................... 151
/*Programa 2 – versión 1 */ .......................................................................................... 153
/**Programa 2 – versión 2 */......................................................................................... 156
/**Programa 3 */ /**subprograma 1*/............................................................................ 160
/** subprograma 2*/ ...................................................................................................... 160
/** subprograma 3*/ ...................................................................................................... 160
Ligaduras dinámicas......................................................................................................... 163
/** Programa 1 */ /** Subprograma 1*/ ......................................................................... 163
/**Subprograma 2*/ ...................................................................................................... 164
5
Tesis Héctor César Lazzarini
6
Tesis Héctor César Lazzarini
Agradecimientos:
A toda la gente me alentó para que termine este trabajo.
A todos los profesores de España, realmente es imposible dejar de recordarlos,
por su calidez humana y profesional.
A todos: un “GRACIAS” sincero.
Formosa, 14 de mayo de 2005
7
Introducción
Prólogo
Los objetivos de este trabajo son establecer las pautas básicas para introducir a
los alumnos en el mundo de la Programación Orientada a Objetos (POO), en el marco
de la materia PROGRAMACIÓN IV de la carrera Licenciatura en Sistemas de
Información.
En este libro se enseñaran los conceptos de la Programación Orientada a Objetos
con abundantes ejemplos basados en el lenguaje JAVA. Muchos de estos ejemplos
van incorporando progresivamente cada nuevo concepto que se enseña, para, de esta
manera, facilitar la incorporación de un nuevo concepto con un ejemplo ya visto.
He tratado, en este trabajo, de compatibilizar ambas cosas. Puntualmente, y tal lo
afirmado en el Proyecto original de esta Tesis, necesito formar a los alumnos con un
concepto “entendible” de la POO, porque no debemos perder de vista que es una
materia con la cual estos se reciben de Programadores Universitarios, por lo que
deben aplicar inmediatamente estos conceptos.
Debo indicar que he escogido el lenguaje JAVA como soporte para explicar los
conceptos a enseñar por su popularidad comercial y su aplicación actual en Internet.
Este trabajo de Tesis se ha estructurado de la siguiente manera:
Introducción histórica.
Explicación conceptual de los temas planteados. No se incluyen ejemplos
de ningún tipo.
Explicación de los conceptos con ejemplos básicos. Se revisan los
conceptos brevemente y se aplica a un ejemplo preciso.
Presentación de programas en Java que aplican puntualmente los
conceptos impartidos. Se trata de programas que funcionan
autónomamente.
Presentación de diversos programas más genéricos que combinan varios
conceptos combinados.
Conclusiones y perspectivas a futuro.
Bibliografía utilizada.
Glosario
Listado de códigos de programas desarrollados donde se analiza cada
concepto en particular.
9
Capítulo 1 – Introducción a la Programación Orientada a Objetos.
Capitulo 1
INTRODUCCIÓN A LA PROGRAMACIÓN ORIENTADA A OBJETOS (POO)
Un poco de historia de la POO.
La programación de computadoras es una actividad humana que se ha
desarrollado casi enteramente durante la segunda mitad del siglo XX. Por lo tanto
podemos suponer que aún está en sus orígenes y que el futuro traerá todavía grandes
adelantos técnicos y teóricos que mejorarán sus resultados.
En su corta historia, la programación ha sufrido importantes cambios, diríamos
“casi revoluciones”. Los primeros avances metodológicamente ordenados, fueron
protagonizados principalmente por Wirth, Dijstra y de forma menos teórica pero quizás
con más impacto por Kernighan y Ritchie. Es lo que se denominó la programación
estructurada.
Los primeros lenguajes de programación eran simplemente instrucciones que se le
podían dar a un autómata como una computadora, para que realizara ciertas
operaciones. Así un programa no era sino una lista de instrucciones encaminadas a
realizar algún cálculo.
A medida que las computadoras fueron haciéndose más sofisticadas, sus
lenguajes propios o lenguajes de máquina iban cambiando y surgió la necesidad de
crear unos lenguajes intermedios que cualquier usuario pudiera aprender y que no
dependieran de la máquina concreta en la que se iban a ejecutar los programas. Así
surgieron varios lenguajes que se hicieron famosos y también surgieron los primeros
compiladores. Un compilador es un programa que traduce las instrucciones de un
lenguaje más o menos humano, a las de una máquina.
La aparición de estos lenguajes intermedios y sus compiladores marca el comienzo
de la programación como una nueva ciencia. El tremendo éxito que las computadoras
tuvieron a lo largo de los años 60 fue llevando a la creación de programas cada vez
más complejos que llegaban a tener miles de líneas de código.
Hacer correcciones a este tipo de programas y agregarles mejoras se fue
convirtiendo en una labor muy ardua. Ante este problema que amenazaba con
convertir las computadoras en máquinas estériles, surgió un grupo de científicos de la
computación, de los cuales Dijstra y Wirth son dos de los más destacados. Estos
propusieron una serie de ideas que llevaron a la creación de ese nuevo concepto
indicado al inicio: la programación estructurada.
Otros científicos experimentaron ideas similares creando diversos lenguajes de
programación orientada a objetos como Smalltalk. En ellos se experimentó con otras
ideas útiles como la definición de subclases que heredan las propiedades de su
superclase o sea de la clase de la que se derivan, pero agregando variables y
funciones nuevas. También surgió una idea que ayudaría a evitar los difíciles
problemas que surgían en el manejo de la memoria dinámica: los constructores y
destructores de objetos.
11
Capítulo 1 – Introducción a la Programación Orientada a Objetos.
A principios de los 90 se popularizó un nuevo lenguaje orientado a objetos. Se
trata del C++ creado por Bjarne Stroustrup. La idea de Bjarne Stroustrup fue crear un
lenguaje orientado a objetos que heredara prácticamente toda la sintaxis y
posibilidades del lenguaje que en ese momento era más popular entre los
programadores, el lenguaje C. Este truco ayudó a popularizar la programación
orientada a objetos y preparó el camino para la aparición del Java.
Los creadores de Java aprendieron bien la lección de Bjarne Stroustrup. Un nuevo
lenguaje para tener éxito debía ser muy parecido al que en el momento de su
lanzamiento fuese el más popular. Así, Java se creó con un gran parecido al C++.
Pero Java es un lenguaje más puro de programación orientada a objetos, conserva
un poco del C original, pero se parece más al C++. Puede verse al C++ como un paso
intermedio en la transición de la programación estructurada del C a la programación
orientada a objetos más pura del Java. Microsoft está tratando de impulsar un nuevo
lenguaje llamado C# (C sharp) que promete ser aún más puro como lenguaje
orientado a objetos, adopta prácticamente todas las mejoras de Java y agrega algunas
nuevas. Habrá que estar pendientes una vez lanzado a los programadores de sus
posibles ventajas y probables desventajas (en computación nunca se gana algo sin
perder otra cosa, lo importante es que en promedio las cosas vayan mejorando).
Hacia los 80’s el paradigma orientado a objetos comenzaba a madurar como un
enfoque concreto de desarrollo de software. En los últimos años esta metodología ha
experimentado un gran progreso, tanto en el desarrollo de programas como en la
forma de presentar las aplicaciones del sistema al usuario.
Actualmente la Tecnología Orientada a Objetos (TOO) no solo se aplica a los
lenguajes de programación, sino que también se ha propagado a los métodos de
análisis y diseño y a otras áreas, tales como las bases de datos y/o las
comunicaciones. Por lo tanto, para hacer desarrollo de sistemas de software basados
en la TOO, hay que entender bien todos los conceptos del modelo de objetos que está
detrás de ella y sus antecedentes históricos.
Uno de los defectos de la programación imperativa es que las variables globales
pueden ser utilizadas y modificar sus contenidos, desde cualquier punto del programa.
Los programas que carecen de disciplina para acceder a variables globales tienden a
ser inmanejables. La razón es que los módulos que acceden a estas variables no se
pueden comprender completamente, de forma independiente, de todos aquellos otros
módulos que también acceden a las mismas variables globales, es decir, todo está
relacionado con todo.
Este problema fue detectado alrededor de 1970 por David L. Parnas, quien
propuso la norma de ocultar información como solución. Su idea era encapsular cada
una de las variables globales del programa en un módulo junto con sus operaciones
asociadas, sólo mediante las cuales se podía tener acceso a estas variables. El resto
de los módulos podrían acceder a las variables sólo de forma indirecta mediante las
operaciones diseñadas a tal efecto. En la actualidad denominamos objetos a este tipo
de módulos.
El primer lenguaje que introdujo los conceptos de orientación a objetos fue
SIMULA 67 creado en Noruega, por un grupo de investigadores dirigido por O. J. Dahl
y K. Nygaard, con el fin de realizar simulaciones discretas de sistemas reales.
12
Capítulo 1 – Introducción a la Programación Orientada a Objetos.
En estos tiempos no existían lenguajes de programación que se ajustaran a sus
necesidades, así que se basaron en el lenguaje ALGOL 60 y lo extendieron con
conceptos de objetos, clases, herencia, el polimorfismo por inclusión (que se obtiene
introduciendo la herencia de clases) y procedimientos virtuales.
El lenguaje fue utilizado sobre todo en Europa y no tuvo mucho impacto comercial,
sin embargo los conceptos que se definieron en él, se volvieron sumamente
importantes para el futuro del desarrollo de software.
La programación estructurada propone dos ideas básicas: no repetir código y
proteger las variables que una parte del programa usa, de que sean modificadas
accidentalmente por otras partes del programa. Una sola repetición de código es
fuente probable de errores y dificulta el mantenimiento de un programa. Para no repetir
código hay que escribir funciones o procedimientos que se encarguen de realizar
siempre lo que las diferentes repeticiones realizarían. Para proteger las variables hay
que desarrollar lenguajes que permitan definir variables locales, es decir, que sólo
pueden ser utilizadas dentro de una función o procedimiento. De esta manera ninguna
otra función del programa puede cambiarla.
Como consecuencia de estas ideas disminuye considerablemente el tamaño de los
programas y éstos se hacen más confiables y más fáciles de corregir o mejorar. Para
facilitar la programación estructurada, aparecen nuevos lenguajes, notoriamente el
Pascal y el C, son los dos lenguajes de programación estructurada más conocidos.
Wirth, creador del Pascal, nunca quedó satisfecho con este lenguaje. En su
constante análisis de los hábitos de programación encontraba que todavía había
muchas cosas que mejorar. Una década después del Pascal publicó otro lenguaje
llamado Modula2 en el que presentaban algunas de sus ideas más recientes. Los
módulos de Modula2 pretendían representar objetos o clases. Dentro de un módulo se
definían variables que representaban el estado del objeto y se definían procedimientos
que describían su comportamiento. Las variables y los procedimientos de un módulo
podían ser de uso público o privado, exclusivos de la clase, según lo decidiera el
creador del módulo. Con todo esto se mejoraba notoriamente lo instituido por el Pascal
donde el concepto de procedimientos o funciones privadas no existía y se daba un
paso importante hacia la programación de clases o programación orientada a objetos.
Alrededor de los años 70’s fue desarrollado el lenguaje de programación OO
llamado SMALLTALK en los laboratorios Xerox en Palo Alto, EE.UU. Éste lenguaje
adoptó los conceptos nombrados anteriormente como su fundamento. El hecho de ser
creado en EE.UU., ayudó a que se introdujera a escala mundial el término de
Orientación a Objetos (Object Oriented OO) y que cobrara importancia entre los
diseñadores de lenguajes de programación.
Los puntos importantes de este lenguaje fueron, por un lado, adoptar el concepto
de objeto y clase como núcleo del lenguaje y la programación interactiva, incorporando
las ideas ya conocidas de lenguajes funcionales. Es decir que se tuviese un lenguaje
interpretado y no compilado.
En 1985, Bjarne Stroustrup extendió el lenguaje de programación C a C++, es
decir C con conceptos de clases y objetos, cerca de esta fecha, 1986, se creó desde
sus bases el lenguaje EIFFEL por B. Meyer. Ambos manejan conceptos de objetos y
herencia de clases. La herencia es múltiple, y se introduce pensando en dar mayor
13
Capítulo 1 – Introducción a la Programación Orientada a Objetos.
flexibilidad a los desarrolladores. Sin embargo, actualmente la herencia múltiple se ha
evitado por agregar complejidad en las estructuras de clases.
Ambos lenguajes tuvieron importancia entre 1985 y hasta la primera mitad de los
90’s.
En 1995 apareció JAVA, el más reciente lenguaje OO, desarrollado por la empresa
SUN Microsystems, que hereda conceptos de C++, pero los simplifica y evita la
herencia múltiple. En su lugar se introduce el término de interfaz, y la herencia múltiple
de interfases. Obtiene una rápida aceptación gracias a los applets, que son programas
en JAVA insertados en páginas WEB dentro del código HTML.
Estos programas pueden viajar a través de la Internet y brindarle al usuario mayor
interactividad con las páginas WEB. JAVA introduce también, la programación
concurrente y distribuida. El lenguaje es mitad compilado y mitad interpretado dando
como resultado la portabilidad a distintas plataformas. JAVA aun sigue evolucionando
y se espera que en los próximos años logre la madurez adecuada para convertirse en
un lenguaje de desarrollo de mayor importancia.
Ideas básicas de la POO
La programación orientada a objetos es una metodología que descansa en el
concepto de objeto para imponer la estructura modular de los programas. Permite
comprender el dominio del problema a resolver, al intentar construir un modelo del
mundo real que envuelve nuestro sistema. Es decir, la representación de este mundo
mediante la identificación de los objetos que constituyen el vocabulario del dominio del
problema, su organización y la representación de sus responsabilidades.
El paradigma orientado a objetos se basa en los tres métodos de organización que
utilizamos desde nuestra infancia y en los que basamos todo nuestro pensamiento:
a) la diferencia entre un objeto y sus atributos (por ejemplo, entre una
manzana y su sabor o peso),
b) la diferencia entre un objeto y sus componentes (por ejemplo, entre una
manzana y su piel o semillas) y
c) la formación y distinción entre clases de objetos (por ejemplo, entre
manzanas rojas, manzanas verdes, etc.)
La idea de manejar objetos reales como contenedores de estados y
comportamientos, es mucho más atractiva desde el punto de vista del usuario. De esta
manera no tendrá que batallar con construcciones orientadas al computador, sino que
podrá manejar objetos (y operaciones) que se asemejen más a sus equivalentes en el
mundo real. La elevación del nivel de abstracción es sin duda un objetivo deseable.
Las técnicas orientadas a objetos usan un mismo modelo conceptual para el
análisis, el diseño y la programación. La transición desde el análisis al diseño es tan
natural que es difícil especificar donde comienza uno y donde acaba el otro.
14
Capítulo 1 – Introducción a la Programación Orientada a Objetos.
Ventajas e inconvenientes de la orientación a objetos
Entre las ventajas más importantes podemos destacar:
Favorece la comunicación entre analistas, diseñadores, programadores y
usuarios finales al utilizar todos los mismos modelos conceptuales.
Esto se traduce en un aumento de la productividad, ya que la comunicación es
uno de los puntos críticos en las primeras fases del proyecto.
Se facilita la representación de estructuras complejas sin necesidad de
adaptarnos a normas y modelos, ya que lo que manejamos son objetos del
mundo real, lo que facilita la tarea del analista.
La semántica de estas técnicas es más rica (al ser más natural); al usuario final
le es más fácil comprender lo que el analista representa en sus modelos (ya
que representa los objetos que lo rodean habitualmente)
Favorece la modularidad, la reusabilidad y el mantenimiento del software.
Estas técnicas son más resistentes al cambio que las tradicionales técnicas de
análisis orientadas a flujos de datos.
Algunas de sus desventajas:
Hay que ser muy cuidadosos en la creación de los objetos, ya que de ello
dependerá el éxito de nuestro proyecto. Un error en estas primeras definiciones
podría resultar catastrófico. Precisamente el secreto de esta técnica está en la
correcta definición inicial de los objetos.
Los estándares en este tipo de técnicas están en continua evolución, lo que
exige una actualización permanente.
Los analistas, diseñadores y desarrolladores del proyecto deben conocer las
reglas del juego y poseer suficiente experiencia en programación.
El modelo OO
Podemos indicar que se apoya en cuatro conceptos básicos:
objeto
clase
herencia
envío de mensajes
Los primeros tres conceptos se refieren a la parte estructural o estática del modelo
y el cuarto, que corresponde a mensajes, se refiere a la parte del comportamiento
dinámico.
15
Capítulo 1 – Introducción a la Programación Orientada a Objetos.
Definiciones básicas
Definición de Objeto: En términos más generales, un objeto es una abstracción
conceptual del mundo real que se puede traducir a un lenguaje computacional o de
programación OO.
La abstracción de objeto se caracteriza por tener una identidad única que lo
distingue de otros objetos.
También tiene un estado, que permite informar lo que éste representa y su
comportamiento, es decir lo que él sabe hacer. Hablando en términos
computacionales, la identidad del objeto se puede interpretar como la referencia. El
estado de objeto es una lista de variables conocidas como sus atributos, cuyos valores
representan el estado que caracteriza al objeto.
El comportamiento es una lista de métodos, procedimientos, funciones u
operaciones que un objeto puede ejecutar a solicitud de otros objetos. Los objetos
también se conocen como instancias.
Definición de Clase: Es una colección de objetos similares. Estos objetos deben
tener los mismos atributos con valores posiblemente diferentes asignados a estos, y el
mismo conjunto de métodos que definen su comportamiento.
Por otro lado también se puede ver una clase como un molde, esquema o un
patrón que define la forma de sus objetos. O bien, como la estructura estática que
define:
a) el esquema de estados, y
b) el comportamiento que van a tener los objetos
A partir de ese esquema, dinámicamente durante la ejecución de un programa, se
van a ir creando objetos que pertenezcan a esa clase.
Definición de Herencia: Una clase puede heredar sus atributos y métodos a
varias subclases (la clase que hereda es llamada superclase). Esto significa que una
subclase, aparte de los atributos y métodos propios, tiene incorporados los atributos y
métodos heredados de la superclase.
Una subclase puede a su vez comportarse como una superclase y heredar a otras
clases, creando de esta manera la jerarquía de herencia. Cuando una clase hereda de
más de una superclase se conoce como herencia múltiple.
Definición de Envío de Mensajes: ¿Qué sucede cuando los objetos desean
comunicarse entre sí?. Un objeto recibe un estímulo externo de solicitud de un servicio
que se traduce en la invocación de un método de éste objeto.
Al ejecutarse el método, el objeto puede solicitar servicios de otros objetos,
enviándoles mensajes que implican a su vez la invocación de sus métodos, y dentro
de estos, nuevamente invocar servicios de otros objetos y así sucesivamente. Este
envío de mensajes en cascada representa el comportamiento dinámico del modelo de
objetos.
16
Capítulo 1 – Introducción a la Programación Orientada a Objetos.
Haciendo un paréntesis, es importante diferenciar entre lenguajes OO y lenguajes
que no lo son. Tenemos lenguajes:
Base-objetos: Lenguajes que no tienen herencia. Presentan un concepto parecido a
clase y alguna forma de crear objetos a partir de ésta.
Orientado a objetos: Presentan el concepto de objetos, clases y herencia de clases.
Diferencia entre el modelado por descomposición funcional y el OO.
Veamos la diferencia con un ejemplo
Problema: Hacer bife criollo
Resolución según la Descomposición funcional:
Se declara una lista de datos, los cuales se encuentran en un espacio común. El
algoritmo divide al problema en sub-problemas o funciones, partiendo de las más
complejas hasta llegar a las acciones más sencillas de realizar, llamadas también
atómicas.
En todo momento la descomposición del problema se hará identificando las
acciones (verbos) que se deben realizar.
Hacer bife criollo acompañado de papa se divide en dos subproblemas:
freír bifes con cebollas
añadir acompañamiento
Freír bifes con cebollas presenta tres subdivisiones:
freír bifes
preparar salsa con cebolla, perejil y pimientos verdes
juntar bifes con la salsa
Freír bifes y juntar bifes con la salsa son actividades que ya se saben realizar
(atómicas).
Sin embargo, el caso de preparar salsa con pimientos verdes aún podemos
aclararlo más:
Rehogar cebolla, el pimiento verde y el perejil
Agregar 1 ajo picado chico
freír la salsa
que ya son actividades atómicas.
Para el caso de añadir acompañamiento se particiona en dos actividades atómicas
añadir papa cocida
17
Capítulo 1 – Introducción a la Programación Orientada a Objetos.
añadir perejil
Si se deseara ahora preparar bife criollo con salsa de pimiento rojo, se tendría que
buscar cuales actividades hay que modificar. En este caso sería la parte de los datos y
preparar salsa verde, rehogar cebolla pimiento verde y perejil, en el caso de nuestro
ejemplo.
Sin embargo, lo complicado no termina en modificarlos, ya que no se sabe si en
alguna otra parte del sistema se puedan referenciar y entonces esto traiga problemas.
Descomposición OO
La modelación de esta solución se realiza a través de los sustantivos que definen
al problema. En este caso los sustantivos (nombres) más importantes son:
Bife_Criollo, Salsa_Verde, Ingredientes, Licuadora, Sartén y Acompañamiento.
Todos estos van a ser nuestros objetos que internamente van a integrar datos y
operaciones, que podemos asociarlos de manera lógica a cada uno de sus conceptos.
Cuando se pide haz_bife_criollo al objeto Bife_Criollo, este debe tener un método
que sabe hacer bife criollo apoyándose en el objeto Freír_Bifes_Con_Cebolla, que
tiene bifes como dato y saber freír bifes cómo método.
Para el caso de Salsa de pimiento verde, Bife_Criollo le envía un mensaje
diciéndole prepárate y entonces se preparará enviando mensajes a Ingredientes que
los Rehoga, Licuadora que los pica y finalmente llama a Sartén para que los fría.
Finalmente Bife_Criollo le va a enviar un mensaje a Acompañamiento, para que se
añada la papa y el perejil, y quede finalmente preparado nuestro plato Bife Criollo.
Es importante notar que los datos se encuentran implícitos formando parte de los
objetos.
Si en este modelo se desea hacer el Bife Criollo con Salsa de Pimientos Rojos,
solo bastaría sustituir el objeto Salsa de Pimientos Verdes por el objeto Salsa de
Pimientos Rojos, que se preparará, enviando mensajes a Ingredientes, Licuadora y
Sartén, sin modificar nada más.
18
Capítulo 2 – Programación Orientada a Objetos – Fijando Conceptos.
Capitulo 2.
PROGRAMACIÓN ORIENTADA A OBJETOS – FIJANDO CONCEPTOS
Introducción.
La Orientación a Objetos (OO) promete mejoras de amplio alcance en la forma de
diseño, desarrollo y mantenimiento del software proponiendo una solución a largo
plazo a los problemas y preocupaciones que han existido desde el comienzo en el
desarrollo de software:
la falta de portabilidad del código y reusabilidad,
código que es difícil de modificar,
ciclos de desarrollo largos, y
técnicas de codificación no intuitivas.
Un lenguaje orientado a objetos que ataca estos problemas, tiene 3 características
básicas:
debe estar basado en objetos,
debe estar basado en clases, y
ser capaz de tener herencia de clases.
Muchos lenguajes cumplen uno o dos de estos puntos; muchos menos cumplen
los tres. La barrera más difícil de sortear es usualmente la herencia.
Dado que la Programación Orientada a Objetos (POO) se basa en la idea natural
de la existencia de un mundo lleno de objetos y que la resolución del problema se
realiza en términos de objetos, un lenguaje se dice que está basado en objetos si
soporta objetos como una característica fundamental del mismo.
El elemento fundamental de la POO es, como su nombre lo indica, el objeto.
Podemos definir un objeto como un conjunto de datos y programas que poseen
estructura y forma parte de una organización.
Esta definición especifica varias propiedades importantes de los objetos. En primer
lugar, un objeto no es un dato simple, sino que contiene en su interior cierto número de
componentes bien estructurados. En segundo lugar, cada objeto no es un ente
aislado, sino que forma parte de una organización jerárquica o de otro tipo.
Información
La Información es una colección de datos que entra, fluye, se procesa y sale de un
Sistema.
El dato es una representación de la información con la estructura más adecuada
para su tratamiento o proceso.
19
Capítulo 2 – Programación Orientada a Objetos – Fijando Conceptos.
Sistemas de información.
Un Sistema es un conjunto de objetos ordenadamente relacionados entre sí, con
arreglo a unas reglas que cooperan para aportar los procesos necesarios para el
cumplimento de una función o finalidad determinada.
Un Sistema de Información se caracteriza porque sus procesos son la adquisición,
manipulación, uso, producción, almacenamiento y distribución de información.
Profundizando el concepto de Objeto.
Las personas tienen una idea clara de lo que es un objeto: conceptos adquiridos
que nos permiten sentir y razonar acerca de las cosas del mundo. Un objeto podría ser
real o abstracto, por ejemplo una organización, una factura, una figura en un
graficador, una pantalla de usuario, un avión, un vuelo de avión, etc.
En el análisis y diseño orientados a objetos (OO), interesa el comportamiento del
objeto. Si se construye software, los módulos de software OO se basan en los tipos de
objetos.
El software que implanta el objeto contiene estructuras de datos y operaciones que
expresan dicho comportamiento. Las operaciones se codifican como métodos.
Entonces, dentro del software orientado a objeto, un objeto es cualquier cosa, real
o abstracta, acerca de la cual almacenamos datos y los métodos que controlan dichos
datos.
Un objeto puede estar compuesto por otros objetos. Estos últimos a su vez
también pueden estar compuestos por otros objetos. Esta intrincada estructura es la
que permite construir objetos muy complejos.
Tipo de objeto.
Los conceptos que poseemos se aplican a tipos determinados de objetos. Por
ejemplo, “empleado” se aplica a los objetos que son personas empleadas por alguna
organización. Algunas instancias de empleado podrían ser Juan Pérez, José Martínez,
etc.
En el análisis orientado a objetos, estos conceptos se llaman tipos de objetos; las
instancias se llaman objetos.
Así, un tipo de objeto es una categoría de objeto, mientras que un objeto es una
instancia de un tipo de objeto.
En el mundo de las bases de datos existen los tipos de entidad, como cliente o
empleado. Existen muchas instancias de cada tipo de entidad (como Juan Pérez o
José Martínez para el tipo de entidad empleado). Del mismo modo, en OO se define
tipos de objetos e instancias de tipo de objeto.
20
Capítulo 2 – Programación Orientada a Objetos – Fijando Conceptos.
Sin embargo, el término objeto tiene diferencias fundamentales con el término
entidad, ya que la entidad sólo se refiere a los datos, mientras que objeto se refiere a
los datos y a los métodos mediante los cuales se controlan a los propios datos.
En OO, la estructura de datos y los métodos de cada tipo de objeto se manejan
juntos. No se puede tener acceso o control de la estructura de datos excepto mediante
los métodos que forman parte del tipo de objeto.
Estructura de un objeto.
Un objeto puede considerarse como una especie de cápsula dividida en tres
partes:
Relaciones
Propiedades
Métodos
Cada uno de estos componentes desempeña roles totalmente independientes:
Las relaciones permiten que el objeto se inserte en la organización y están
formadas esencialmente por punteros a otros objetos.
Las propiedades distinguen un objeto determinado de los restantes que
forman parte de la misma organización y tiene valores que dependen de la
propiedad de que se trate. Las propiedades de un objeto pueden ser
heredadas a sus descendientes en la organización.
Los métodos son las operaciones que pueden realizarse sobre el objeto,
que normalmente estarán incorporados en forma de programas (código)
que el objeto es capaz de ejecutar y que también pone a disposición de sus
descendientes a través de la herencia.
Veamos en detalle estos 3 conceptos:
1) Las relaciones entre objetos son, precisamente, los enlaces que permiten a un
objeto relacionarse con aquellos que forman parte de la misma organización.
Las hay de dos tipos fundamentales:
Relaciones jerárquicas. Son esenciales para la existencia misma de la
aplicación porque la construyen. Son bidireccionales, es decir, un objeto es
padre de otro cuando el primer objeto se encuentra situado inmediatamente
encima del segundo en la organización en la que ambos forman parte;
asimismo, si un objeto es padre de otro, el segundo es hijo del primero. Una
organización jerárquica simple puede definirse como aquella en la que un
objeto puede tener un solo padre, mientras que en una organización
jerárquica compleja un hijo puede tener varios padres.
Relaciones semánticas. Se refieren a las relaciones que no tienen nada que
ver con la organización de la que forman parte los objetos que las
establecen. Sus propiedades y consecuencias solo dependen de los
21
Capítulo 2 – Programación Orientada a Objetos – Fijando Conceptos.
objetos en sí mismos (de su significado) y no de su posición en la
organización.
Veámoslo con un ejemplo: supongamos que vamos a construir un diccionario
informatizado que permita al usuario obtener la definición de una palabra cualquiera.
Supongamos que, en dicho diccionario, las palabras son objetos y que la organización
jerárquica es la que proviene de forma natural de la estructura de nuestros
conocimientos sobre el mundo.
La raíz del diccionario podría llamarse TEMAS. De éste término genérico,
descenderán 3 grandes ramas de objetos llamadas VIDA, MUNDO y HOMBRE. El
primero (VIDA) comprenderá las ciencias biológicas: Biología y Medicina. El segundo
(MUNDO), las ciencias de la naturaleza inerte: las Matemáticas, la Física, la Química y
la Geología. El tercero (HOMBRE) comprenderá las ciencias humanas: la Geografía,
la Historia, etc.
Aplicamos el ejemplo: establecemos la relación trabajo entre los objetos NEWTON
y ÓPTICA y la interpretaremos diciendo que significa que Newton trabajó en Óptica. La
relación es, evidentemente, semántica, pues no establece ninguna connotación
jerárquica entre NEWTON y ÓPTICA y su interpretación depende exclusivamente del
significado de ambos objetos.
La existencia de esta relación nos permitirá responder a preguntas como:
¿Quién trabajó en óptica?, ¿En qué trabajó Newton?, ¿Quién trabajó en Física?
Las dos primeras se deducen inmediatamente de la existencia de la relación
trabajo. Para la tercera observamos que si Newton trabajó en Óptica automáticamente
sabemos que trabajó en Física, por ser óptica una rama de la Física (en nuestro
diccionario, el objeto ÓPTICA es hijo del objeto FÍSICA). Entonces gracias a la POO
podemos responder a la tercera pregunta sin necesidad de establecer una relación
entre NEWTON y FÍSICA, apoyándonos sólo en la relación definida entre NEWTON y
ÓPTICA y en que ÓPTICA es hijo de FÍSICA. De este modo se elimina toda
redundancia innecesaria y la cantidad de información que tendremos que definir para
todo el diccionario será mínima.
2) Propiedades. Todo objeto puede tener cierto número de propiedades, cada una
de las cuales tendrá, a su vez, uno o varios valores.
En POO, las propiedades corresponden a las clásicas “variables” de la
programación estructurada. Son, por lo tanto, datos encapsulados dentro del objeto,
junto con los métodos (programas) y las relaciones (punteros a otros objetos).
Las propiedades de un objeto pueden tener un valor único o pueden contener un
conjunto de valores más o menos estructurados (matrices, vectores, listas, etc.).
Además, los valores pueden ser de cualquier tipo (numérico, alfabético, etc.) si el
sistema de programación lo permite.
Pero existe una diferencia con las "variables", y es que las propiedades se pueden
heredar de unos objetos a otros. En consecuencia, un objeto puede tener una
propiedad de maneras diferentes:
22
Capítulo 2 – Programación Orientada a Objetos – Fijando Conceptos.
Propiedades propias. Están formadas dentro de la cápsula del objeto.
Propiedades heredadas. Están definidas en un objeto diferente, antepasado
de éste (padre, abuelo, etc.). A veces a estas propiedades se les llama
“propiedades miembro” porque el objeto las posee por el mero hecho de ser
miembro de una clase.
3) Métodos. Los métodos especifican la forma en que se controlan los datos de
un objeto. Los métodos, en un tipo de objeto sólo hacen referencia a la
estructura de datos de ese tipo de objeto. No deben tener acceso directo a las
estructuras de datos de otros objetos. Para utilizar la estructura de datos de
otro objeto, deben enviar un mensaje a éste.
Un objeto entonces es “una cosa” cuyas propiedades están representadas por
tipos de datos y su comportamiento por métodos.
El método es una operación que realiza el acceso a los datos. Podemos definir
método como un programa procedimental o procedural escrito en cualquier lenguaje,
que está asociado a un objeto determinado y cuya ejecución sólo puede
desencadenarse a través de un mensaje recibido por éste o por sus descendientes.
Sinónimos de “método”, son todos aquellos términos que se han aplicado
tradicionalmente a los programas, como procedimiento, función, rutina, etc. Sin
embargo, es conveniente utilizar el término “método” para que se distingan claramente
las propiedades especiales que adquiere un programa en el entorno POO, que afectan
fundamentalmente a la forma de invocarlo (únicamente a través de un mensaje) y a su
campo de acción, limitado a un objeto y a sus descendientes, aunque posiblemente no
a todos.
Los métodos son programas, por lo tanto, tienen argumentos o parámetros.
Como los métodos pueden heredarse de unos objetos a otros, un objeto puede
disponer de un método de dos maneras diferentes:
Métodos propios. Están incluidos dentro de la cápsula del objeto.
Métodos heredados. Están definidos en un objeto diferente, antepasado de
éste (padre, abuelo, etc.). A veces estos métodos se llaman métodos
miembro porque el objeto los posee por el mero hecho de ser miembro de
una clase.
Demonios.
Es un tipo especial de métodos, relativamente poco frecuente en los sistemas de
POO, que se activa automáticamente cuando sucede algo especial. Es decir, es un
programa, como los métodos ordinarios, pero se diferencia de estos porque su
ejecución no se activa con un mensaje, sino que se desencadena automáticamente
cuando ocurre un suceso determinado: la asignación de un valor a una propiedad de
un objeto, la lectura de un valor determinado, etc.
23
Capítulo 2 – Programación Orientada a Objetos – Fijando Conceptos.
Los demonios, cuando existen, se diferencian de otros métodos por que no son
heredables y porque a veces están ligados a una de las propiedades de un objeto,
más que al objeto entero.
Abstracción.
La Abstracción es una descripción especial simplificada de un sistema que hace
énfasis en ciertos rasgos y suprime otros.
La buena abstracción es aquella que logra hacer énfasis en los detalles
significativos o relevantes de la solución y discrimina cualquier otra característica. Con
esto se consigue un mapeo de los objetos del mundo real a los objetos del sistema.
Por ejemplo, la perspectiva de ver a un gato es muy distinta entre una abuela
amorosa y un médico veterinario. La abuela hará una abstracción fijándose en rasgos
afectivos y de cuidado mientras que el veterinario lo verá como un objeto anatómicofisiológico de estudio.
Modularidad.
La Modularidad es una partición funcional de todo el sistema. Cada módulo o parte
del sistema debe contar tanto con una funcionalidad clara y relativamente sencilla
como con una facilidad de combinarse con otros módulos.
Condicionantes tales como limitación de memoria, o las características de los
compiladores o lenguajes particulares, inducen a la modularidad, pero el principio en la
Tecnología Orientada a Objetos, es dividir funcionalmente buscando la interrelación
más rica entre módulos.
Una división temática de los módulos es una manera muy conveniente de
equilibrar la modularización. De esta manera, un módulo puede ser el de los cálculos
numéricos mientras que otro, el de las operaciones con cadenas, o bien, un módulo
puede ser el de las variables generales y de arranque y configuración de un sistema,
Un segundo módulo alojará las clases primitivas, de las cuales se derivarán siempre
para su uso otras clases y un tercer módulo tendrá las clases que usan a las
primitivas.
En el caso de nuestro gato, lo veríamos descompuesto en unidades funcionales.
Su corazón funciona muy bien, al igual su cuello y cola, cada parte debe ensamblar
perfectamente con las de su entorno para formar un gato completo.
Encapsulación de objetos.
En forma sucinta y concreta, podemos decir que es el “principio por el cual se
deben modelar al mismo tiempo y de forma inseparable Métodos y Datos”.
La Interfaz representa la frontera y el lugar de paso en la comunicación del objeto
con el mundo exterior.
24
Capítulo 2 – Programación Orientada a Objetos – Fijando Conceptos.
El empaque conjunto de datos y métodos se llama encapsulado. El objeto esconde
sus datos de los demás objetos y permite el acceso a los datos mediante sus propios
métodos. Esto recibe el nombre de “ocultamiento de información”.
El encapsulamiento evita la corrupción de los datos de un objeto. Si todos los
programas pudieran tener acceso a los datos de cualquier forma que quisieran los
usuarios, los datos se podrían corromper o utilizar de mala manera. El encapsulado
protege los datos del uso arbitrario o accidental.
El encapsulado oculta los detalles de su implantación interna a los usuarios de un
objeto. Los usuarios se dan cuenta de las operaciones que puede solicitar del objeto,
pero desconocen los detalles de cómo se lleva a cabo la operación. Todos los detalles
específicos de los datos del objeto y la codificación de sus operaciones están fuera del
alcance del usuario.
Así, encapsulado es el resultado (o acto) de ocultar los detalles de implantación de
un objeto respecto de su usuario.
El encapsulado, al separar el comportamiento del objeto de su implantación,
permite la modificación de ésta sin que se tengan que modificar las aplicaciones que lo
utilizan.
Esto no quiere decir, sin embargo, que sea imposible conocer lo necesario
respecto a un objeto y a lo que él contiene. Si así fuera, no se podría hacer gran cosa
con él. Lo que sucede es que las peticiones de información a un objeto deben
realizarse a través de mensajes dirigidos a él, con la orden de realizar la operación
pertinente. La respuesta a estas órdenes será la información requerida, siempre que el
objeto considere que quien envía el mensaje está autorizado para obtenerla.
Reutilización.
El hecho de que cada objeto sea una cápsula, facilita enormemente que un objeto
determinado pueda ser transportado a otro punto de la organización, o incluso a otra
organización totalmente diferente que precise de él. Si el objeto ha sido bien
construido, sus métodos seguirán funcionando en el nuevo entorno sin problemas.
Esta cualidad hace que la POO sea muy apta para la reutilización de programas.
Clase.
La clase es una colección de objetos con características comunes. Las clases son
entidades conceptuales que sirven para abstraer y modelizar un sistema.
Toda clase posee 2 tipos o clases de componentes:
Una estática: los datos. Caracterizan los posibles estados que pueden
adoptar los Objetos de la Clase en un instante determinado.
Otra dinámica: los métodos. Caracterizan los posibles comportamientos de
los Objetos de la Clase a lo largo de su existencia.
25
Capítulo 2 – Programación Orientada a Objetos – Fijando Conceptos.
Los criterios de clasificación de una Clase son:
Atributos: Variables que tomarán ciertos valores (Datos) en un estado del
Objeto. Definen la estructura o componente estática de los Objetos.
Eventos: Estímulos ante los que reaccionan los Objetos cambiando de
Estado.
Funciones: Ante un evento, actúan sobre los datos haciendo que el Objeto
cambie de estado. Determinan la componente dinámica de los Objetos.
Así, una clase es una creación de un tipo de objeto. Especifica una estructura de
datos y los métodos operativos permisibles que se aplican a cada uno de sus objetos.
Herencia.
Es una relación transitiva entre clases, que permite a un objeto de una clase utilizar
como propios los datos y métodos definidos en otra clase. Por ejemplo: La clase
cliente hereda de la clase persona sus métodos y datos.
La clase padre se suele denominar superclase.
La clase hija se suele
especialización o heredada.
denominar
subclase,
descendiente,
derivada,
La Subclase consta de dos tipos de características:
Estructura o comportamiento heredado de la superclase.
Estructura o comportamiento propios.
La subclase se especializa de las siguientes formas:
Enriquecimiento: Estructura o comportamiento añadidos como propios.
Ocultación: Estructura o comportamiento heredado y anulado
Sustitución: Estructura o comportamiento heredado y redefinido
Ejemplos de herencia.
Dijimos que un tipo de objeto de alto nivel puede especializarse en tipos de objetos
de bajo nivel.
Por ejemplo, el tipo de objeto persona, puede tener subtipos estudiante y
empleado. A su vez, el tipo de objeto estudiante puede tener como subtipo estudiante
de pre-grado y estudiante de post-grado, mientras que empleado puede tener como
subtipo a académico y administrativo. Existe de este modo una jerarquía de tipos,
subtipos, sub-subtipos, etc.
Entonces, una clase implanta el tipo de objeto. Una subclase hereda propiedades
de su clase padre; una sub-subclase hereda propiedades de las subclases; etc.
26
Capítulo 2 – Programación Orientada a Objetos – Fijando Conceptos.
Una subclase puede heredar la estructura de datos y los métodos de su
superclase. También tiene sus propios métodos e incluso sus propios tipos de datos.
Tipos de herencia.
Simple: Se hereda de una sola clase.
Múltiple: Se hereda de dos o más clases.
Polimorfismo.
Una de las características fundamentales de la POO es el polimorfismo, que no es
otra cosa que la posibilidad de construir varios métodos con el mismo nombre, pero
con relación a la clase a la que pertenece cada uno, con comportamientos diferentes.
Esto conlleva la habilidad de enviar un mismo mensaje a objetos de clases
diferentes. Estos objetos recibirían el mismo mensaje global pero responderían a él de
formas diferentes; por ejemplo, un mensaje "+" a un objeto ENTERO significaría suma,
mientras que para un objeto STRING significaría concatenación.
Jerarquías.
Organización de los objetos
En principio, los objetos forman siempre una organización jerárquica, en el sentido
de que ciertos objetos son superiores a otros de cierto modo. Existen varios tipos de
jerarquías: serán simples cuando su estructura pueda ser representada por medio de
un "árbol". En otros casos puede ser más compleja.
En cualquier caso, sea la estructura simple o compleja, podrán distinguirse en ella
3 niveles de objetos:
La raíz de la jerarquía: Se trata de un objeto único y especial. Este se
caracteriza por estar en el nivel más alto de la estructura y suele recibir un
nombre muy genérico, que indica su categoría especial, como por ejemplo
objeto madre, raíz o entidad.
Los objetos intermedios: Son aquellos que descienden directamente de la
raíz y que a su vez tienen descendientes. Representan conjuntos o clases
de objetos, que pueden ser muy generales o muy especializados, según la
aplicación. Normalmente reciben nombres genéricos que denotan al
conjunto de objetos que representan, por ejemplo, VENTANA, CUENTA,
FICHERO.
Los objetos terminales: Son todos aquellos que descienden de una clase o
subclase y no tienen descendientes. Suelen llamarse casos particulares,
instancias o ítem, porque simbolizan los elementos del conjunto
representado, por la clase o subclase a la que pertenecen.
La jerarquía es el orden por niveles de todas las abstracciones.
27
Capítulo 2 – Programación Orientada a Objetos – Fijando Conceptos.
Las dos partes más importantes de la jerarquía son la estructura de clases y la
estructura de objetos. La primera establece todas las relaciones de herencia en sus
modalidades permitidas, la segunda, la dinámica de mensajes.
La organización de todas las abstracciones logradas en un sistema está en un
orden riguroso que categoriza objetos de un mismo o semejante tipo dentro de una
misma categoría.
Así al hablar de manzanas, podremos hablar de las amarillas, las rojas, o las
verdes, pero en otra categoría hablaremos de sus componentes, pulpa, piel, semilla, y
posiblemente en una categoría más inferior de sus tejidos. Ni los tejidos sabrán a qué
componente pertenecen ni los componentes a qué manzana pertenecen.
La abstracción superior no sabe de sus constituyentes ínfimos y viceversa. Cada
abstracción debe connotar un nivel.
Mensajes
Para que un objeto haga algo, le enviamos una solicitud. Esta hace que se
produzca una operación. La operación ejecuta el método apropiado y, de manera
opcional, produce una respuesta. El mensaje que constituye la solicitud contiene el
nombre del objeto, el nombre de una operación y, a veces, un grupo de parámetros.
La programación orientada a objetos es una forma de diseño modular en la que
con frecuencia el mundo se piensa en términos de objetos, operaciones, métodos y
mensajes que se transfieren entre tales objetos. Un mensaje es una solicitud para que
se lleve a cabo la operación indicada y se produzca el resultado.
Los objetos pueden ser muy complejos, puesto que pueden contener muchos subobjetos, éstos a su vez pueden contener otros, etc. La persona que utilice el objeto no
tiene que conocer su complejidad interna, sino la forma de comunicarse con él y la
forma en que le responde.
28
Capitulo 3.
JAVA Y LA PROGRAMACIÓN ORIENTADA A OBJETOS
Introducción. ¿Porqué Java?
Java es un lenguaje puro Orientado a Objetos, es decir cumple perfectamente el
paradigma de OO. Se ha elegido este lenguaje para ejemplificar los siguientes
conceptos de PPO:
Objetos
Clase
Modularidad
Herencia
Jerarquías
Abstracción
Encapsulación
Ligaduras Dinámicas o Binding
Mensajes.
Polimorfismo o Sobrecarga
Redefinición
Es un lenguaje de programación relativamente joven, a pesar de ello, desde su
aparición hasta nuestros días, el crecimiento de su uso ha sido vertiginoso. Esto se
debe en gran parte a la naturaleza misma del lenguaje, que ha permitido a los
creadores de Java añadir al lenguaje bibliotecas de clases que pueden ser fácilmente
adaptadas a las necesidades del desarrollador de software.
Sun Microsystems, es la empresa que ha desarrollado el lenguaje Java, en un
intento de resolver simultáneamente todos los problemas que se le plantean a los
desarrolladores de software por la proliferación de arquitecturas incompatibles, tanto
entre las diferentes máquinas como entre los diversos sistemas operativos y sistemas
de ventanas que funcionaban sobre una misma máquina, añadiendo la dificultad de
crear aplicaciones distribuidas en una red como Internet.
Origen de Java.
Entre las “leyendas” más difundidas, respecto del origen del lenguaje Java, se
encuentra aquella que dice que:
En enero de 1991, James Gosling, Mike Sheridan, Chris Warth, Ed Frank y Patrick
Naughton, entre otros, de Sun Microsystems, se reúnen y toman la idea de que el
futuro de la Informática pasa porque todos los electrodomésticos y productos de
consumo estén controlados por un ordenador. Muchos han colaborado en el diseño y
evolución del lenguaje: Bill Joy, Arthur van Hoff, Jonathan Payne, Frank Yellin y Tim
Lindholm, por citar algunos de ellos.
Tras unos comienzos dudosos, Sun decidió crear una filial, denominada
FirstPerson Inc., para dar margen de maniobra al equipo responsable del proyecto.
29
Capítulo 3 – Java y la Programación Orientada a Objetos.
El mercado, inicialmente previsto para los programas de FirstPerson, eran los
equipos domésticos: microondas, tostadoras y, fundamentalmente, televisión
interactiva. Este mercado, dada la falta de pericia de los usuarios para el manejo de
estos dispositivos, requería interfases mucho más cómodos e intuitivos que los
sistemas de ventanas que proliferaban en el momento.
Otros requisitos importantes a tener en cuenta eran la fiabilidad del código y la
facilidad de desarrollo.
James Gosling, el miembro del equipo con más experiencia en lenguajes de
programación, decidió que las ventajas aportadas por la eficiencia de C++ no
compensaban el gran coste de pruebas y depuración. James Gosling había estado
trabajando en su tiempo libre en un lenguaje de programación que él había llamado
Oak, el cual, aún partiendo de la sintaxis de C++, intentaba remediar las deficiencias
que iba observando.
Los lenguajes como C o C++, deben ser compilados para un chip, y si se cambia el
chip, todo el software debe compilarse de nuevo. Esto encarece mucho los desarrollos
y el problema es especialmente sentido en el campo de la electrónica de consumo. La
aparición de un chip más barato y, generalmente, más eficiente, conduce
inmediatamente a los fabricantes a incluirlo en las nuevas series de sus cadenas de
producción, por pequeña que sea la diferencia en precio ya que, multiplicada por la
tirada masiva de los aparatos, supone un ahorro considerable. Por tanto, Gosling
decidió mejorar las características de Oak y utilizarlo.
El primer proyecto en que se aplicó este lenguaje recibió el nombre de Proyecto
Verde (Green Project) y consistía en un sistema de control completo de los aparatos
electrónicos y el entorno de un hogar. Para ello se construyó un ordenador
experimental denominado *7 (Star Seven), estamos hablando del año 1992. El sistema
presentaba una interfaz basada en la representación de la casa de forma animada y el
control se llevaba a cabo mediante una pantalla sensible al tacto. En el sistema
aparecía Duke, la actual mascota de Java (esa especie de pingüinito de cabeza negra,
cuerpo blanco y nariz roja). Posteriormente se aplicó a otro proyecto denominado VOD
(Video On Demand) en el que se lo empleaba como interfaz para la televisión
interactiva. Ninguno de estos proyectos se convirtió nunca en un sistema comercial,
pero fueron desarrollados enteramente en un Java primitivo y fueron como su
bautismo de fuego.
En 1993, la compañía de TV, Time Warner, pretende introducirse en el mercado de
la televisión interactiva. Sun y Silicon Graphics compiten para hacerse con el proyecto.
Lo consigue Sun. Una vez que en Sun se dieron cuenta de que a corto plazo la
televisión interactiva no iba a ser un gran éxito, urgieron a FirstPerson a desarrollar
con rapidez nuevas estrategias que produjeran beneficios. No lo consiguieron y
FirstPerson cerró en la primavera de 1994. Los miembros del equipo, al que pertenece
James Gosling, pasan a Sun Interactive, otra filial de Sun creada en 1994.
En paralelo, aparece en 1993 el navegador NCSA Mosaic, que facilitará el acceso
interactivo a la Internet. El número de usuarios de la Internet crece exponencialmente.
En 1994, Bill Joy decide escribir un navegador (WebRunner, tipo Mosaic) en código
Oak, en principio, sólo como una demo, que se convertirá más adelante en HotJava en
septiembre de dicho año. James Gosling escribe un compilador de Java. Hoffman
escribe otro en Java.
30
Capítulo 3 – Java y la Programación Orientada a Objetos.
En marzo de 1995, la versión “1.0a” sale al mundo exterior y el código fuente de
Java se distribuye en Internet.
Y bueno ...., a partir de allí el uso intensivo de Java no ha parado de crecer.
Lo mejor será hacer caso omiso de las historias que pretenden dar carta de
naturaleza a la clarividencia industrial de sus protagonistas; porque la cuestión es si,
independientemente de su origen y entorno comercial, Java ofrece soluciones a
nuestras expectativas. Porque tampoco vamos a desechar la penicilina aunque haya
sido su origen fruto de la casualidad.
Recomiendo leer en Internet, una muy completa historia de Java en la siguiente
dirección: http://ei.cs.vt.edu/~wwwbtb/book/chap1/java_hist.html
Características de Java
Es importante ver las características del lenguaje Java, para decidir si lo utilizamos
o no, según el tipo de trabajo que debemos encarar. Estas características
seguramente son opinables según el punto de vista que se las analice. Algunas de
ellas son:
Tamaño reducido
Simplicidad
Portabilidad
Interpretado
Distribuido
Robusto
Seguro
Programación concurrente y distribuida
Dinámico
Liberación de memoria no utilizada
Manejo de Arreglos
Manejo de excepciones
Analicemos estas características individualmente:
Reducido: Todo el paquete de desarrollo que incluye la librería de clases,
los programas de compilación, interpretación, depuración, aplicaciones,
Applets ejemplo, etc., ocupa aproximadamente 10 Mb en disco.
Simple: Porque, prácticamente sin saber mucho de programación, se
aprende a hacer Applets y aplicaciones fácilmente; pudiéndose obtener
resultados en poco tiempo. Java ofrece toda la funcionalidad de un lenguaje
31
Capítulo 3 – Java y la Programación Orientada a Objetos.
potente, pero sin las características menos usadas y más confusas de
éstos.
C++ es un lenguaje que adolece de falta de seguridad, pero es muy
difundido, por ello, Java se diseñó para ser parecido a C++ y así facilitar un
rápido y fácil aprendizaje.
Java elimina muchas de las características de otros lenguajes como C++,
para mantener reducidas las especificaciones del lenguaje y añadir
características muy útiles como el reciclador de memoria dinámica (ver
explicación detallada más abajo).
Java reduce en un 50% los errores más comunes de programación con
lenguajes como C y C++ al eliminar muchas de las características de éstos,
entre las que destacan:
a. aritmética de punteros
b. no existen referencias
c. registros (struct)
d. definición de tipos (typedef)
e. macros (#define)
f.
necesidad de liberar memoria (free)
Portable, Interpretado: este era el principal objetivo que Sun buscaba, pues
permite interpretar los programas Java desde cualquier plataforma de
programación. Y es un lenguaje de plataforma independiente, tanto, al nivel
de código fuente, como al nivel binario.
Podemos escribir código Java en una plataforma y marcharnos a otra con
la garantía de que ésta también entenderá el código fuente sin necesidad
de tener que rescribirlo.
Por otro lado, los archivos binarios Java resultado de la ejecución,
conocidos por bytecodes, podrán ejecutarse desde cualquier plataforma sin
necesidad de ninguna recompilación. Los bytecodes equivalen a código
máquina pero sin ser específicos de ningún procesador. Esto es así porque
para ejecutar los programas en Java se hacen dos operaciones:
1) la compilación obteniendo los bytecodes (usando el compilador
javac.exe) y
2) la interpretación de estos desde cada plataforma con el intérprete
Java (usando java.exe).
El inconveniente del uso de bytecodes, como tenemos ocasión de ver
cuando cargamos un Applet desde una página web, es la pérdida de
velocidad de ejecución.
Robusto, Seguro: permite la transferencia y el manejo de datos sin errores
aparentes; siempre será posible la caída de la red o de nuestra maquina.
Cada vez que se transfiere al ordenador de un usuario un programa, se
corre el riesgo de recibir un virus, caballos de Troya, etc.
Además, existen otros programas maliciosos contra los que hay que
protegerse: estos programas pueden recoger información privada del
usuario como números de tarjetas de crédito, cuentas bancarias y palabras
32
Capítulo 3 – Java y la Programación Orientada a Objetos.
de acceso que pueden ser obtenidas analizando el contenido del sistema
de ficheros local de la computadora cliente.
Java establece un cortafuegos (firewall) entre una aplicación de red y la
computadora local: cuando se carga desde el navegador de un usuario una
página web que contiene un applet Java, no hay ningún peligro de ser
infectados por un virus o a recibir intentos de accesos malintencionados.
Java consigue esto creando un entorno de ejecución aislado para ese
applet, que no tiene acceso a los recursos locales de la máquina cliente.
Ésta es una de las grandes ventajas de Java, en cuanto a seguridad se
refiere.
Concurrente: permite ejecutar varios programas Java (applets) a la vez en
el navegador. Java al ser multitareas (multithreaded), permite muchas
actividades simultáneas en un programa. Los threads (a veces llamados,
procesos ligeros o hilos), son básicamente pequeños procesos o piezas
independientes de un gran proceso.
Al estar los threads construidos en el lenguaje, son más fáciles de usar y
más robustos que sus homólogos en C o C++. El beneficio de ser
multithreaded consiste en un mejor rendimiento interactivo y mejor
comportamiento en tiempo real.
Aunque el comportamiento en tiempo real está limitado a las capacidades
del sistema operativo subyacente (Unix, Windows, etc.), aún supera a los
entornos de flujo único de programa (single-threaded), tanto en facilidad de
desarrollo, como en rendimiento.
Como ejemplo de todo esto, supongamos la visualización de una imagen
desde Internet. Con Java, esta imagen se puede ir trayendo en un thread
independiente, permitiendo que el usuario pueda acceder a la información
en la página sin tener que esperar por el navegador.
Distribuido: Java se ha construido con extensas capacidades de
interconexión TCP/IP (Transmission Control Protocol/Internet Protocol).
Existen librerías de rutinas para acceder e interactuar con protocolos como
http y ftp. Esto permite a los programadores acceder a la información a
través de la red con tanta facilidad como a los ficheros locales.
Java en sí no es distribuído, sino que proporciona las librerías y
herramientas para que los programas puedan ser distribuidos, es decir, que
se ejecuten en varias máquinas, interactuando.
Como ejemplo, Java también tiene la posibilidad de que los objetos puedan
ejecutar procedimientos remotos; esta facilidad se llama RMI (Remote
Method Invocation). Esto le aporta más características del modelo
cliente/servidor.
Dinámico: por su capacidad de interactuar con el usuario, Java se beneficia
todo lo posible de la tecnología orientada a objetos. Java no intenta
conectar todos los módulos que comprenden una aplicación hasta el tiempo
de ejecución. Las librerías nuevas o actualizadas no paralizarán las
aplicaciones actuales (siempre que mantengan el API (Application
Programming Interface)). La siguiente figura muestra las diferencias entre
un navegador convencional y uno con Java.
33
Capítulo 3 – Java y la Programación Orientada a Objetos.
Java también simplifica el uso de protocolos nuevos o actualizados. Si
nuestro ejecuta una aplicación Java sobre la red y encuentra una pieza de
la aplicación que no sabe manejar, es capaz de traer automáticamente
cualquiera de esas piezas que el sistema necesita para funcionar, desde el
servidor remoto.
Java, para evitar tener que ir descargando módulos de byte-codes, objetos
o nuevas clases, implementa las opciones de persistencia, para que no se
eliminen cuando se limpie la caché de la máquina.
Administrador de Memoria: Es una tarea de la cual el programador no debe
preocuparse. Java elimina muchas de las características de otros lenguajes
como C++, para mantener reducidas las especificaciones del lenguaje y
añadir características muy útiles como el garbage collector (reciclador de
memoria dinámica). No es necesario preocuparse de liberar memoria, el
reciclador se encarga de ello y como es un thread de baja prioridad, cuando
entra en acción, permite liberar bloques de memoria muy grandes, lo que
reduce la fragmentación de la memoria.
Administrador de Arreglos: Posee un eficiente sistema de administración de
arreglos (array), que permite almacenar gran cantidad de datos en
34
Capítulo 3 – Java y la Programación Orientada a Objetos.
memoria, con un acceso directo a ellos. Implementa los arrays auténticos,
en vez de listas enlazadas de punteros, con comprobación de límites, para
evitar la posibilidad de sobreescribir o corromper memoria resultado de
punteros que señalan a zonas equivocadas.
Excepciones: Se dispone de una buena batería de métodos que controlan
los posibles errores que pudiesen ocurrir durante la ejecución del programa,
los que evitan, estando bien utilizados, que el programa cancele su
ejecución.
Una excepción es una condición anormal que surge en una secuencia de
código durante la ejecución de un programa. Es decir, es un "error de
ejecución".
En los lenguajes de programación que no tienen gestión de excepciones,
hay que controlar los posibles errores de ejecución del programa
manualmente, usando códigos de error. Sin embargo, Java posee gestión
de excepciones, y lleva el problema de la gestión del error en tiempo de
ejecución al mundo orientado a objetos.
En Java, cuando surge una condición excepcional, se crea un objeto que
representa la excepción y se envía al método que provocó esta excepción.
Este método puede elegir gestionar la excepción él mismo o pasarla. Pero
en algún punto, la excepción es capturada y procesada.
Las excepciones pueden ser generadas por el intérprete de Java o pueden
ser generadas por el propio código. Las excepciones generadas por Java
están relacionadas con errores que violan las reglas del lenguaje Java o las
restricciones del entorno de ejecución de Java. Las excepciones generadas
manualmente se suelen utilizar para informar de algún error al método
llamante.
35
Capítulo 4 – Conceptos de Orientación a Objetos.
Capitulo 4.
PROFUNDIZANDO CONCEPTOS DE POO – EJEMPLOS CON JAVA
En este capítulo se analizarán los siguientes conceptos de la POO, pero
aplicados a ejemplos básicos utilizando el lenguaje de programación JAVA.
1. Herencia
2. Sobrescritura de métodos
3. La clase base y la clase derivada
4. Redefinición de Métodos Heredados
5. Clases y Métodos Abstractos
6. Sobrecarga/Polimorfismos
7. Ligaduras Dinámicas ó Binding
8. Redefinición
9. Interfases
10. Permisos de Accesos
11. Transformaciones de tipos ó Casting
12. Sincronización
1. Herencia.
La herencia, concepto ya creado por Dahl y Nygaar en 1965, es una propiedad
esencial de la Programación Orientada a Objetos que consiste en la creación de
nuevas clases a partir de otras ya existentes
Es el mecanismo mediante el cual un objeto adquiere (o hereda) las propiedades
de otro. De esta forma se consigue la clasificación jerárquica. Si no se hiciera una
clasificación jerárquica de los objetos, cada objeto debería definir todas sus
características explícitamente, y esto no sería viable.
Sin embargo, utilizando la herencia, un objeto puede heredar sus atributos
generales de otro objeto (su padre), y definir explícitamente sólo aquellas cualidades
que lo hacen único dentro de su clase. Por tanto, la herencia es el mecanismo que
permite a un objeto ser una instancia específica de un caso más general.
Una clase que hereda de otra, se denomina subclase, y aquella clase de la que se
hereda, se denomina superclase.
Por ejemplo, supongamos que se define una clase para describir a los animales;
se podría definir otra clase para describir a los mamíferos. La clase "mamíferos"
heredaría todos los atributos de la clase "animales", pero además definiría una serie
de atributos específicos de los mamíferos, como el tipo de dientes, o las glándulas
mamarias. En este caso, la clase "mamíferos" es subclase de "animales", y la clase
"animales" es superclase de "mamíferos".
Una subclase hereda todos los atributos de cada uno de sus antecesores en la
jerarquía de clases. Esta característica permite a los programas orientados a objetos
crecer en complejidad de manera lineal en vez de geométrica.
37
Capítulo 4 – Conceptos de Orientación a Objetos.
La herencia es la característica fundamental que distingue un lenguaje orientado a
objetos, como el C++, Smalltalk, Eiffel o Java, de otro convencional como C, BASIC,
etc.
Herencia en Java.
La herencia permite las clasificaciones jerárquicas. En Java, para heredar una
clase, se utiliza la palabra reservada extends.
Por ejemplo, si la clase B hereda la clase A (B es subclase de A, A es superclase
de B), la declaración de la clase B sería así:
class B extends A {
// variables de instancia
// métodos
}
Así, la clase B hereda todas las variables de instancia y métodos de la clase A, y
además define sus propias variables de instancia y métodos. Pero hay una excepción:
una subclase no puede acceder a aquellos miembros de la superclase que han sido
declarados como private.
2. Sobrescritura de métodos.
En una jerarquía de clases, cuando un método de una subclase tiene el mismo
nombre y tipo que un método de su superclase, entonces se dice que el método de la
subclase sobrescribe al método de la superclase.
Y cuando se llama a un método sobrescrito dentro de una subclase, siempre se
refiere a la versión del método definida en la subclase. La versión del método definida
por la superclase está oculta.
Y la llamada a una función sobrescrita se resuelve en tiempo de ejecución, en
lugar de durante la compilación; esto se denomina selección de método dinámica.
La selección de método dinámica es la forma que tiene Java de implementar el
polimorfismo durante la ejecución.
Así, combinando la herencia con la sobrescritura de métodos, una superclase
puede definir la forma general de los métodos que serán utilizados por todas sus
subclases. Por otra parte, este polimorfismo dinámico es uno de los mecanismos más
poderosos que ofrece el diseño orientado a objetos para soportar la reutilización del
código y la robustez.
3. La clase base y la clase derivada.
La herencia ofrece una ventaja importante, permite la reutilización del código. Una
vez que una clase ha sido depurada y probada, el código fuente de dicha clase no
necesita modificarse. Su funcionalidad se puede cambiar derivando una nueva clase
que herede la funcionalidad de la clase base y le añada otros comportamientos.
38
Capítulo 4 – Conceptos de Orientación a Objetos.
La programación en los entornos gráficos, en particular Windows, con el lenguaje
C++, es un ejemplo ilustrativo. Los compiladores como los de Borland y Microsoft
proporcionan bibliotecas de clases cuyas clases describen el aspecto y la conducta de
las ventanas, controles, menús, etc.
Una de estas clases denominada TWindow describe el aspecto y la conducta de
una ventana, tiene una función miembro denominada Paint, que no dibuja nada en el
área de trabajo de la misma.
Definiendo una clase derivada de TWindow, podemos redefinir en ella la función
Paint para que dibuje una figura.
Aprovechamos de este modo la ingente cantidad y complejidad del código
necesario para crear una ventana en un entorno gráfico. Solamente, tendremos que
añadir en la clase derivada el código necesario para dibujar un rectángulo, una elipse,
etc.
La base de la POO fue este tipo de programación, en entornos de ventanas, de
hecho SmallTalk se hizo para eso.
En el lenguaje Java, todas las clases derivan implícitamente de la clase base
Object, por lo que heredan las funciones miembro definidas en dicha clase.
En Java disponemos de los paquetes básicos:
java.lang: Clases propias del lenguaje, clase Object, clase String, clase
System y clases para los tipos asociados a las primitivas.
java.util: Clases para las utilidades, clase Date, clase Vector y clase
Hashtable.
java.io: Clases para la E/S y para el manejo de ficheros.
java.net: Clases para el soporte de redes; clases Socket y clase URL.
java.awt: Clases para la interfase gráfica, incluyendo las clases Window,
Menu, Button, Font, CheckBox.
java.applet: Clases que implementan las Applets de Java, incluyendo
la clase Applet y la interfase AudioClip.
La clase base.
Algunas de las típicas ocasiones en las que nos vemos obligados a crear clases
bases son, por ejemplo:
En la fase de análisis, nos damos cuenta que diversos tipos de datos tienen
algo en común, por ejemplo en el juego del ajedrez: peones, alfiles, rey, reina, caballos
y torres, son piezas del juego. Creamos, por lo tanto, una clase base pieza y
derivamos cada pieza individual a partir de dicha clase base.
39
Capítulo 4 – Conceptos de Orientación a Objetos.
En otra ocasión, precisamos ampliar la funcionalidad de un programa, sin tener
que modificar el código existente.
Ejemplos de aplicación del presente concepto los vemos en los programas:
Ventana.java, VentanaTitulos.java y VentanaApp.java.
Vamos a poner un ejemplo del segundo tipo, (ampliar la funcionalidad de un
programa) que simule la utilización de librerías de clases para crear una interfase
gráfica de usuario como Windows 3.1 o Windows 95.
Supongamos que tenemos una clase que describe la conducta de una ventana
muy simple, aquella que no dispone de título en la parte superior, por lo tanto no
puede desplazarse, pero si cambiar de tamaño actuando con el ratón en los bordes
derecho e inferior.
La clase Ventana tendrá las siguientes variables miembros:
la posición x e y de la ventana, de su esquina superior izquierda, y
las dimensiones de la ventana: ancho y alto.
public class Ventana {
protected int x;
protected int y;
protected int ancho;
protected int alto;
//constructor de la Clase Base
}
public Ventana(int x, int y, int ancho, int alto) {
this.x=x;
this.y=y;
this.ancho=ancho;
this.alto=alto;
}
Constructor (Ventana)
Nombre del constructor = nombre de la clase
Se encarga de todas las operaciones de inicialización necesarias.
No tiene valor de retorno.
Las funciones miembro, además del constructor serán las siguientes:
la función mostrar que simula una ventana en un entorno gráfico, aquí
solamente nos muestra la posición y las dimensiones de la ventana.
public void mostrar(){
System.out.println("posición : x
=" + x + ", y
40
Capítulo 4 – Conceptos de Orientación a Objetos.
=" + y);
System.out.println("dimensiones: ancho =" + ancho
+ ", alto =" + alto);
}
La función cambiarDimensiones (de forma relativa) que simula el
cambio en la anchura y altura de la ventana.
public void cambiarDimensiones(int dancho, int
dalto){
ancho += dancho;
alto += dalto;
}
Objetos de la clase base.
Como vemos en el código, el constructor de la clase base inicializa los cuatro
miembros dato. Llamamos al constructor creando un objeto de la clase Ventana (Java
invoca al constructor al crear el objeto).
La instanciación (new) reserva el lugar de almacenamiento e invoca al constructor
Ventana ventana=new Ventana (0, 0, 20, 30);
Desde el objeto ventana podemos llamar a las funciones miembro públicas
ventana.mostrar();
ventana.cambiarDimensiones(10, 10);
La clase derivada
Podemos incrementar la funcionalidad de la clase Ventana definiendo una clase
derivada denominada VentanaTitulo.
Los objetos de dicha clase tendrán todas las características de los objetos de la
clase base, pero además tendrán un título, y se podrán desplazar (se simula el
desplazamiento de una ventana con el ratón).
La clase derivada heredará las variables miembros de la clase base y las funciones
miembro, y tendrá una variable miembro más, el título de la ventana.
public class VentanaTitulo extends Ventana{
protected String titulo;
public VentanaTitulo(int x, int y, int ancho, int
alto, String nombre) {
super(x, y, ancho, alto);
titulo = nombre;
}
}
extends es la palabra reservada que indica que la clase VentanaTitulo deriva, o
es una subclase, de la clase Ventana.
41
Capítulo 4 – Conceptos de Orientación a Objetos.
La primera sentencia del constructor de la clase derivada es una llamada al
constructor de la clase base mediante la palabra reservada super.
La llamada super(x, y, ancho, alto); inicializa los cuatro miembros dato
de la clase base Ventana:( x, y, ancho, alto)
A continuación, se inicializan las variables miembros de la clase derivada, y se
realizan las tareas de inicialización que sean necesarias. Si no se llama explícitamente
al constructor de la clase base Java lo realiza por nosotros, llamando al constructor por
defecto si existe.
La función miembro denominada desplazar cambia la posición de la ventana,
añadiéndole el desplazamiento.
public void desplazar(int dx, int dy){
x+=dx;
y+=dy;
}
Ahora redefinimos la función miembro mostrar para mostrar una ventana con un
título.
public void mostrar(){
super.mostrar();
System.out.println("titulo
}
: "+titulo);
En la clase derivada se define una función que tiene el mismo nombre y los
mismos parámetros que la de la clase base. (redefinimos la función mostrar en la
clase derivada).
La función miembro mostrar de la clase derivada VentanaTitulo hace una
llamada a la función mostrar de la clase base Ventana, mediante
super.mostrar();
De este modo aprovechamos el código ya escrito, y le añadimos el código que
describe la nueva funcionalidad de la ventana (por ejemplo, que muestre el título).
Si nos olvidamos de poner la palabra reservada super llamando a la función
mostrar, tendríamos una función recursiva. (La función mostrar llamaría a mostrar
indefinidamente.)
public void mostrar(){ //¡ojo!,función recursiva
System.out.println("titulo : "+titulo);
mostrar();
}
Objetos de la clase derivada.
42
Capítulo 4 – Conceptos de Orientación a Objetos.
Creamos un objeto ventana de la clase derivada VentanaTitulo
VentanaTitulo ventana=new VentanaTitulo(0, 0, 20, 30,
"Principal");
Mostramos la ventana con su título, llamando a la función mostrar, redefinida en la
clase derivada
ventana.mostrar();
Desde el objeto ventana de la clase derivada llamamos a las funciones miembro
definidas en dicha clase
ventana.desplazar(4, 3);
Desde el objeto ventana de la clase derivada podemos llamar a las funciones
miembro definidas en la clase base.
ventana.cambiarDimensiones(10, -5);
Para mostrar la nueva ventana desplazada y cambiada de tamaño escribimos
ventana.mostrar();
Modificadores de acceso.
En la herencia, surge un nuevo control de acceso denominado protected.
Hemos puesto protected delante de las variables miembros x e y de la
clase base Ventana
public class Ventana {
protected int x;
protected int y;
//...
}
En la clase derivada, la función miembro desplazar accede a dichas
variables miembros
public class VentanaTitulo extends Ventana{
//...
public void desplazar(int dx, int dy){
x+=dx;
y+=dy;
}
}
43
Capítulo 4 – Conceptos de Orientación a Objetos.
Si cambiamos el modificador de acceso de los miembros x e y de la clase base
Ventana de protected a private, el compilador nos informará que los
miembros x e y no son accesibles.
Los miembros ancho y alto se pueden poner con acceso private, sin
embargo, es mejor dejarlos como protected ya que podrían ser utilizados por
alguna función miembro de otra clase derivada de VentanaTitulo.
Dentro de una jerarquía pondremos un miembro con acceso private, si estamos
seguros de que dicho miembro solamente va a ser usado por dicha clase.
Como vemos hay cuatro modificadores de acceso a los miembros dato y a los
métodos:
1. private, las variables y métodos de instancia privados sólo pueden ser
accedidos desde dentro de la clase. No son accesibles desde las subclases.
2. protected, sólo las subclases de la clase y nadie más, puede acceder a las
variables y métodos de instancia protegidos
3. public, cualquier clase desde cualquier lugar puede acceder a las variables y
métodos de instancia públicos
4. friendly, por defecto, si no se especifica el control de acceso, las variables
y métodos de instancia se declaran friendly (amigas), lo que significa que
son accesibles por todos los objetos dentro del mismo paquete, pero no por los
externos al paquete. Es lo mismo que protected.
Veamoslo resumido en los siguientes cuadros:
Clases dentro del mismo paquete:
Modificador de
acceso
Heredado
Accesible
Default
Private
Protected
Public
Si
No
Si
Si
Si
No
Si
Si
Modificador de
acceso
Heredado
Accesible
Default
Private
Protected
Public
No
No
Si
Si
No
No
No
Si
Clases en distintos paquetes:
44
Capítulo 4 – Conceptos de Orientación a Objetos.
Desde el punto de vista práctico, cabe reseñar que no se heredan los miembros
privados, ni aquellos miembros (dato o función) cuyo nombre sea el mismo en la clase
base y en la clase derivada.
La clase base object.
La clase Object es la clase raíz de la cual derivan todas las clases. Esta
derivación es implícita.
La clase Object define una serie de funciones miembro que heredan todas las
clases. Las más importantes son las siguientes:
public class Object {
public boolean equals(Object obj) {
return (this == obj);
}
protected native Object clone() throws
CloneNotSupportedException;
public String toString() {
return getClass().getName() + "@" +
Integer.toHexString(hashCode());
}
protected void finalize() throws Throwable { }
//otras funciones miembro...
}
Métodos que pueden ser redefinidos por el programador.
Igualdad de dos Objetos:
El método equals de la clase Object compara dos objetos uno que llama a la
función y otro es el argumento de dicha función.
equals() Indica si dos objetos son o no iguales. Devuelve true si son iguales,
tanto si son referencias al mismo objeto como si son objetos distintos con iguales
valores de las variables miembro.
Entonces, boolean objeto.equals(Object), demuestra si el objeto dado
como parámetro es igual al objeto actual.
Esta implementación comprueba si ambas referencias son iguales. Podemos
simplificar la consulta utilizando el operador = = (igualdad).
Las distintas clases de la librería de Java suelen sobrescribirlo para comprobar si
los contenidos de la instancia son los mismos.
45
Capítulo 4 – Conceptos de Orientación a Objetos.
Por ejemplo, cuando String sobrescribe este método compara si las cadenas
contenidas en ambas instancias son iguales.
Representación en forma de texto de un objeto:
El método toString() devuelve un String que contiene una representación
del objeto como cadena de caracteres, por ejemplo para imprimirlo o exportarlo.
El método toString imprime por defecto el nombre de la clase a la que
pertenece el objeto y su código (hash).
Esta función miembro se redefine en la clase derivada para mostrar la información
que nos interese acerca del objeto.
La función toString se llama automáticamente siempre que pongamos un
objeto como argumento de la función System.out.println o concatenado con
otro string.
public String toString() {
return getClass().getName() + "@" +
Integer.toHexString(hashCode());
}
Entonces: String objeto.toString()devuelve una representación del
objeto en formato de cadena de caracteres, es llamado automáticamente por Java
cuando necesita convertir el objeto en cadena. Esta implementación devuelve una
cadena que contiene el nombre de la clase del objeto, seguido de una arroba y del
código hash del mismo.
Duplicación de objetos:
El método clone crea un objeto duplicado (clónico) de otro objeto, es decir,
clone() crea un objeto a partir de otro objeto de la misma clase.
El método original heredado de Object lanza una
CloneNotSupportedException.
Si se desea clonar una clase hay que implementar la interfase Cloneable y
redefinir el método clone().
Este método debe hacer una copia miembro a miembro del objeto original. No
debería llamar al operador new ni a los constructores.
Entonces: Object objeto.clone() copia el objeto.
La intención es que, al sobreescribirlo, creemos un objeto nuevo cuyas
propiedades tengan el mismo valor que las del objeto a copiar. Es decir, que
x.clone.equals(x) sea cierto.
46
Capítulo 4 – Conceptos de Orientación a Objetos.
Finalización:
El método finalize se llama cuando va a ser liberada la memoria que ocupa el
objeto por el recolector de basura (garbage collector). Normalmente, no es necesario
redefinir este método en las clases, solamente en contados casos especiales. Por
ejemplo, si una clase mantiene un recurso que no es de Java como un descriptor de
archivo o un tipo de letra del sistema de ventanas, o cuando se han abierto varios
archivos durante la vida de un objeto, y se desea que los archivos estén cerrados
cuando dicho objeto desaparece, entonces sería acertado el utilizar la finalización para
asegurar que los recursos se liberan. Es similar a los destructores de C++.
La forma en la que se redefine este método es el siguiente.
class CualquierClase{
//..
protected void finalize() trows Throwable{
super.finalize();
//código que libera recursos externos
}
}
La primera sentencia que contenga la redefinición de finalize ha de ser una
llamada a la función del mismo nombre de la clase base, y a continuación le añadimos
cierta funcionalidad, habitualmente, la liberación de recursos, cerrar un archivo, etc.
Entonces: void objeto.finalize()es llamado por el recolector de basura
antes de eliminar el objeto. Esta implementación no hace nada, debe ser el
programador el que sobreescriba este método en caso de que quiera realizar algo en
especial antes de eliminar el objeto de la memoria.
Métodos que NO pueden ser Redefinidos:
getClass() devuelve un objeto de la clase class, al cual se le pueden
aplicar métodos para determinar el nombre de la clase, su superclase, las
interfases implementadas, etc.
Se puede crear un objeto de la misma clase que otro sin saber de qué clase es.
Entonces: Class objeto.getClass() devuelve un objeto de tipo class que
identifica el tipo de objeto que tenemos.
Por ejemplo podemos comprobar si x e y son del mismo tipo con:
x.getClass() = = y.getClass()
int objeto.hashCode() devuelve el código Hash del objeto.
Este código se utiliza para las tablas Hash, que son la manera más eficiente de
almacenar objetos. Para que dichas tablas funcionen necesitan que cada objeto
almacenado tenga un código numérico que se mantenga durante toda la ejecución del
47
Capítulo 4 – Conceptos de Orientación a Objetos.
programa y que sea el mismo para dos objetos si dichos objeto son iguales según el
método equals(). Un ejemplo sería, para las cadenas, devolver la longitud de la
misma.
notify(),
notifyAll()
wait()
Son métodos relacionados con las threads
Concretando:
Se puede construir una clase a partir de otra mediante el mecanismo de la
herencia.
Para indicar que una clase deriva de otra se utiliza la palabra extends,
class ClaseHija extends ClasePadre {
...
}
Cuando una clase deriva de otra, hereda todas sus variables y métodos.
Estas funciones y variables miembro pueden ser redefinidas (overridden) en la
clase derivada, que puede también definir o añadir nuevas variables y métodos.
En cierta forma es como si la sub-clase (la clase derivada) “contuviera” un objeto
de la super-clase; en realidad lo “amplía” con nuevas variables y métodos.
Java permite múltiples niveles de herencia, pero no permite que una clase derive
de varias (no es posible la herencia múltiple). Se pueden crear tantas clases derivadas
de una misma clase como se quiera.
Todas las clases de Java creadas por el programador tienen una super-clase.
Cuando no se indica explícitamente una super-clase con la palabra extends, la clase
deriva de java.lang.Object, que es la clase raíz de toda la jerarquía de clases
de Java. Como consecuencia, todas las clases tienen algunos métodos que han
heredado de Object.
4. Redefinición de métodos heredados
Una clase puede redefinir cualquiera de los métodos heredados de su super-clase
que no sean final. El nuevo método sustituye al heredado para todos los efectos en
la clase que lo ha redefinido.
Los métodos de la super-clase que han sido redefinidos pueden ser todavía
accedidos por medio de la palabra super desde los métodos de la clase derivada,
aunque con este sistema sólo se puede subir un nivel en la jerarquía de clases.
48
Capítulo 4 – Conceptos de Orientación a Objetos.
Los métodos redefinidos pueden ampliar los derechos de acceso de la super-clase
(por ejemplo ser public, en vez de protected o package), pero nunca
restringirlos.
Los métodos de clase static no pueden ser redefinidos en las clases derivadas.
Modificador final:
Si declaramos un método como final, indicaremos que no puede ser
sobreescrito en clases heredadas, por lo que mantendrá su implementación. Si es la
clase la que lleva el modificador (final), entonces nos aseguramos que nadie pueda
heredar de dicha clase
Por último, si declaramos una propiedad como final, crearemos una constante,
ya que dicha variable se puede inicializar, pero no modificar. Este último caso presenta
un uso más habitual de este modificador:
class Dias {
static final int LUNES=0, MARTES=1, MIERCOLES=2,
JUEVES=3, VIERNES=4, SABADO=5, DOMINGO=6;
}
5. Clases y métodos abstractos
Una clase abstracta (abstract) es una clase de la que no se pueden crear
objetos. Su utilidad es permitir que otras clases deriven de ella, proporcionándoles un
marco o modelo que deben seguir y algunos métodos de utilidad general. Las clases
abstractas se declaran anteponiéndoles la palabra abstract, por ejemplo:
public abstract class NombreClase { ... }
Una clase abstract puede tener métodos declarados como abstract, en
cuyo caso no se da definición del método. Si una clase tiene algún método abstract
es obligatorio que la clase sea abstract.
En cualquier sub-clase este método deberá ser redefinido o volver a declararse
como abstract (el método y la sub-clase). Una clase abstract puede tener
métodos que no son abstract.
Aunque no se puedan crear objetos de esta clase, sus sub-clases heredarán el
método completamente a punto para ser utilizado.
Debemos tener en cuenta que como los métodos static no pueden ser
redefinidos, un método abstract no puede ser static.
6. Polimorfismo
Esta palabra que significa "muchas formas", es una característica del lenguaje
Java que permite a una interfase ser usada por una clase general de acciones. En
49
Capítulo 4 – Conceptos de Orientación a Objetos.
términos más generales, el concepto de polimorfismo a menudo se expresa por la
frase "una interfaz, múltiples métodos". Esto significa que es posible diseñar una
interfaz genérica para un grupo de actividades relacionadas.
Es evidente que esta forma de trabajar ayuda a reducir la complejidad del diseño,
pues permite usar una misma interfaz para especificar un conjunto de acciones
similares. Será el compilador el que tendrá que seleccionar la acción concreta (esto
es, el método) para aplicar en cada situación. Como programadores, nosotros sólo
tenemos que conocer la interfaz general.
Más detalladamente, funciona así: cuando se llama a un método, el código
generado por el compilador consulta una tabla oculta de punteros a funciones que
dispone cada clase. La dirección de la función llamada no se asigna en tiempo de
compilación como ocurre con el enlace estático del C sino que se obtiene la dirección
de la función llamada justo en el momento antes de ejecutarse.
Repasando, el polimorfismo consiste en poder definir métodos con la misma firma
(nombre del método más argumentos) en diferentes niveles de la jerarquía de clases,
y, que durante la ejecución el programa, sepa discernir a qué método llamar en cada
caso.
Podemos entender el sobrepaso fácilmente con un modelo simplificado. Durante la
ejecución, cuando un mensaje es pasado a un objeto, la firma del mensaje es
comparada con la firma de los mensajes de la clase a la que pertenece dicho objeto.
Si se encuentra una igualdad el método correspondiente es ejecutado. Si no la firma
del mensaje es comparada con la firma de los mensajes de la superclase. El proceso
se repite hasta que una igualdad se encuentre. Una igualdad es garantizada, ya que el
compilador de Java hubiera generado un error en el tiempo de compilación si la firma
del mensaje invocado sobre un objeto no corresponde a ninguna firma dentro de la
clase a la que pertenece este o alguna superclase.
La idea básica es que una referencia a un objeto de una determinada clase es
capaz de servir de referencia o de nombre a objetos de cualquiera de sus clases
derivadas.
ClasePadre objeto1, objeto2;
objeto1= new ClaseHija(parámetros);
objeto2= new ClaseHija2(parámetros);
Análogamente una referencia a un objeto de una determinada Interfase es capaz
de servir de referencia o de nombre a objetos de cualquiera de las clases que
implementan dicha interfase.
Interface objeto1, objeto2;
objeto1= new Clase (parámetros);
objeto2= new Clase2 (parámetros);
Es decir, permite tratar de un modo unificado objetos distintos, aunque
pertenecientes a distintas sub-clases o bien a clases que implementan dicha interfase.
50
Capítulo 4 – Conceptos de Orientación a Objetos.
El polimorfismo tiene que ver con la relación que se establece entre la llamada a
un método y el código que efectivamente se asocia con dicha llamada. A esta relación
se llama vinculación o ligadura (binding).
7. Vinculación, Ligadura o Binding.
La vinculación puede ser:
temprana (en tiempo de compilación)
tardía (en tiempo de ejecución).
Con funciones normales o sobrecargadas se utiliza generalmente vinculación
temprana. Con funciones redefinidas se utiliza siempre vinculación tardía, excepto si el
método es final.
La vinculación tardía hace posible que, con un método declarado en una clase
base (o en una interfase) y redefinido en las clases derivadas (o en clases que
implementan esa interfase), sea el tipo de objeto y no el tipo de la referencia lo que
determine qué definición del método se va a utilizar. El tipo del objeto al que apunta
una referencia sólo puede conocerse en tiempo de ejecución, y por eso el
polimorfismo necesita evaluación tardía.
El polimorfismo permite a los programadores separar las cosas que cambian de las
que no cambian, y de esta manera hacer más fácil la ampliación, el mantenimiento y la
reutilización de los programas.
El polimorfismo puede hacerse con referencias de super-clases abstractas, superclases normales, e interfases.
Las interfases permiten ampliar muchísimo las posibilidades del polimorfismo por
su mayor flexibilidad y por su independencia de la jerarquía de clases estándar.
Conversión de objetos
El polimorfismo visto previamente está basado en utilizar referencias de un tipo
más “amplio” que los objetos a los que apuntan. Las ventajas del polimorfismo son
evidentes, pero hay una importante limitación: el tipo de la referencia (clase abstracta,
clase base o interfase) limita los métodos que se pueden utilizar y las variables
miembro a las que se pueden acceder.
Por ejemplo, un objeto puede tener una referencia cuyo tipo sea una interfase,
aunque sólo en el caso en que su clase o una de sus super-clases implementen dicha
interfase. Un objeto cuya referencia es un tipo interfase sólo puede utilizar los métodos
definidos en dicha interfase. Dicho de otro modo, ese objeto no puede utilizar las
variables y los métodos propios de su clase.
De esta forma las referencias de tipo interfase definen, limitan y unifican la forma
de utilizarse de objetos pertenecientes a clases muy distintas (que implementan dicha
interfase).
Si se desea utilizar todos los métodos y acceder a todas las variables que la clase
de un objeto permite, hay que utilizar un cast (lanzamiento) explícito, que convierta su
51
Capítulo 4 – Conceptos de Orientación a Objetos.
referencia más general en la del tipo específico del objeto. De aquí una parte
importante del interés del cast entre objetos (más bien entre referencias).
Para la conversión entre objetos de distintas clases, Java exige que dichas clases
estén relacionadas por herencia (una deberá ser subclase de la otra). Se realiza una
conversión implícita o automática de una subclase a una superclase siempre que se
necesite, ya que el objeto de la subclase siempre tiene toda la información necesaria
para ser utilizado en lugar de un objeto de la superclase.
La conversión en sentido contrario debe hacerse de modo explícito y puede
producir errores por falta de información o de métodos. Si falta información, se obtiene
una ClassCastException. No se puede acceder a las variables exclusivas de la
sub-clase a través de una referencia de la super-clase. Sólo se pueden utilizar los
métodos definidos en la super-clase, aunque la definición utilizada para dichos
métodos sea la de la sub-clase.
Por ejemplo, supóngase que se crea un objeto de una sub-clase B y se referencia
con un nombre de una super-clase A,
A a = new B();
en este caso el objeto creado dispone de más información de la que la referencia a
le permite acceder (podría ser, por ejemplo, una nueva variable miembro j declarada
en B). Para acceder a esta información adicional hay que hacer un cast explícito en la
forma (B)a.
System.out.println( ((B)a).j ); //imprime la variable j
Un cast de un objeto a la super-clase puede permitir utilizar variables -no métodosde la super-clase, aunque estén redefinidos en la sub-clase.
Considérese el siguiente ejemplo: La clase C deriva de B y B deriva de A. Las tres
definen una variable x. En este caso, si desde el código de la sub-clase C se utiliza:
x
this.x
super.x
((B)this).x
((A)this).x
//
//
//
//
//
//
se accede a la
se accede a la
se accede a la
subir un nivel
se accede a la
se accede a la
x de C
x de C
x de B. Sólo se puede
x de B
x de A
Métodos (funciones miembro).
Los métodos son funciones definidas dentro de una clase. Salvo los métodos
static o de clase, se aplican siempre a un objeto de la clase por medio del operador
punto (.). Dicho objeto es su argumento implícito. Los métodos pueden además tener
otros argumentos explícitos que van entre paréntesis, a continuación del nombre del
método.
52
Capítulo 4 – Conceptos de Orientación a Objetos.
La primera línea de la definición de un método se llama declaración (header), y el
código comprendido entre las llaves {…} es el cuerpo (body) del método.
public Float método() {
// header y comienzo del método
sentencias
// body o cuerpo
return valor
}
// final del método
El header consta del cualificador de acceso (public, en este caso), del tipo del
valor de retorno (Float en este ejemplo, void si no tiene), del nombre de la función
y de una lista de argumentos explícitos entre paréntesis, separados por comas. Si no
hay argumentos explícitos se dejan los paréntesis vacíos.
Los métodos tienen visibilidad directa de las variables miembro del objeto que es
su argumento implícito, es decir, pueden acceder a ellas sin cualificarlas con un
nombre de objeto y el operador punto. De todas formas, también se puede acceder a
ellas mediante la referencia this (this.variable) o sí, alguna variable local o
argumento las oculta.
El valor de retorno (return) puede ser un valor de un tipo primitivo o una
referencia. En cualquier caso no puede haber más que un único valor de retorno (que
puede ser un objeto o un arreglo). Se puede devolver también una referencia a un
objeto por medio de un nombre de interfase. El objeto devuelto debe pertenecer a una
clase que implemente esa interfase. Se puede devolver como valor de retorno un
objeto de la misma clase que el método o de una sub-clase, pero nunca de una superclase.
Los métodos pueden definir variables locales. Su visibilidad llega desde la
definición al final del bloque en el que han sido definidas. No hace falta inicializar las
variables locales en el punto en que se definen, pero el compilador no permite
utilizarlas sin haberles dado un valor. A diferencia de las variables miembro, las
variables locales no se inicializan por defecto.
Si en el header del método se incluye la palabra native
public native void metodo();
no hay que incluir el código o implementación del método. Este código deberá
estar en una librería dinámica (Dynamic Link Library o DLL). Estas librerías son
ficheros de funciones compiladas normalmente en lenguajes distintos de Java (C, C++,
Fortran, etc.). Es la forma de poder utilizar conjuntamente funciones realizadas en
otros lenguajes desde código escrito en Java.
Un método también puede declararse como synchronized
public synchronized double metodo(){...}
53
Capítulo 4 – Conceptos de Orientación a Objetos.
Estos métodos tienen la particularidad de que sobre un objeto no pueden
ejecutarse simultáneamente dos métodos que estén sincronizados.
Métodos sobrecargados (overloaded)
Otra de las ventajas de este lenguaje de programación es que nos permite definir
dos o más métodos dentro de la misma clase con el mismo nombre, siempre que la
declaración de sus parámetros sea diferente. En este caso se dice que el método está
sobrecargado y el proceso de definir un método así se conoce como sobrecarga del
método. La sobrecarga de métodos es una de las maneras en que Java implementa el
polimorfismo.
Cuando se llama a un método sobrecargado, el compilador actúa justo sobre la
versión cuyo tipo de parámetros coincida con los de la llamada. Así, se podría definir la
siguiente clase "SumaGenerica" que aglutinará las sumas de todos los tipos primitivos:
class SumaGenerica {
int suma (int a, int b) {
return a+b;
}
double suma (double a, double b) {
return a+b;
}
...
}
Realmente no sería necesario definir el método suma() para todos los tipos de
datos, pues aquí también interviene el casting implícito que hace Java. Por ejemplo,
una suma de float, llamaría automáticamente al método que devuelve double
siempre y cuando no esté definido el método que devuelve float, claro está.
No existe una regla exacta para saber si un método se debe sobrecargar o no.
Realmente, la idea es aprovechar las ventajas que ofrece esta forma de polimorfismo,
así que lo normal es sobrecargar aquellos métodos que estén intrínsicamente
relacionados, como es el caso del ejemplo anterior pero no nos debemos confundir.
Por ejemplo, el método sqrt(), aunque se llama igual, calcula de forma totalmente
diferente la raíz cuadrada de un número entero que la de uno de punto flotante. Aquí,
aunque aplicáramos sobrecarga al método, realmente no estaríamos respetando el
propósito para el que se creó el polimorfismo.
Hemos expuesto que al igual que C++, Java permite métodos sobrecargados
(overloaded), es decir, métodos distintos que tienen el mismo nombre, pero que se
diferencian por el número y/o tipo de los argumentos.
A la hora de llamar a un método sobrecargado, Java sigue las siguientes reglas
para determinar el método concreto que debe llamar:
1. Si existe el método cuyos argumentos se ajustan exactamente al tipo de los
argumentos de la llamada (argumentos actuales), se llama ese método.
54
Capítulo 4 – Conceptos de Orientación a Objetos.
2. Si no existe un método que se ajuste exactamente, se intenta promover los
argumentos actuales al tipo inmediatamente superior (por ejemplo char a int, int
a long, float a double, etc.) y se llama el método correspondiente.
3. Si sólo existen métodos con argumentos de un tipo más restringido (por
ejemplo, int en vez de long), el programador debe hacer un cast explícito en la
llamada.
4. El valor de retorno no influye en la elección del método sobrecargado. En
realidad es imposible saber desde el propio método lo que se va a hacer con
él. No es posible crear dos métodos sobrecargados, es decir con el mismo
nombre, que sólo difieran en el valor de retorno.
8. Redefinición
Una clase puede redefinir (override) un método heredado de una superclase.
Redefinir un método es dar una nueva definición. En este caso el método debe tener
exactamente los mismos argumentos en tipo y número que el método redefinido. Una
sugerencia importante es que la redefinición debe ser algo excepcional en POO.
Sobrecarga de Constructores
Es aquí donde realmente se aprecian los beneficios del polimorfismo. Como
sabemos, el constructor de una clase es el que inicializa los valores que el
programador crea conveniente cuando ésta (la clase) se instancia. Pues bien,
sobrecargando el constructor conseguimos dotar a la clase de flexibilidad. Por
ejemplo, como mínimo se debería tener en cuenta que podría no pasársele
parámetros al constructor, cuando éste lo espera, debido a un fallo en alguna otra
parte de la aplicación (me refiero a cualquier otra clase que llame a ésta). Es por ello
que siempre es recomendable definir al menos dos constructores: el específico de la
aplicación que estemos diseñando y el "estándar".
Veamos un ejemplo:
class Caja {
double ancho;
double alto;
double profundidad;
// El siguiente es el constructor específico
Caja(double w, double h, double d) {
ancho = w; alto = h; profundidad = d;
}
// pero podría ser que no le llegarán parámetros
// por fallar la otra clase (método) que lo
// invoque
55
Capítulo 4 – Conceptos de Orientación a Objetos.
Caja () {
ancho = alto = profundidad = -1
//-1 indica volumen no existente
}
// e incluso podemos pensar que se quiere construir
// un cubo, entonces, por qué introducir 3 valores?
Caja (double valor) {
ancho = alto = profundidad = valor;
}
}
double volume() {
return ancho * alto * profundidad;
}
Objetos como parámetros
Además de usar los tipos primitivos como parámetros, en Java es perfectamente
posible pasar a un método, un objeto como parámetro.
class Comparar {
int a, b;
Comparar (int i, int j) {
a = i;
b = j;
}
}
boolean equals (Comparar c) {
if (c.a == a && c.b == b) return true;
else return false;
}
Como se puede apreciar, el método equals() de Comparar comprueba si dos
objetos son idénticos.
Paso de argumentos.
Existen dos formas de pasar un argumento a una rutina:
Por valor: El método copia el valor de un argumento en el parámetro formal de
la rutina.
Por referencia: El método copia el objeto en el parámetro formal de la rutina.
56
Capítulo 4 – Conceptos de Orientación a Objetos.
En el primer caso, al ser solo una copia, cualquier modificación dentro de la rutina
de ese valor no tendrá efecto una vez fuera de éste, mientras que si es por referencia,
sí persistirá la modificación hecha, aún cuando salgamos de la rutina.
Cuando en Java se pasan argumentos de tipo simple, estos siempre se hacen por
valor. Si se desea pasar un parámetro por referencia se debe pasar un objeto. Esto
ocurre porque cuando se crea una variable de un tipo de clase, el programador solo
crea una referencia al objeto. Así, cuando se pasa esta referencia a un método, el
parámetro que recibe éste, se refiere al mismo objeto que el referido por el argumento.
Por lo tanto, los cambios que se hagan en la referencia de la rutina afectarán también
al objeto pasado como argumento.
El operador static
Hay veces que se desea definir una clase miembro para ser usada
independientemente de cualquier objeto de esa clase. Normalmente a una clase
miembro se accede sólo si hemos instanciado un objeto de dicha clase. No obstante,
es posible crear un miembro que pueda ser usado por sí mismo, sin necesidad de
referenciar a una instancia específica. Para crear tales tipos de miembros se emplea el
operador static.
Cuando un miembro se declara con esta palabra reservada, se puede acceder a él
antes de que cualquier objeto de su clase sea creado, y sin referenciar a ningún
objeto. Se puede declarar tanto los métodos como las variables como static. El
ejemplo más claro de un miembro static es el main(). Se declara de esta manera
porque se debe llamar antes de que cualquier objeto sea declarado.
Las variables static
Las variables de instancia declaradas como static (también llamadas "de
clase") son, esencialmente, variables globales. Cuando creamos objetos específicos
de esa clase no se hace ninguna copia de las variables static. Lo que ocurre es
que todas las instancias de esa clase comparten la misma variable static. Estas
variables mayormente tienen sentido cuando varias instancias de la misma clase
necesitan llevar el control o estado del valor de un dato.
Para llamar a este tipo de variables se suele utilizar el nombre de la clase (no es
imprescindible, pues se puede utilizar también el nombre de cualquier objeto), porque
de esta forma queda más claro, seguida de un "." y la variable Estatica.b
Las variables miembro static se crean en el momento en que pueden ser
necesarias:
cuando se va a crear el primer objeto de la clase
cuando se llama a un método static
cuando se utiliza una variable static de dicha clase.
Lo importante es que las variables miembro static se inicializan siempre antes
que cualquier objeto de la clase.
57
Capítulo 4 – Conceptos de Orientación a Objetos.
Los métodos static.
También llamados "de clase", pueden recibir objetos de su clase como argumentos
explícitos, pero no tienen argumento implícito ni pueden utilizar la referencia this. Para
llamar a estos métodos se suele emplear el nombre de la clase, en vez del nombre de
un objeto de la clase.
Los métodos y variables de clase son lo más parecido que Java tiene a las
funciones y variables globales de C/C++.
Las restricciones que tiene un método static son:
Solo pueden llamar otro método static
Solo deben acceder a datos static
No se pueden referir a this o super de ninguna manera
Si se necesita hacer algún tipo de computación para inicializar las variables
static, se puede declarar también un bloque static el cual se
ejecutará solo una vez, cuando se carga.
class Estatica {
static int a = 3;
static int b;
static void show(int x) {
System.out.println("x = " + x);
System.out.println("a = " + a);
System.out.println("b = " + b);
}
static {
System.out.println("Bloque estático
inicializado");
b = a * 4;
// vemos que, aunque declarada como static
// b se inicializa en tiempo de ejecución
}
public static void main(String args[ ]) {
show(15);
}
}
Tan pronto como la clase “Estatica” se carga, todas las sentencias se ejecutan.
Primero, “a” se inicializa a 3, luego se ejecuta el bloque estático y, por último, “b”
se inicializa al valor asignado.
En resumen podemos decir para los miembros de tipo static:
Miembros (métodos o atributos) implementados al nivel de clase
Desde métodos static la referencia this no tiene sentido
No se puede acceder a miembros no estáticos desde métodos estáticos
static: Semántica de "ámbito global"
Permite desarrollo de código sin usar POO
58
Capítulo 4 – Conceptos de Orientación a Objetos.
El operador final
Al declarar una variable como final, se previene que su contenido sea
modificado. No obstante, Java permite separar la definición de la inicialización. Esta
última se puede hacer más tarde, en tiempo de ejecución, llamando a métodos o en
función de otros datos.
La variable final así definida, es constante pero no tiene porqué tener el mismo
valor en todas las ejecuciones del programa, pues depende de cómo haya sido
inicializada. Esto es lo más parecido a las constantes de otros lenguajes de
programación.
final float PI = 3.1416;
//Según la convención de
// código las variables "constantes" se ponen en
// mayúsculas.
Aunque básicamente este operador se emplea para crear constantes en Java,
también podemos definir una clase o un método como final.
Una clase final no puede tener clases derivadas. Esto se hace por motivos de
seguridad y de eficiencia, porque cuando el compilador detecta que los métodos no
van a ser redefinidos puede hacer optimizaciones adicionales.
Un método final no puede ser redefinido por una clase que derive de su propia
clase.
9. Interfases.
Una interfase es un conjunto de declaraciones de métodos (sin definición).
También puede definir constantes, que son implícitamente public, static y
final, y deben siempre inicializarse en la declaración.
Todas las clases que implementan una determinada interfase están obligadas a
proporcionar una definición de los métodos de la interfase, y en ese sentido adquieren
una conducta o modo de funcionamiento.
Una clase puede implementar una o varias interfases. Para indicar que una clase
implementa una o más interfases se ponen los nombres de las interfases, separados
por comas, detrás de la palabra implements, que a su vez va siempre a la derecha
del nombre de la clase o del nombre de la super-clase en el caso de herencia.
public class Clase extends ClasePadre implements
Interfase, Interfase2 {
...
...
}
¿Qué diferencia hay entre una interfase y una clase abstract?
Ambas tienen en común que pueden contener varias declaraciones de métodos (la
clase abstract puede además definirlos). A pesar de esta semejanza, que hace que en
59
Capítulo 4 – Conceptos de Orientación a Objetos.
algunas ocasiones se pueda sustituir una por otra, existen también algunas diferencias
importantes:
Una clase no puede heredar de dos clases abstract, pero sí puede heredar de
una clase abstract e implementar una interfase, o bien implementar dos o más
interfases.
Una clase no puede heredar métodos -definidos- de una interfase, aunque sí
constantes.
Las interfases permiten mucha más flexibilidad para conseguir que dos clases
tengan el mismo comportamiento, independientemente de su situación en la
jerarquía de clases de Java.
Las interfases permiten “publicar” el comportamiento de una clase develando
un mínimo de información.
Las interfases tienen una jerarquía propia, independiente y más flexible que la
de las clases, ya que tienen permitida la herencia múltiple.
De cara al polimorfismo las referencias de un tipo interfase se pueden utilizar
de modo similar a las clases abstract.
Definición de interfases.
Una interfase se define de un modo muy similar a las clases:
public interface NombreInterface {
public void metodo(param, param2);
public void metodo2(param);
}
Cada interfase public debe ser definida en un fichero *.java con el mismo
nombre de la interfase. Los nombres de las interfases suelen comenzar también con
mayúscula. Las interfases no admiten más que los modificadores de acceso public
y package. Si la interfase no es public no será accesible desde fuera del
package.
Los métodos declarados en una interfase son siempre package y abstract, de
modo implícito.
Utilización de interfases
Las constantes definidas en una interfase se pueden utilizar en cualquier clase
(aunque no implemente la interfase) precediéndolas del nombre de la interfase
area = 2.0*NombreInterface.var*r;
60
Capítulo 4 – Conceptos de Orientación a Objetos.
Sin embargo, en las clases que implementan la interfase las constantes se pueden
utilizar directamente, como si fueran constantes de la clase.
A veces se crean interfases para agrupar constantes simbólicas relacionadas.
Desde el punto de vista del polimorfismo, el nombre de una interfase se puede
utilizar como un nuevo tipo de referencia. En este sentido, el nombre de una interfase
puede ser utilizado en lugar del nombre de cualquier clase que la implemente, aunque
su uso estará restringido a los métodos de la interfase. Un objeto de ese tipo puede
también ser utilizado como valor de retorno o como argumento de un método.
Conceptos Útiles
Es importante distinguir entre la referencia a un objeto y el objeto mismo. Los
objetos son los elementos declarados de una clase. Una referencia es una variable
que indica dónde está guardado un objeto en la memoria del ordenador
La llamada a super.
Referencia a la superclase de la que desciende la clase actual
Reutilización de código por medio de herencia
o Super invoca al comportamiento anterior.
o Se puede añadir comportamiento adicional
Implícita en constructores como primera instrucción
En métodos: super.nombre_metodo()
10. Permisos de acceso en java.
Una de las características de la POO es la encapsulación, que consiste
básicamente en ocultar la información que no es pertinente o necesaria para realizar
una determinada tarea. Los permisos de acceso de Java son una de las herramientas
para conseguir esta finalidad.
Accesibilidad de los Packages (paquetes).
El primer tipo de accesibilidad hace referencia a la conexión física de las
computadoras y a los permisos de acceso entre ellas y en sus directorios y ficheros.
En este sentido, un paquete (package) es accesible si sus directorios y ficheros
son accesibles (si están en un ordenador accesible y se tiene permiso de lectura).
Además de la propia conexión física, serán accesibles aquellos paquetes que se
encuentren en la variable CLASSPATH del sistema.
Accesibilidad de Clases o Interfases.
En principio, cualquier clase o interfase de un paquete es accesible para todas las
demás clases del paquete, tanto si es public como si no lo es.
61
Capítulo 4 – Conceptos de Orientación a Objetos.
Una clase public es accesible para cualquier otra clase (siempre que su paquete
sea accesible). Recuérdese que las clases e interfases sólo pueden ser public o
paquete (la opción por defecto cuando no se pone ningún modificador).
Accesibilidad de las Variables y Métodos Miembros de una Clase.
Desde dentro de la propia clase:
Todos los miembros de una clase son directamente accesibles (sin calificar con
ningún nombre o calificando con la referencia this) desde dentro de la propia clase.
Los métodos no necesitan que las variables miembro sean pasadas como argumento.
Los miembros private de una clase sólo son accesibles para la propia clase. Si
el constructor de una clase es private, sólo un método static de la propia clase
puede crear objetos.
Desde una sub-clase.
Las sub-clases heredan los miembros private de su super-clase, pero sólo
pueden acceder a ellos a través de métodos public, protected o package de la
super-clase.
Desde otras clases del paquete (package).
Desde una clase de un package se tiene acceso a todos los miembros que no
sean private de las demás clases del package.
Desde otras clases fuera del paquete (package).
Los métodos y variables son accesibles si la clase es public y el miembro es
public. También son accesibles si la clase que accede es una sub-clase y el
miembro es protected.
Resumen de los permisos de acceso de Java.
Visibilidad
Desde la propia clase
Desde otra clase en el propio
package
Desde otra clase fuera del
package
Desde una sub-clase en el
propio package
Desde una sub-clase fuera del
propio package
public
Si
Si
protected
Si
Si
private
Si
No
package
Si
Si
Sí
No
No
No
Sí
Sí
No
Si
Si
Si
No
No
62
Capítulo 4 – Conceptos de Orientación a Objetos.
11. Transformaciones de tipo: casting.
En muchas ocasiones hay que transformar una variable de un tipo a otro, por
ejemplo de int a double, o de float a long. En otras ocasiones la conversión
debe hacerse entre objetos de clases diferentes, aunque relacionadas mediante la
herencia. Explicaremos ahora estas transformaciones de tipo.
Conversión de tipos primitivos.
Casting Implícito: La conversión entre tipos primitivos es más sencilla. En Java se
realizan de modo automático conversiones implícitas de un tipo a otro de más
precisión, por ejemplo de int a long, de float a double, etc. Estas conversiones
se hacen al mezclar variables de distintos tipos en expresiones matemáticas o al
ejecutar sentencias de asignación en las que el miembro izquierdo tiene un tipo
distinto (más amplio) que el resultado de evaluar el miembro derecho.
Casting Explícito (Operador Cast): Las conversiones de un tipo de mayor a otro
de menor precisión requieren una orden explícita del programador, pues son
conversiones inseguras que pueden dar lugar a errores (por ejemplo, para pasar a
short un número almacenado como int, hay que estar seguro de que puede ser
representado con el número de cifras binarias de short). A estas conversiones
explícitas de tipo se les llama cast.
El cast se hace poniendo el tipo al que se desea transformar entre paréntesis,
como por ejemplo,
long result;
result = (long) (a/(b+c));
12. Sincronización de threads (hilos).
La sincronización nace de la necesidad de evitar que dos o más threads (hilos)
traten de acceder a los mismos recursos al mismo tiempo. Por ejemplo, si un thread
tratara de escribir en un fichero, y otro thread tuviera al mismo tiempo tratando de
borrar dicho fichero, se produciría una situación no deseada.
Otra situación en la que hay que sincronizar threads se produce cuando un
thread debe esperar a que estén preparados los datos que le debe suministrar el
otro thread.
63
Capítulo 5 – Implementación de los conceptos de OO con JAVA
Capítulo 5.
IMPLEMENTACIÓN DE LOS CONCEPTOS DE OO CON JAVA
En este capítulo se propone fijar los conceptos aprendidos, pero a través de
programas de aplicación que utilicen cada uno de ellos.
Implementado el concepto Jerarquía
Los temas a implementar son:
La Jerarquía de Clases
La jerarquía de clases que describen las figuras planas
Consideremos las figuras planas cerradas como el rectángulo y el círculo. Tales
figuras comparten características comunes, tales como, la posición de la figura, de su
centro y el área de la figura, aunque el procedimiento para calcular el área sea
completamente distinto. Podemos entonces, diseñar una jerarquía de clases, tal que la
clase base denominada Figura, tenga las características comunes y cada clase
derivada las específicas. La relación jerárquica se muestra en la figura siguiente:
Figura
Circulo
Rectángulo
La clase Figura es la que contiene las características comunes a dichas figuras
concretas por lo tanto, no tiene forma ni tiene área. Esto lo expresamos declarando
Figura como una clase abstracta, declarando la función miembro area abstract.
Las clases abstractas solamente se pueden usar como clases base para otras
clases. No se pueden crear objetos pertenecientes a una clase abstracta. Sin
embargo, se pueden declarar variables de dichas clases.
En el juego del ajedrez podemos definir una clase base denominada Pieza, con las
características comunes a todas las piezas, como es su posición en el tablero, y
derivar de ella las características específicas de cada pieza particular. Así pues, la
clase Pieza será una clase abstracta con una función abstract denominada mover, y
cada tipo de pieza definirá dicha función de acuerdo a las reglas de su movimiento
sobre el tablero.
65
Capítulo 5 – Implementación de los conceptos de OO con JAVA
La clase Figura
La definición de la clase abstracta Figura, contiene la posición x e y de la figura
particular, de su centro, y la función area, que se va a definir en las clases derivadas
para calcular el área de cada figura en particular.
public abstract class Figura {
protected int x;
protected int y;
public Figura(int x, int y) {
this.x=x;
this.y=y;
}
public abstract double area();
}
La clase Rectángulo
Las clases derivadas heredan los miembros dato x e y de la clase base, y definen
la función area, declarada abstract en la clase base Figura, ya que cada figura
particular tiene una fórmula distinta para calcular su área. Por ejemplo, la clase
derivada Rectangulo, tiene como datos, aparte de su posición (x, y) en el plano,
sus dimensiones, es decir, su ancho y alto.
class Rectangulo extends Figura {
protected double ancho, alto;
public Rectangulo(int x, int y, double ancho,
double alto){
super(x,y);
this.ancho=ancho;
this.alto=alto;
}
public double area(){
return ancho*alto;
}
}
La primera sentencia en el constructor de la clase derivada es una llamada al
constructor de la clase base, para ello se emplea la palabra reservada super. El
constructor de la clase derivada llama al constructor de la clase base y le pasa las
coordenadas del punto x e y. Después inicializa sus miembros dato ancho y alto.
En la definición de la función area, se calcula el área del rectángulo como producto
de la anchura por la altura, y se devuelve el resultado
66
Capítulo 5 – Implementación de los conceptos de OO con JAVA
La clase Círculo
class Circulo extends Figura {
protected double radio;
public Circulo(int x, int y, double radio){
super(x,y);
this.radio=radio;
}
public double area(){
return Math.PI*radio*radio;
}
}
Como vemos, la primera sentencia en el constructor de la clase derivada es una
llamada al constructor de la clase base empleando la palabra reservada super.
Posteriormente, se inicializa el miembro dato radio, de la clase derivada Círculo.
En la definición de la función area, se calcula el área del círculo mediante la
conocida fórmula p*r2, o bien p*r*r. La constante Math.PI es una aproximación
decimal del número irracional pi.
Uso de la jerarquía de clases.
Creamos un objeto c de la clase Círculo situado en el punto (0, 0) y de 5.5
unidades de radio. Calculamos y mostramos el valor de su área.
Circulo c=new Circulo(0, 0, 5.5);
System.out.println("Area del círculo "+c.area());
Creamos un objeto r de la clase Rectangulo situado en el punto (0, 0) y de
dimensiones 5.5 de anchura y 2 unidades de largo. Calculamos y mostramos el valor
de su área.
Rectangulo r=new Rectangulo(0, 0, 5.5, 2.0);
System.out.println("Area del rectángulo "+r.area());
Veamos ahora, una forma alternativa, guardamos el valor devuelto por new al
crear objetos de las clases derivadas en una variable f del tipo Figura (clase base).
67
Capítulo 5 – Implementación de los conceptos de OO con JAVA
Figura f=new Circulo(0, 0, 5.5);
System.out.println("Area del círculo "+f.area());
f=new Rectangulo(0, 0, 5.5, 2.0);
System.out.println("Area del rectángulo
"+f.area());
Enlace dinámico o binding.
En el lenguaje C, los identificadores de la función están asociados siempre a
direcciones físicas antes de la ejecución del programa, esto se conoce como enlace
temprano o estático. Ahora bien, los lenguajes C++ y Java permiten decidir a que
función llamar en tiempo de ejecución, esto se conoce como enlace tardío o dinámico.
Podemos crear un array de la clase base Figura y guardar en sus elementos
los valores devueltos por new al crear objetos de las clases derivadas.
Figura[] fig=new Figura[4];
fig[0]=new Rectangulo(0,0, 5.0, 7.0);
fig[1]=new Circulo(0,0, 5.0);
fig[2]=new Circulo(0, 0, 7.0);
fig[3]=new Rectangulo(0,0, 4.0, 6.0);
¿A qué función area llamará la sentencia: ?
fig[i].area();
La respuesta será, según sea el índice i.
Si i es cero, el primer elemento del array guarda una referencia a un objeto de
la clase Rectangulo, luego llamará a la función miembro area de Rectangulo.
Si i es uno, el segundo elemento del array guarda una referencia un objeto de la
clase Circulo, luego llamará también a la función area de Circulo, y así
sucesivamente.
Podemos introducir el valor del índice i, a través del teclado, o seleccionando un
control en un applet, en el momento en que se ejecuta el programa. Luego, la decisión
sobre qué función area se va a llamar se retrasa hasta el tiempo de ejecución.
Ejemplo de polimorfismo.
Supongamos que deseamos saber la figura que tiene mayor área
independientemente de su forma. Primero, programamos una función que halle el
mayor de varios números reales positivos.
68
Capítulo 5 – Implementación de los conceptos de OO con JAVA
double valorMayor(double[] x){
double mayor=0.0;
for (int i=0; i<x.length; i++)
if(x[i]>mayor){
mayor=x[i];
}
return mayor;
}
Ahora, la llamada a la función valorMayor
double numeros[]={3.2, 3.0, 5.4, 1.2};
System.out.println("El valor mayor es " +
valorMayor(numeros));
La función figuraMayor que compara el área de figuras planas es semejante a
la función valorMayor anteriormente definida, se le pasa el array de objetos de la
clase base Figura. La función devuelve una referencia al objeto cuya área es la
mayor.
static Figura figuraMayor(Figura[] figuras){
Figura mFigura=null;
double areaMayor=0.0;
for(int i=0; i<figuras.length; i++){
if(figuras[i].area()>areaMayor){
areaMayor=figuras[i].area();
mFigura=figuras[i];
}
}
return mFigura;
}
La clave de la definición de la función está en las líneas
if(figuras[i].area()>areaMayor){
areaMayor=figuras[i].area();
mFigura=figuras[i];
}
En la primera línea, se llama a la versión correcta de la función area
dependiendo de la referencia al tipo de objeto que guarda el elemento figuras[i]
del array. En areaMayor se guarda el valor mayor de las áreas calculadas, y en
mFigura, la figura cuya área es la mayor.
La principal ventaja de la definición de esta función estriba en que la función
figuraMayor está definida en términos de la variable figuras de la clase base
69
Capítulo 5 – Implementación de los conceptos de OO con JAVA
Figura, por tanto, trabaja no solamente para una colección de círculos y rectángulos,
sino también para cualquier otra figura derivada de la clase base Figura. Así si se
deriva Triangulo de Figura, y se añade a la jerarquía de clases, la función
figuraMayor podrá manejar objetos de dicha clase, sin modificar para nada el
código de la misma.
Veamos ahora la llamada a la función figuraMayor
Figura[] fig=new Figura[4];
fig[0]=new Rectangulo(0,0, 5.0, 7.0);
fig[1]=new Circulo(0,0, 5.0);
fig[2]=new Circulo(0, 0, 7.0);
fig[3]=new Rectangulo(0,0, 4.0, 6.0);
Figura fMayor=figuraMayor(fig);
System.out.println(“El área mayor es “+fMayor.area());
Pasamos el array fig a la función figuraMayor, el valor que retorna lo
guardamos en fMayor. Para conocer el valor del área, desde fMayor se llamará a
la función miembro area. Se llamará a la versión correcta dependiendo de la
referencia al tipo de objeto que guarde por fMayor. Si fMayor guarda una
referencia a un objeto de la clase Circulo, llamará a la función area definida en
dicha clase. Si fMayor guarda una referencia a un objeto de la clase Rectangulo,
llamará a la función area definida en dicha clase, y así sucesivamente.
La combinación de herencia y enlace dinámico se denomina polimorfismo. El
polimorfismo es, por lo tanto, la técnica que permite pasar un objeto de una clase
derivada a funciones que conocen el objeto solamente por su clase base.
Ampliación de la jerarquía de clases.
Ampliamos el árbol jerárquico de las clases que describen las figuras planas
regulares:
Figura
Triángulo
Rectángulo
Círculo
Cuadrado
70
Capítulo 5 – Implementación de los conceptos de OO con JAVA
La clase Cuadrado es una clase especializada de Rectangulo, ya que un
cuadrado tiene los lados iguales. El constructor solamente precisa de tres argumentos
los cuales corresponden a la posición de la figura y a la longitud del lado.
class Cuadrado extends Rectangulo{
public Cuadrado(int x, int y, double dimension){
super(x, y, dimension, dimension);
}
}
El constructor de la clase derivada llama al constructor de la clase base y le pasa
la posición x e y de la figura, el ancho y alto que tienen el mismo valor. No es
necesario redefinir una nueva función area. La clase Cuadrado hereda la función
area definida en la clase Rectangulo.
La clase Triangulo.
Esta clase tiene como datos, aparte de su posición (x, y) en el plano, la base y la
altura del triángulo.
class Triangulo extends Figura{
protected double base, altura;
public Triangulo(int x, int y, double base,
double altura){
super(x, y);
this.base=base;
this.altura=altura;
}
public double area(){
return base*altura/2;
}
}
El constructor de la clase Triangulo llama al constructor de la clase Figura, le
pasa las coordenadas x e y de su centro, y luego inicializa los miembros dato base y
altura.
En la definición de la función area, se calcula el área del triángulo como producto
de la base por la altura y dividido por dos.
71
Capítulo 5 – Implementación de los conceptos de OO con JAVA
El operador instanceof.
El operador instanceof tiene dos operandos:
un objeto en el lado izquierdo, y,
una clase en el lado derecho.
Esta expresión devuelve true o false dependiendo de que el objeto situado a
la izquierda sea o no una instancia de la clase situada a la derecha o de alguna de sus
clases derivadas.
Rectangulo rect=new Rectangulo(0, 0, 5.0, 2.0);
rect instanceof String
//false
rect instanceof Rectangulo
//true
El objeto rect de la clase Rectangulo no es un objeto de la clase String. El
objeto rect si es un objeto de la clase Rectangulo.
Veamos la relación entre rect y las clases de la jerarquía
rect instanceof Figura
rect instanceof Cuadrado
//true
//false
rect es un objeto de la clase base Figura pero no es un objeto de la clase
derivada Cuadrado
En resumen:
La herencia es la propiedad que permite la creación de nuevas clases a partir de
clases ya existentes.
La clase derivada hereda los datos y las funciones miembro de la clase base, y
puede redefinir algunas de las funciones miembro o definir otras nuevas, para ampliar
la funcionalidad que ha recibido de la clase base. Para crear un objeto de la clase
derivada se llama primero al constructor de la clase base mediante la palabra
reservada super. Luego, se inicializan los miembros dato de dicha clase derivada.
El polimorfismo se implementa por medio de las funciones abstractas, en las
clases derivadas se declara y se define una función que tiene el mismo nombre, el
mismo número de parámetros y del mismo tipo que en la clase base, pero que da
lugar a un comportamiento distinto, específico de los objetos de la clase derivada.
(redefinición de métodos)
72
Capítulo 5 – Implementación de los conceptos de OO con JAVA
Enlace dinámico significa que la decisión sobre la función a llamar se demora
hasta el tiempo de ejecución del programa.
No se pueden crear objetos de una clase abstracta pero sí se pueden declarar
referencias en las que guardamos el valor devuelto por new al crear objetos de las
clases derivadas. Esta peculiaridad nos permite pasar un objeto de una clase derivada
a una función que conoce el objeto solamente por su clase base. De este modo
podemos ampliar la jerarquía de clases sin modificar el código de las funciones que
manipulan los objetos de las clases de la jerarquía.
Clases y Métodos Finales
Clases Finales.
Se puede declarar una clase como final, cuando no nos interesa crear clases
derivadas de dicha clase. La clase Cuadrado se puede declarar como final, ya
que no se espera que ningún programador necesite crear clases derivadas de
Cuadrado.
final class Cuadrado extends Rectangulo{
public Cuadrado(int x, int y, double dimension){
super(x, y, dimension, dimension);
}
}
Uno de los mecanismos que tienen los hackers (piratas) para dañar o para obtener
información privada en los sistemas es la de crear una clase derivada y sustituir dicha
clase por la original. La clase derivada actúa exactamente igual que la original pero
también puede hacer otras cosas, normalmente, dañinas. Para prevenir los posibles
daños, se declara la clase como final, impidiendo a cualquier programador la creación
de clases derivadas de ésta.
Por ejemplo, la clase String que es una de las más importantes en la
programación en lenguaje Java, está declarada como final. El lenguaje Java
garantiza que siempre que se utilice un string, es un objeto de la clase String que
se encuentra en el paquete java.lang.String, y no cualquier otro string.
Métodos finales.
Como sabemos, una de las formas de aprovechar el código existente, es la de
crear una clase derivada y redefinir algunos de los métodos de la clase base.
73
Capítulo 5 – Implementación de los conceptos de OO con JAVA
class Base{
//...
final public void funcionFinal(){
//...
}
public void dibujar(Graphics g){
}
}
class Derivada{
//...
public void dibujar(Graphics g){
// dibujar algunas figuras
}
}
La clase Base define una función miembro pública “dibujar”, que no dibuja
nada en el contexto gráfico g. La clase Derivada redefine la función miembro
dibujar, para dibujar algunas figuras en el contexto grafico g. La función que se
redefine tiene que tener la misma declaración en la clase Base y en la clase
Derivada.
Para evitar que las clases derivadas redefinan una función miembro de una clase
base, se le antepone la palabra clave final. La función miembro funcionFinal
de la clase Base no se puede redefinir en la clase Derivada, pero si se puede
redefinir la función miembro dibujar.
Interfases.
Una interfase es una colección de declaraciones de métodos (sin definirlos) y
también puede incluir constantes.
Runnable es un ejemplo de interfase en el cual se declara, pero no se
implementa, una función miembro run.
public interface Runnable {
public abstract void run();
}
Las clases que implementen (implements) el interfase Runnable han de
definir obligatoriamente la función run.
74
Capítulo 5 – Implementación de los conceptos de OO con JAVA
class Animacion implements Runnable{
//..
public void run(){
// define la función run
}
}
El papel de la interfase es el de describir algunas de las características de una
clase. Por ejemplo, el hecho de que una persona sea un futbolista no define su
personalidad completa, pero hace que tenga ciertas características que las distinguen
de otras.
Clases que no están relacionadas pueden implementar la interfase Runnable,
por ejemplo, una clase que describa una animación, y una clase que realice un cálculo
intensivo.
Diferencias entre una Interfase y una Clase Abstracta.
Una interfase es:
una lista de métodos no implementados,
puede incluir la declaración de constantes.
Una clase abstracta puede incluir:
métodos implementados y no implementados o abstractos,
miembros dato constantes y otros no constantes.
Ahora bien, la diferencia es mucho más profunda. Imaginemos que Runnable
fuese una clase abstracta. Un applet definido por la clase MiApplet que moviese
una figura por su área de trabajo, derivaría a la vez de la clase base Applet (que
describe la funcionalidad mínima de un applet que se ejecuta en un navegador) y de la
clase Runnable.
En el lenguaje Java la clase MiApplet deriva de la clase base Applet e
implementa el interfase Runnable
class MiApplet extends Applet implements Runnable{
//...
// define la función run de la interfase
public void run(){
//...
}
// redefine paint de la clase base Applet
public void paint(Graphics g){
//...
}
// define otras funciones miembro
}
75
Capítulo 5 – Implementación de los conceptos de OO con JAVA
Una clase solamente puede derivar extends de una clase base, pero puede
implementar varias interfases. Los nombres de las interfases se colocan separados
por una coma después de la palabra reservada implements.
El lenguaje Java no fuerza una relación jerárquica, simplemente permite que
clases no relacionadas puedan tener algunas características de su comportamiento
similares.
Las interfases y el Polimorfismo.
En el lenguaje C++, es posible la herencia múltiple, pero este tipo de herencia
presenta dificultades. Por ejemplo, cuando dos clases B y C derivan de una clase base
A, y a su vez una clase D deriva de B y C. Este problema es conocido con el nombre
de diamante.
A
C
B
C
D
En el lenguaje Java solamente existe la herencia simple, pero las clases pueden
implementar interfases. Vamos a que la importancia de las interfases no consiste en
resolver los problemas inherentes a la herencia múltiple sin forzar relaciones
jerárquicas, sino es el de incrementar el polimorfismo del lenguaje más allá del que
proporciona la herencia simple.
Para explicar este aspecto importante y novedoso del lenguaje Java adaptaremos
los ejemplos que aparecen en el artículo del Bill Venners "Designing with interfaces"
publicado en Java World (http://www.javaworld.com/) en Diciembre de 1998.
Comparemos la herencia simple mediante un ejemplo similar al de la jerarquía de
las figuras planas, con las interfases.
Herencia simple.
Ver los programas completos: Animal.java, PoliApp.java
Creamos una clase abstracta denominada Animal de la cual deriva las clases
Gato y Perro. Ambas clases redefinen la función habla declarada abstracta en la
clase base Animal.
76
Capítulo 5 – Implementación de los conceptos de OO con JAVA
public abstract class Animal {
public abstract void habla();
}
class Perro extends Animal{
public void habla(){
System.out.println("¡Guau!");
}
}
class Gato extends Animal{
public void habla(){
System.out.println("¡Miau!");
}
}
El polimorfismo nos permite pasar la referencia a un objeto de la clase Gato a una
función onomatopeya que conoce al objeto por su clase base Animal
public class PoliApp {
public static void main(String[] args) {
Gato gato=new Gato();
onomatopeya(gato);
}
static void onomatopeya(Animal sujeto){
sujeto.habla();
}
}
El compilador no sabe exactamente que objeto se le pasará a la función
onomatopeya en el momento de la ejecución del programa. Si se pasa un objeto de
la clase Gato se imprimirá ¡Miau!, si se pasa un objeto de la clase Perro se
imprimirá ¡Guau!.
El compilador solamente sabe que se le pasará un objeto de alguna clase derivada
de Animal. Por tanto, el compilador no sabe que función habla será llamada en el
momento de la ejecución del programa.
El polimorfismo nos ayuda a hacer el programa más flexible, porque en el futuro
podemos añadir nuevas clases derivadas de Animal, sin que cambie para nada el
método onomatopeya.
Ver programas completos donde se trata el tema interfases: Parlanchin.java,
Animal.java, Reloj.java, PoliApp.java
77
Capítulo 5 – Implementación de los conceptos de OO con JAVA
Vamos a crear una interfase denominada Parlanchin que contenga la
declaración de una función denominada habla.
public interface Parlanchin {
public abstract void habla();
}
Hacemos que la jerarquía de clases que deriva de Animal implemente la
interfase Parlanchin
public abstract class Animal implements Parlanchin{
public abstract void habla();
}
class Perro extends Animal{
public void habla(){
System.out.println("¡Guau!");
}
}
class Gato extends Animal{
public void habla(){
System.out.println("¡Miau!");
}
}
Ahora veamos otra jerarquía de clases completamente distinta, la que deriva de la
clase base Reloj. Una de las clases de dicha jerarquía Cucu implementa la interfase
Parlanchin y por lo tanto, debe de definir obligatoriamente la función habla
declarada en dicha interfase.
public abstract class Reloj {
}
class Cucu extends Reloj implements Parlanchin{
public void habla(){
System.out.println("¡Cucu, cucu, ..!");
}
}
Definamos la función onomatopeya de modo que conozca al objeto que se le
pasa, no por una clase base, sino por la interfase Parlanchin. A dicha función le
podemos pasar cualquier objeto que implemente la interfase Parlanchin, esté o no
en la misma jerarquía de clases.
78
Capítulo 5 – Implementación de los conceptos de OO con JAVA
public class PoliApp {
public static void main(String[] args) {
Gato gato=new Gato();
onomatopeya(gato);
Cucu cucu=new Cucu();
onomatopeya(cucu);
}
static void onomatopeya(Parlanchin sujeto){
sujeto.habla();
}
}
Al ejecutar el programa, veremos que se imprime en la consola ¡Miau!, porque a la
función onomatopeya se le pasa un objeto de la clase Gato, y después ¡Cucu,
cucu, ..!, porque a la función onomatopeya se le pasa un objeto de la clase Cucu.
Si solamente hubiese herencia simple, Cucu tendría que derivar de la clase
Animal (lo que no es lógico) o bien no se podría pasar a la función onomatopeya.
Con interfases, cualquier clase en cualquier familia puede implementar la interfase
Parlanchin, y se podrá pasar un objeto de dicha clase a la función
onomatopeya. Esta es la razón por la cual las interfases proporcionan más
polimorfismo que el que se puede obtener de una simple jerarquía de clases.
Composición.
Hay dos formas de reutilizar el código:
mediante la herencia,
mediante la composición: se utiliza una clase ya creada incluyendo
instancias de la misma en nuevas clases
representa una relación "tiene un".
Es decir, si tenemos una clase Rueda y una clase Coche, es de esperar que la
clase Coche tenga cuatro instancias de Rueda:
class Coche {
Rueda rueda1, rueda2, rueda3, rueda 4;
...
}
La composición significa utilizar objetos dentro de otros objetos. Por ejemplo, un
applet es un objeto que contiene en su interior otros objetos como botones, etiquetas,
etc. Cada uno de los controles está descrito por una clase.
79
Capítulo 5 – Implementación de los conceptos de OO con JAVA
Vamos a estudiar una clase Rectangulo definiendo el origen, no como un par
de coordenadas x e y (números enteros) sino como objetos de una clase denominada
Punto.
Ver Programas completos: Punto.java, Rectángulo.java, RectanguloApp.java
La clase punto
La clase Punto tiene dos miembros dato, la abscisa x y la ordenada y de un
punto del plano. Definimos dos constructores uno por defecto que sitúa el punto en el
origen, y otro constructor explícito que proporciona las coordenadas x e y de un punto
concreto.
public class Punto {
int x;
int y;
// funciones miembro
}
El constructor explícito de la clase Punto podemos escribirlo de dos formas
public Punto(int x1, int y1) {
x = x1;
y = y1;
}
Cuando el nombre de los parámetros es el mismo que el nombre de los miembros
datos escribimos
public Punto(int x, int y) {
this.x = x;
this.y = y;
}
this.x que está a la izquierda y que recibe el dato x que se le pasa al
constructor se refiere al miembro dato, mientras que x que está a la derecha es el
parámetro.
Recordemos que this es una palabra reservada que guarda una referencia al
objeto propio, u objeto actual.
La función miembro desplazar simplemente cambia la posición del punto desde
(x, y) a (x+dx, y+dy). Cuando es llamada, recibe en sus dos parámetros dx y
dy el desplazamiento del punto y actualiza las coordenadas x e y del punto. La
función no retorna ningún valor.
80
Capítulo 5 – Implementación de los conceptos de OO con JAVA
public void desplazar(int dx, int dy){
x += dx;
y += dy;
}
Para crear un objeto de la clase Punto cuyas coordenadas x e y valgan
respectivamente 10 y 23 escribimos
Punto p = new Punto(10, 23);
Para desplazar el punto p 10 unidades hacia la izquierda y 40 hacia abajo,
llamamos desde el objeto p a la función desplazar y le pasamos el desplazamiento
horizontal y vertical.
p.desplazar(-10, 40);
La clase Rectangulo
La clase Rectangulo tiene como miembros dato, el origen, que es un objeto de
la clase Punto y las dimensiones ancho y alto.
public class Rectangulo {
Punto origen;
int ancho;
int alto ;
// funciones miembro
}
El constructor por defecto, crea un rectángulo situado en el punto 0,0 y con
dimensiones nulas
public Rectangulo() {
origen = new Punto(0, 0);
ancho=0;
alto=0;
}
El constructor explícito crea un rectángulo situado en un determinado punto p y
con unas dimensiones que se le pasan en el constructor
81
Capítulo 5 – Implementación de los conceptos de OO con JAVA
public Rectangulo(Punto p, int w, int h) {
origen = p;
ancho = w;
alto = h;
}
Podemos definir otros constructores en términos del constructor explícito usando la
palabra reservada this.
public Rectangulo(Punto p) {
this(p, 0, 0);
}
public Rectangulo(int w, int h) {
this(new Punto(0, 0), w, h);
}
El primero crea un rectángulo de dimensiones nulas situado en el punto p. El
segundo, crea un rectángulo de unas determinadas dimensiones, w y h, situándolo en
el punto 0, 0.
Dentro del cuerpo de cada constructor, se llama al constructor explícito mediante
this pasándole en sus parámetros los valores apropiados.
Para desplazar un rectángulo, trasladamos su origen (esquina superior izquierda) a
otra posición, sin cambiar su anchura o altura. Desde el objeto origen, llamamos a la
función desplazar miembro de la clase Punto
void desplazar(int dx, int dy) {
origen.desplazar(dx, dy);
}
Objetos de la clase Rectángulo.
Para crear un rectángulo rect1 situado en el punto (0, 0) y cuyas dimensiones
son 100 y 200 escribimos
Rectangulo rect1=new Rectangulo(100, 200);
Para crear un rectángulo rect2, situado en el punto de coordenadas 44, 70 y de
dimensiones nulas escribimos:
Punto p=new Punto(44, 70);
Rectangulo rect2=new Rectangulo(p);
82
Capítulo 5 – Implementación de los conceptos de OO con JAVA
O bien, en una sóla línea
Rectangulo rect2=new Rectangulo(new Punto(44, 70));
Para desplazar el rectángulo rect1 desde el punto (100, 200) a otro punto
situado 40 unidades hacia la derecha y 20 hacia abajo, sin modificar sus dimensiones,
escribimos:
rect1.desplazar(40, 20);
Para hallar y mostrar el área del rectángulo rect1 podemos escribir
System.out.println("el área es " +
rect1.calcularArea());
Para hallar el área de un rectángulo de 100 unidades de largo y 50 de alto y
guardar el resultado en la variable entera areaRect, escribimos en una sola línea:
int areaRect=new Rectangulo(100, 50).calcularArea();
Definiendo Clases.
Definición de Clases.
Cuando trabajamos con el flujo de entrada y salida, quizás vimos que hay pasos
muy tediosos. Quizás no se comportan los objetos de estas clases tal y como nosotros
queremos. ¿Tenemos que aceptar un comportamiento de un objeto inadecuado para
nuestros propósitos solo porque otro programador los diseñó así? No, nosotros
podemos definir nuestras propias clases que provean el comportamiento que nosotros
requerimos.
Definición de Clase Simple:
class Saludos {
public Saludos(){
}
public void saludo(){
System.out.println("Hola mundo!");
}
}
Esto define una clase Saludos con un constructor y un solo método saludo que
despliega el mensaje "Hola mundo!". Usando esta definición podemos crear un objeto
de la siguiente forma:
Saludos x;
x = new Saludos();
83
Capítulo 5 – Implementación de los conceptos de OO con JAVA
Podemos mandarle un mensaje de la siguiente forma:
x.saludo();
En general todos los métodos llevan la palabra reservada public al principio. Un
método simple tiene la estructura:
public valor-retorno nombre-metodo (){
// cuerpo del método
}
La palabra reservada void es usada con aquellos métodos que no devolverán
nada.
Definición de Métodos con Parámetros.
Supongamos que queremos diseñar un método saludo mas genérico, en donde
al mandar el mensaje podamos escoger a quien saludar. Esta información adicional, la
tendría que incluir el que llama al método como argumento de la siguiente forma:
x.saludo("alumnos")
Para desplegar "Hola alumnos!".
Para poder implementar esto necesitamos incluir parámetros en el encabezado del
método, para que este tenga acceso a esta información. La nueva clase quedaría
entonces así:
class Saludos {
public Saludos(){
}
public void saludo(){
System.out.println("Hola mundo!");
}
public void saludo(String alguien){
System.out.println("Hola".concat(alguien).concat("!"));
}
}
Al incluir dos métodos con el mismo nombre, pero diferente firma,
public void saludo()
public void saludo(String alguien)
estamos sobrecargando el método saludo. Por lo tanto cuando el usuario quiere
saludar a todo el mundo, hace la llamada:
84
Capítulo 5 – Implementación de los conceptos de OO con JAVA
x.saludo();
pero cuando quiere saludar a alguien en especial, hace la llamada
x.saludo("Colegio");
Como podemos saludar a todo el mundo sin especificarlo explícitamente, se dice
que el saludo al mundo es el saludo default.
Uso de Atributos.
Supongamos que queramos darle la opción al creador del objeto Saludos que
escoja el saludo default, para que no necesariamente sea al "mundo". Esta
información debería de ser proveída en el constructor del objeto. Así, con las
siguientes instrucciones: desplegarían un saludo a la universidad:
Saludos x;
x = new Saludos("Universidad");
x.saludos();
Para poder implementar esto, debemos incluir un constructor que reciba una
cadena de argumento. Es decir, que se debe incluir un constructor con un parámetro
String. Para que tenga el método saludo acceso a la cadena recibida en el
constructor, necesitamos almacenarla en un atributo. Esto lo hacemos porque un
método no puede acceder a las variables o parámetros de los otros métodos.
Los atributos pueden ser accedidos por todos los métodos de dentro de la clase.
La declaración de estos es la misma que las variables, con la diferencia que
comienzan con la palabra reservada private. La nueva clase quedaría así:
class Saludos {
private String quien;
public Saludos(){
quien = "mundo";
}
public Saludos(String s){
quien = s;
}
}
85
public void saludo(){
System.out.println("Hola ".concat(quien).concat("!"));
}
public void saludo(String alguien){
System.out.println("Hola ".concat(alguien).concat("!"));
}
Capítulo 5 – Implementación de los conceptos de OO con JAVA
El orden de declaración entre los atributos y los métodos es irrelevante. Cualquier
orden es legal. Como norma, conviene para la clase, que declararemos todos los
atributos antes de los métodos.
Los miembros (atributos y métodos de la clase) pueden ser públicos, privados o
amigables (default).
Los miembros públicos (public) pueden ser accedidos por todos.
Los miembros amigables (friendly) pueden ser accedidos por miembros del
mismo paquete (conjunto de clases).
Los miembros privados (private) solo pueden ser accedidos por miembros de
la misma clase.
Uso de la Definición de Clase.
El primer paso que tenemos que hacer es escribir nuestra clase Saludos en un
archivo que tiene que llamarse Saludos.java. Como esta clase utiliza el
PrintStream, asegúrese de incluir la línea:
import java.io.*;
Después compilamos este archivo para producir Saludos.class. Una vez que ya
hicimos esto, podemos utilizar nuestra clase en un programa. Por ejemplo, podríamos
tener:
import java.io.*;
class SaludandoUnPoco {
public static void main(String args[]) {
System.out.println("Saludando un poco a todos!");
Saludos s1, s2;
s1 = new Saludos();
s2 = new Saludos("FISICC");
}
}
s1.saludo();
s2.saludo();
s1.saludo("Suger Montano");
Ya el programa SaludandoUnPoco se compila y se corre de la manera usual.
Diseño e Implementación de Clases.
La definición de la clase trabajada anteriormente fue extremadamente casual.
Empezamos con una clase muy simple y le fuimos agregando funcionalidad mientras
86
Capítulo 5 – Implementación de los conceptos de OO con JAVA
lo íbamos necesitando. Dicho enfoque fue útil para explicar la definición de las clases,
pero es poco funcional para escribir clases de alguna significación. Es como construir
una casa, cuarto por cuarto. Necesitamos un enfoque más sistemático. Podemos
seguir los siguientes pasos:
Diseño de la clase:
Decida el comportamiento que la clase deberá proveer. Como el
comportamiento de un objeto es proveído a través de sus métodos,
determine claramente que métodos debe proveer la clase.
Determine la interfase de la clase que se usará. La interfase de la clase es
la forma en que podemos usar un objeto de esta clase. En este paso ya es
necesario determinar los prototipos de los métodos.
Escriba un programa de prueba que utilice la clase. Esta es una forma de
revisar si lo que tenemos hasta ahora tiene sentido.
Escriba un esqueleto de la clase. Este, el esqueleto, es la declaración de la
clase con los prototipos de todos los métodos, pero con el cuerpo de los
métodos vacío.
87
Capítulo 5 – Implementación de los conceptos de OO con JAVA
Implementación de la clase:
Consiste en escribir los cuerpos de los métodos y declarar los atributos mientras se
vayan necesitando. Tenga en mente lo siguiente:
Puede empezar a trabajar en cualquiera de los métodos.
Puede dejar de trabajar en cualquiera de los métodos antes de completarlo
e ir a trabajar en otro método.
Mejora de la Implementación
Por lo general cualquier código escrito, a pesar de que funcione puede ser
mejorado. La interfase se debe mantener intacta.
Primer Ejemplo Completo: Clase para Entrada/Salida Interactiva (InteractiveIO)
Para ilustrar este enfoque de diseño e implementación de clases, vamos a
comenzar resolviendo el problema de entrada y salida interactiva. Queremos diseñar
una clase que provea una manera sencilla de leer y desplegar información.
Diseño de la Clase.
Comportamiento que la clase deberá proveer.
Si tuviéramos una clase InteractiveIO quisiéramos que esta me ofreciera el
siguiente comportamiento:
Escribir a la pantalla, asegurándome que se vaya a desplegar
inmediatamente.
Preguntar al usuario por información, desplegándole un mensaje y leyendo
una cadena del teclado, asegurándome que el mensaje se despliegue
antes de que la computadora espere la entrada.
Interfase y Prototipos de los métodos.
A partir del comportamiento deseado de InteractiveIO, quisiéramos tener la
capacidad de hacer lo siguiente:
Declarar una referencia a la clase InteractiveIO de la siguiente forma:
InteractiveIO interIO;
Esto me dice simplemente que la clase se debe llamar InteractiveIO.
Crear objetos que sean instancias de InteractiveIO, sin hacer referencia a
System.in o System.out de la siguiente forma:
interIO = new InteractiveIO();
88
Capítulo 5 – Implementación de los conceptos de OO con JAVA
De esto podemos deducir que necesitaremos un constructor sin parámetros.
Mandar un mensaje al objeto para escribir a pantalla de la siguiente forma:
interIO.write("Por favor conteste cada pregunta.");
Debemos proveer un método write que reciba una cadena como parámetro, sin
devolver nada.
Mandar un mensaje al objeto para preguntar una cadena, desplegando una frase
descriptiva de lo que queremos leer, de la siguiente forma:
String s;
s = interIO.promptAndRead("Cual es su nombre?");
Debemos proveer un método promptAndRead que reciba una cadena como
parámetro, y que devuelva una cadena.
Aquí ya tenemos una idea clara del prototipo de los métodos que incluirá la clase
InteractiveIO. Estos son:
public InteractiveIO()
public void write(String s)
public String promptAndRead(String s)
Programa de prueba que utilice la clase.
Para usar la clase, el programa de prueba más simple que podemos escribir es
uno que lee una palabra y a continuación la despliega. Si nuestra clase
InteractiveIO funcionara como nosotros quisiéramos este quedaría así:
import java.io.*;
class ProbandoInteractiveIO {
public static void main(String args[]) {
InteractiveIO interIO;
String line;
interIO = new InteractiveIO();
line = interIO.promptAndRead("Ingrese una palabra: ");
interIO.write(line);
}
}
Concluimos que una clase InteractiveIO diseñada tal y como la tenemos, lleva
a cabo bien la tarea. Provee de una entrada y salida interactiva simple,
despreocupándonos de System.in, System.out y flush. Esta clase haría todo
esto por nosotros.
89
Capítulo 5 – Implementación de los conceptos de OO con JAVA
Esqueleto de la clase.
class InteractiveIO {
// aqui irían los atributos, si necesitáramos
public InteractiveIO() {
// enunciados
}
/** despliega s*/
public void write(String s) {
// enunciados
}
}
/** despliega s, lee una cadena del teclado y devuelve
una referencia a esta */
public String promptAndRead(String s) {
// enunciados
}
Implementación de la clase
Completamos los métodos de la clase.
class InteractiveIO {
// aparentemente no necesitamos atributos
public InteractiveIO() {
// parece que necesitamos nada!
}
/** despliega s*/
public void write(String s) {
System.out.print(s);
System.out.flush();
}
/** despliega s, lee una cadena del teclado y devuelve
una referencia a esta */
}
public String promptAndRead(String s) throws Exception {
System.out.print(s);
System.out.flush();
BufferedReader br = new BufferedRead(
new InputSteamReader(System.in));
String line;
line = br.readLine();
return line;
}
90
Capítulo 5 – Implementación de los conceptos de OO con JAVA
La palabra reservada return se utiliza para dos operaciones en Java:
Para retornar un valor. Su sintaxis es: return expresion;
Para forzar la salida de un método. No se ejecutara ninguna instrucción
después del return. Incluso si tengo un método que no retorna ningún
valor, pero deseo salirme del método lo puedo hacer mediante el
enunciado: return;
Mejora de la implementación.
Observemos que podemos optimizar el programa en los siguientes aspectos:
No tiene sentido crear en cada llamada al método write una instancia del
BufferedReader. En vez de esto podemos crearlo una sola vez en el constructor y
almacenar una referencia a este como atributo (para que sea accesible desde el
método write).
Las primeras instrucciones del método promptAndRead son iguales a las del
método write. Podemos ahorrar esta repetición de código llamando al método
write desde el método promptAndRead.
No necesitamos almacenar la referencia al objeto String en el método
promptAndRead. Podemos obviar este paso devolviendo la referencia leída en
composición.
La nueva implementación de la clase InteractiveIO entonces me queda así:
class InteractiveIO {
private BufferedReader br;
public InteractiveIO() throws Exception {
br = new BufferedReader(
new InputStreamReader(System.in));
}
/** despliega s*/
public void write(String s) {
System.out.print(s);
System.out.flush();
}
}
91
/** despliega s, lee una cadena del teclado y devuelve
una referencia a esta */
public String promptAndRead(String s) {
this.write(s);
return keyb.readLine();
}
Capítulo 5 – Implementación de los conceptos de OO con JAVA
Aquí trabajamos con tres tipos de variables:
Parámetros: son creadas cuando el método al cual pertenecen es invocado.
Su valor inicial corresponde a la información enviada como argumento. Son
destruidas cuando termina el método. Solo pueden ser accedidas por el
método al cual pertenecen.
Variables Locales: al igual que los parámetros, éstas son creadas cuando el
método se invoca y destruidas cuando termina el método y sólo pueden ser
accedidas por el método al cual pertenecen. A diferencia de los parámetros
éstas deben ser inicializadas dentro del método.
Atributos: tienen el mismo tiempo de vida que el objeto al cual pertenecen.
Son creadas cuando el objeto es creado y destruidas cuando el objeto es
destruido. Pueden ser accedidas por todos los métodos de la misma clase.
Se acostumbra a declararlas private, ya que no deberían ser accedidas
por métodos de otras clases.
Recordemos que cuando mandamos mensajes (al llamar a los métodos)
necesitamos un receptor de dicho mensaje. En el método promptAndRead cuando
invocamos al mensaje write el receptor de este mensaje debe ser el mismo objeto al
cual promptAndRead pertenece. Usamos la palabra reservada this para hacer
referencia al objeto al cual le pertenece el método. Podemos utilizar también la palabra
reservada this para diferenciar entre variables locales y atributos que tengan el
mismo nombre.
Ejercicios propuestos para el alumno:
Agregue un método writeln a la clase InteractiveIO que reciba
una cadena de caracteres, que la despliegue y que garantice que el
siguiente carácter a desplegarse aparecerá en una nueva línea.
Agregue un segundo método writeln (sobrecargado) en la clase
InteractiveIO que no reciba argumentos, pero garantice que el
siguiente carácter a desplegarse aparecerá en una nueva línea.
Segundo Ejemplo Completo: Clase Nombre.
Diseñamos la clase InteractiveIO motivados por el deseo de un uso más
conveniente del PrintStream y del BufferedReader. Por lo general nuestro
punto de partida no son las clases existentes de Java sino problemas de la vida real
que deseamos resolver modelándolos de alguna forma.
Las clases predefinidas que Java nos provee ni siquiera se acercan a modelar el
comportamiento de los elementos de nuestro mundo. El ejemplo que vamos a trabajar
ahora es modelar el nombre de una persona. Podemos estar tentados a utilizar la
clase String, pero esta no nos provee el comportamiento que nosotros quisiéramos
de una clase Nombre
92
Capítulo 5 – Implementación de los conceptos de OO con JAVA
Diseño de la Clase.
Comportamiento que la clase deberá proveer.
Si tuviera una clase Nombre quisiera que esta me ofreciera el comportamiento
para:
Obtener las iniciales como una cadena.
Obtener el nombre como una cadena; en el orden apellido, primer nombre
Obtener el nombre como una cadena; en el orden primer nombre, apellido
Agregar o reemplazar un titulo.
Interfase y Prototipos de los métodos.
A partir del comportamiento deseado de la clase Nombre, quisiéramos estar en la
capacidad de hacer lo siguiente:
Declarar una referencia a la clase Nombre de la siguiente forma:
Nombre alumno;
Esto me dice simplemente que la clase se debe llamar Nombre.
Crear objetos que sean instancias de Nombre, basados en el primer nombre y el
apellido de la siguiente forma:
alumno = new Nombre("Juan", "Perez");
De esto podemos deducir que necesitaremos un constructor con dos parámetros
String.
Mandar un mensaje al objeto para obtener las iniciales como una cadena, de la
siguiente forma:
String s;
s = alumno.getInitials();
Debemos proveer un método getInitials que devuelva una cadena.
Mandar un mensaje al objeto para obtener el nombre completo, en la forma
apellido, primer nombre; como una cadena, de la siguiente forma:
String s;
s = alumno.getLastFirst();
Debemos proveer un método getLastFirst que devuelva una cadena.
Mandar un mensaje al objeto para obtener el nombre completo, en la forma primer
nombre apellido precedido de un título opcional, como una cadena, de la siguiente
forma:
93
Capítulo 5 – Implementación de los conceptos de OO con JAVA
String s;
s = alumno.getFirstLast();
Debemos proveer un método getFirstLast que devuelva una cadena. Mandar
un mensaje al objeto para agregar o reemplazar el título de la siguiente forma:
alumno.setTitle("Ingeniero");
Debemos proveer un método setTitle que reciba como parámetro una cadena.
Aquí ya tenemos una idea clara del prototipo de los métodos que incluirá la clase
Nombre. Estos son:
public
public
public
public
public
Nombre(String pnom, String ap)
String getInitials()
String getLastFirst()
String getFirstLast()
void setTitle(String newTitle)
Observe que el constructor no incluye al titulo. Esta fue nuestra opción (como
diseñadores de la clase). Nuestro razonamiento fue que: no todos tienen titulo.
En contraste con el nombre y apellido, el titulo no es permanente, por lo que
permitimos que se pudiera agregar un titulo o modificarlo.
Programa de prueba que utilice la clase.
Para usar la clase, el programa de prueba más simple que podemos escribir es
uno que lea el primer nombre y apellido y el título. Después el programa deberá
desplegar:
las iniciales
el nombre completo en la forma apellido, primer nombre
el nombre completo en la forma primer nombre apellido.
Una vez que tenemos funcionando nuestra clase InteractiveIO la utilizaremos
en el programa de prueba. Si nuestra clase Nombre funcionara como nosotros
quisiéramos este quedaría así:
94
Capítulo 5 – Implementación de los conceptos de OO con JAVA
import java.io.*;
class ProbandoNombre {
public static void main(String args[]) {
Nombre n;
String primer, apellido, titulo;
InteractiveIO io;
io = new
primer =
apellido
titulo =
}
}
InteractiveIO();
io.promptAndRead("Ingrese primer nombre: ");
= io.promptAndRead("Ingrese apellido: ");
io.promptAndRead("Ingrese apellido:");
n = new Nombre(primer, apellido);
n.setTitle(titulo);
io.writeln(n.getInitials());
io.writeln(n.getFirstLast());
io.writeln(n.getLastFirst());
Concluimos que la clase Nombre diseñada tal y como la tenemos, lleva a cabo
bien la tarea. Provee una forma simple de manipular los nombres.
Esqueleto de la clase.
class Nombre {
// aquí van los atributos si las necesitáramos
}
public Nombre(String pnom, String ap)
// enunciados
}
public String getInitials() {
// enunciados
}
public String getLastFirst() {
// enunciados
}
public String getFirstLast() {
// enunciados
}
public void setTitle(String newTitle)
// enunciados
}
{
{
Implementación de la clase
Completamos los métodos de la clase. Razonamos que:
Los métodos getFirstLast y getLastFirst necesitan ambos acceso al primer
nombre y al apellido. De la misma forma getFirstLast y setTitle necesitan
95
Capítulo 5 – Implementación de los conceptos de OO con JAVA
ambos acceso al título. Por lo tanto, necesitamos hacer del primer nombre, apellido y
titulo, atributos.
Asumiremos que mientras no se especifique un titulo la persona no lo tiene.
class Nombre {
private String primerNombre, apellido, titulo;
}
public Nombre(String pnom, String ap) {
primerNombre = pnom;
apellido = ap;
titulo = "";
}
public String getInitials() {
String inicNom, inicAp;
inicNom = primerNombre.substring(0,1);
inicAp = apellido.substring(0,1);
return inicNom.concat(".").concat(apellido).concat(".");
}
public String getLastFirst() {
return apellido.concat(", ").concat(primerNombre);
}
public String getFirstLast() {
return titulo.concat(" ").concat(primerNombre).concat(
" ").concat(apellido);
}
public void setTitle(String newTitle) {
titulo = newTitle;
}
El objetivo del constructor es garantizar que el objeto comience con valores válidos
en sus atributos.
Queda la mejora de la clase a discreción del alumno.
Para la clase InteractiveIO, es poco probable que se declare más de un objeto
de esta clase. Sin embargo, para el caso de la clase Nombre, es muy probable que se
declaren muchas instancias de esta clase.
Cada una de estas compartirá los mismos métodos pero tendrán su propio juego
de atributos: primerNombre, apellido y titulo. Esto tiene sentido ya que cada
objeto instancia de Nombre tendrá su propio primer nombre, apellido y titulo.
A pesar de que todas las instancias de la clase Nombre vayan a tener el mismo
comportamiento (proveído por sus métodos), las llamadas devolverán resultados
distintos. Esto es porque los atributos tendrán distintos valores.
Los valores de sus atributos constituyen su estado. Puedo mandarle un mensaje
getLastFirst a dos objetos distintos, y se comportaran igual y pero pueden
devolver distinto resultado ya que sus estados son distintos.
96
Capítulo 5 – Implementación de los conceptos de OO con JAVA
Ejercicios propuestos para el alumno:
Escriba un programa que lea tres pares de primer nombre, apellido y a
continuación despliegue sus iniciales. El programa deberá usar la clase
Nombre.
Agregue un método IlustrateName a la clase Nombre que despliegue el
nombre a pantalla.
Diseñe e implemente una clase NombreLargo, que tome en consideración
(además del primer nombre y apellido como lo hizo la clase Nombre) el
segundo nombre.
Diseñe e implemente una clase Direccion.
Diseñando la Salida para los Objetos
En el diseño original de la clase Nombre, la clase no leía ni desplegaba objetos
Nombre. La entrada y salida era la responsabilidad del usuario de la clase. Algunas
veces es deseable que la clase se haga responsable de la entrada y la salida. Para
ilustrar como se podría implementar esto, le agregaremos este comportamiento a la
clase Nombre, comenzando con la salida.
Podemos diseñar un método print para la clase Nombre que provea el
comportamiento de producir como salida el nombre completo. Hay que considerar dos
aspectos en este punto:
¿En que forma se debe producir el nombre completo?
La salida, ¿hacia donde debería ir?
La primera cuestión es algo de diseño, arbitrariamente podemos escoger producir
el nombre completo en el orden titulo primer-nombre apellido. Podemos de una forma
arbitraria escoger hacia donde debería ir la salida, ya sea al monitor o a algún archivo
en especial. Sin embargo, si nos vamos a tomar la molestia de agregar un método
extra a la clase, podríamos diseñarlo para que sea lo más general posible sin limitarlo
a ninguna salida específica. Podríamos hacer responsabilidad del que invoca el
método, hacia donde debería producirse la salida.
Hasta ahora, hemos manejado la salida a través del print y println ha
instancias del PrintStream. Como diseñadores de la clase Nombre, podríamos
insistir que la salida se maneje a través del PrintStream. Podríamos pedirle al
invocador del método print que incluya como argumento el objeto PrintStream, a
donde desea que se mande la salida.
El método quedaría así:
void print (PrintStream destino)
De esta manera, si el que llama al método print desea desplegar el nombre en la
pantalla, en la llamada, incluiría como argumento System.out. Por otro lado si
97
Capítulo 5 – Implementación de los conceptos de OO con JAVA
desease mandar el nombre a un archivo incluiría como argumento alguna instancia del
FileOutputStream. Un ejemplo de uso podría ser:
Nombre n;
PrintStream p;
n = new Nombre("Jodie","Foster");
n.setTitle("Licenciada");
p = new PrintStream (new FileOutputStream ("actrices.txt"));
n.print(System.out);
// despliega Licenciada Jodie Foster en la pantalla
n.print(p); // escribe Licenciada Jodie Foster en el archivo
La implementación quedaría así:
void print (PrintStream destino){
destino.print(this.getLastFirst());
}
Ejercicio propuesto para el alumno:
Implemente una clase llamada SalidaConBitacora. El constructor recibe un
solo argumento: una cadena que representa el nombre de un archivo. La clase
debe proveer dos métodos: print y println, ambos, de los cuales deben
recibir una cadena de argumento. Estos métodos deben mandar la cadena a
desplegar a pantalla y a escribirla al archivo.
Diseñando la Entrada para los Objetos
Desearíamos tener la capacidad de mandarle un mensaje pidiendo que se cree un
objeto Nombre desde cierta entrada. Nos topamos con un problema: los mensajes los
mandamos a objetos y no tenemos un objeto a quien mandarle el mensaje. De hecho,
el envío del mensaje debería de crear el objeto.
En realidad no deseamos mandarle el mensaje a un objeto instancia de la clase
Nombre, sino a la clase Nombre. Esto en Java se lleva a cabo a través de un tipo de
método conocido como método estático. Estos métodos son definidos de la misma
forma que los otros métodos, excepto que estos llevan la palabra reservada static
antes del tipo de retorno. Los métodos estáticos no están asociados a ningún objeto.
Tienen las siguientes características:
Deben de ser invocados independientemente de cualquier instancia de la
clase. Se utiliza el nombre de la clase como receptor del mensaje.
No pueden accesar ningún atributo.
Al igual que con el método print nos debemos preguntar dos cosas:
o ¿En que forma se debe leer el nombre?
o La entrada, ¿de donde debería provenir?
98
Capítulo 5 – Implementación de los conceptos de OO con JAVA
Requeriremos como diseñadores de la clase, que el primer nombre aparezca en
una línea de por sí solo y el apellido en la siguiente línea. De nuevo, esta decisión es
arbitraria. Además diseñaremos el método de tal forma que la fuente de entrada sea
responsabilidad del que invoque el método. Requeriremos que como argumento se
pase una referencia a un BufferedReader, de donde será leído el nombre.
El prototipo sería así:
public static Nombre read(BufferedReader fuente)
Podemos utilizar la clase Nombre para leer un nombre desde el teclado y desde un
archivo de la siguiente forma:
Nombre n;
FileInputStream f;
BufferedReader brFile, brKeyb;
InputStreamReader isrFile, isrKeyb;
isrKeyb = new InputStreamReader(System.in)
brKeyb = new BufferedReader(isrKeyb);
f = new FileInputStream(new File("nombres.txt"));
isrFile = new InputStreamReader(f);
brFile = new BufferedReader(isrFile);
n = Nombre.read(brKeyb);
n.print(System.out);
n = Nombre.read(brFile);
n.print(System.out);
La implementación quedaría así:
public static Nombre read(BufferedReader fuente){
String primer, apellido;
primer = fuente.readLine();
apellido = fuente.readLine();
return new Nombre(primer, apellido);
}
Anteriormente vimos que, cuando se esta ingresando la información desde el
teclado, es aconsejable desplegar un mensaje para decirle al usuario que información
es la que se desea que ingrese. Podemos incluir otro método de entrada readi que
utilice la clase InteractiveIO que ya hemos diseñado para llevar a cabo esto. La
implementación quedaría así:
public static Nombre readi(Interactive io){
String primer, apellido;
primer = io.promptAndRead("Ingrese primer nombre:");
apellido = io.promptAndRead("Ingrese apellido:");
return new Nombre(primer, apellido);
}
99
Capítulo 5 – Implementación de los conceptos de OO con JAVA
Ejercicio propuesto para el alumno:
Agregue los métodos read, readi y print a las clases que usted
desarrolló anteriormente en ejercicios: NombreLargo y Direccion.
Programa Hello world ! Revisado
Con lo que hemos conocido hasta ahora, podemos explicar un poco más el primer
programa que escribimos:
import java.io.*;
class HelloApp {
public static void main (String args[])
{
System.out.println ("Hello world !");
}
}
Vemos que el programa HelloApp es una clase. Toda clase que existe como
programa necesita un método main. El método main es static, es decir no esta
asociado a ningún objeto HelloApp.
La razón de esto, es que la ejecución de nuestro programa debe de comenzar en
algún lado. El intérprete de Java en algún sentido, invoca el método main. Pero esto lo
hace antes de que cualquier objeto de nuestro código haya sido creado.
Además vemos que esta declarado como public. La razón de esto es que
necesita ser llamado desde afuera de la clase (desde el interprete de Java).
100
Capitulo 6 – Software de Calidad.
Capítulo 6.
CONTROL DE CALIDAD EN LOS SISTEMAS
Introducción
El desarrollo de sistemas hoy en día ha tomado gran importancia en el mundo,
siendo ésta cada vez más creciente.
Me parece muy acertado sugerir, en este punto, y luego de haber analizado en
profundidad un lenguaje de programación, que si una empresa de software pretende
competir a nivel internacional, es necesario que contemple seriamente la necesidad
de realizar una CERTIFICACION DE CALIDAD DEL SOFTWARE Y DEL SISTEMA.
En el entorno actual de la realidad económica de nuestro país, y considerando
que se están gestando polos de DESARROLLO DE SOFTWARE, como el que se
acaba de anunciar en la vecina ciudad de Resistencia, Provincia del Chaco, el que
pretende generar fuentes de trabajo mas allá de las fronteras de la nación, el que ha
tomado como modelo a la Republica de la India, debemos, no solo entonces elegir un
buen lenguaje de programación, sino que también, el mismo debe estar acorde a las
pautas mundiales de CALIDAD.
A nivel país, el Gobierno de la Ciudad de Buenos Aires, esta impulsando de
manera coherente desde el año 2003, el programa “PRO ISO 9000”. Este desafío
impulsado por el Gobierno porteño, ya ha generado sus primeros frutos en el área
informática, siendo la empresa MSA Magic Software Argentina SA, la primera en el
rubro en Certificar sus procesos de desarrollo de software, el 21 de abril del 2004.
Es claro que si un sistema presenta errores al momento de ser utilizado, ese
producto pierde confiabilidad a los ojos del usuario hasta el nivel que podría ser
desechado como un producto defectuoso.
Muchos proyectos de sistemas presentan fallas que impiden que el sistema
funcione como era de esperarse o que sea utilizado en su totalidad. Por ello, es
necesario definir e impulsar líneas de acción tendientes a mejorar el sistema
producido.
Dentro de estas líneas de acción, está la relacionada con el proceso mismo del
desarrollo del sistema, y como necesidad primordial, la de realizar una investigación
que permita conocer de primera mano el estado en que se encuentra su proceso de
desarrollo.
Diferenciaremos dos conceptos importantes como son: Calidad en los Sistemas
y Calidad en el Software.
El control de calidad de sistemas está orientado a garantizar el
funcionamiento de los sistemas en explotación.
101
Capitulo 6 – Software de Calidad.
El control de calidad de software está orientado a garantizar las
características de los programas que se están desarrollando.
La Ingeniería de Software concierne a teorías, métodos y herramientas para el
desarrollo profesional de productos.
Un Sistema concierne a un conjunto de componentes interrelacionados
trabajando conjuntamente para un fin común. El sistema puede incluir software,
dispositivos mecánicos y eléctricos, hardware, y ser operado por gente.
Existen varias organizaciones de estandarización internacional que realizan
estándares y modelos de ingeniería de software, esto con el fin de mejorar la Calidad
del Software.
La Calidad
La ISO (International Standard Organization), define La Calidad como la
ausencia de deficiencias:
"Es la totalidad de aspectos y características de un producto o
servicio que se refieren a su capacidad para satisfacer necesidades
dadas en la adecuación de sus objetivos'”.
El Instituto de Ingeniería de Software (SEI) en su modelo CMM (Capability
Maturity Model) define la calidad como:
• El grado en el cual un sistema, componente o proceso cumple con los
requisitos especificados.
• El grado en el cual el sistema, componente o proceso cumple con las
expectativas del cliente o usuario.
La calidad consiste en todos aquellos aspectos del producto que satisfacen las
necesidades del cliente y de ese modo proporcionan la satisfacción del producto.
La calidad es sinónimo de eficiencia, flexibilidad, corrección, confiabilidad,
mantenibilidad, portabilidad, usabilidad, seguridad e integridad.
Calidad del Software
Definición
La Calidad del Software es el conjunto de cualidades que lo caracterizan y que
determinan su utilidad y existencia.
La Calidad del Software es mensurable y varía de un sistema a otro o de un
programa a otro. Por ejemplo: un software elaborado para el control de naves
espaciales debe ser confiable al nivel de "cero fallas"; un software hecho para
ejecutarse una sola vez no requiere el mismo nivel de calidad; mientras que un
producto de software para ser explotado durante un largo período (10 años o más)
necesita ser confiable, mantenible y flexible para disminuir los costos de
mantenimiento y perfeccionamiento durante el tiempo de explotación.
102
Capitulo 6 – Software de Calidad.
Aspectos de la Calidad
La Calidad del Software puede medirse después de elaborado el producto.
Pero esto puede resultar muy costoso si se detectan problemas derivados de
imperfecciones en el diseño, por lo que es imprescindible tener en cuenta tanto la
obtención de la calidad como su control durante todas las etapas del ciclo de vida del
software.
Objetivo de la Calidad en los Sistemas
El Objetivo que persigue la Calidad en los Sistemas está orientada a:
•
•
•
•
•
•
•
Incrementar la productividad y satisfacción al trabajo de los profesionales
afines al campo de la computación.
Mejorar la calidad del producto del software.
Proveer técnicas aplicadas para automatizar el manejo de datos.
Realizar una planeación eficaz de los sistemas.
Documentar.
Validar y controlar formalmente la calidad del trabajo realizado.
Cumplir con los objetivos de la organización en cuanto a productividad de sus
sistemas de cómputo.
Obtención de un Software de Calidad
La Obtención de un Software con Calidad implica la utilización de metodologías
o procedimientos estándares para el análisis, diseño, programación y prueba del
software, que permitan uniformar la filosofía de trabajo, en aras de lograr una mayor
confiabilidad, mantenibilidad y facilidad de prueba, a la vez que eleven la
productividad, tanto para la labor de desarrollo como para el control de la Calidad del
Software.
El principio tecnológico define las técnicas a utilizar en el proceso de
desarrollo del software.
El principio administrativo contempla las funciones de planificación y control
del desarrollo del software, así como la organización del ambiente o centro de
ingeniería de software.
El principio ergonómico define la interfaz entre el usuario y el ambiente
automatizado. Para el aseguramiento de la calidad es necesario su control o
evaluación.
Control de la Calidad
El Control de la Calidad es realizar una observación constante acerca del
cumplimiento de las tareas que pueden ofrecer una calidad objetiva a la forma en
como se está desarrollando un proyecto de Ingeniería de Software. Es decir, una
vigilancia permanente a todo el proceso de desarrollo y ciclo de vida del software. Esta
meta puede alcanzarse mediante frecuentes inspecciones a las metodologías de
103
Capitulo 6 – Software de Calidad.
trabajo y uso de herramientas, revisiones de prototipos y evaluación exhaustiva de los
productos finales.
El Control de la Calidad permite realizar las rectificaciones pertinentes al
desarrollo en cuanto éste empieza a desviarse de sus objetivos, por lo tanto, de la
calidad del trabajo. Estas rectificaciones son posibles gracias a una retroalimentación
de las etapas superiores, creado así un aprendizaje al observar las salidas de cada
etapa, hasta el producto final, y mejorar los procesos que dan origen al sistema.
En el Control de Calidad deben tenerse presentes los costos que esta
involucra. Si se piensa en las tareas que deben realizarse en este control, puede
observase que es necesario llevar a cabo tareas de búsqueda de problemas,
evaluación, realimentación, rectificación, elaboración, modificación y estudio de la
documentación; entre otras actividades.
Pero debe existir un compromiso, ya que un excesivo costo en el control de la
calidad puede hacer que este proceso se torne ineficiente. Pero, por otra parte, el
mejoramiento de la calidad implica reducir los costos ya que se tendría un cierto nivel
de calidad ya asegurado.
Finalmente, y como consecuencia de la naturaleza del proceso de desarrollo de
productos software, el asegurar la calidad en las primeras etapas de este involucra
que los costos del control en las etapas posteriores tenderá a disminuir al tener menos
aspectos que controlar pues, nuevamente, la calidad estaría asegurada en sus bases.
Control de Calidad del Software
Para controlar la Calidad del Software es necesario, definir los parámetros,
indicadores o criterios de medición.
El software posee determinados índices mensurables que son las bases para la
calidad, el control y el perfeccionamiento de la productividad.
Una vez seleccionados los índices de calidad, debe establecerse el proceso de
control, que requiere los siguientes pasos:
1. Definir el software que va a ser controlado: clasificación por tipo,
esfera de aplicación, complejidad, etc., de acuerdo con los estándares
establecidos para el desarrollo del software.
2. Seleccionar una medida que pueda ser aplicada al objeto de
control. Para cada clase de software es necesario definir los
indicadores y sus magnitudes.
3. Crear o determinar los métodos de valoración de los indicadores:
métodos manuales, como cuestionarios o encuestas estándares para la
medición de criterios periciales y herramientas automatizadas para
medir los criterios de cálculo.
4. Definir las regulaciones organizativas para realizar el control:
quiénes participan en el control de la calidad, cuándo se realiza, qué
documentos deben ser revisados y elaborados, etc.
104
Capitulo 6 – Software de Calidad.
Indicadores para diferenciar los productos de calidad de los que carecen de
ella:
•
•
•
El acercamiento a cero defectos.
El cumplimiento de los requisitos intrínsecos y expresos.
La satisfacción del cliente
Sobre todo la satisfacción del cliente.
La Calidad del Software debe ser una disciplina más dentro de la Ingeniería del
software. El principal instrumento para garantizar la calidad de las aplicaciones sigue
siendo el Plan de Calidad. El plan se basa en unas normas o estándares genéricos y
en unos procedimientos particulares.
Las normas, directivas, modelos y estándares son básicamente las siguientes:
•
Familia de normas ISO 9000 y en especial, la ISO 9001 y la ISO 9000-3.2:
1996 Quality Management and Quality Assurance Standards
•
ISO 8402: 1994
•
IEEE 730/1984, Standard for Software Quality Assurance Plans
•
IEEE Std 1028: 1989, IEEE Standard for Software Reviews and Audits
•
CMM. Capability Maturity Model
•
ISO/IEC JTC1 15504. SPICE. Software Process Improvement and Capability
Determination.
•
Modelo de EFQM. Modelo de la Fundación Europea de Gestión de Calidad
Los procedimientos pueden variar en cada organización, pero lo importante es
que estén escritos, personalizados, adaptados a los procesos de la organización y, lo
que es más importante, que se cumplan.
La Calidad del Software debe implementarse a lo largo de todo el ciclo de vida,
debe correr paralela desde la planificación del producto hasta la fase de producción
del mismo.
Capacidades del Software
Las capacidades importantes para un producto de software desde el punto de
vista del usuario, así como los factores que determinan la calidad de cada una de las
capacidades son:
•
•
•
105
Capacidad de operación con los factores de corrección, facilidad, eficiencia,
integridad y facilidad de uso.
Capacidad para ser modificado o de revisión con los factores de flexibilidad,
facilidad de prueba y facilidad de mantenimiento.
Capacidad de transición o de adaptación a otros entornos con los factores de
transportabilidad, capacidad de reutilización y de interoperación.
Capitulo 6 – Software de Calidad.
Calidad Total en el Proceso de Desarrollo del Sistema
Para alcanzar la "Calidad Total", es necesaria la satisfacción por parte de los
elementos que intervienen en el proceso:
•
•
•
La satisfacción de la alta dirección
La satisfacción del personal involucrado en el desarrollo del sistema
La satisfacción del usuario final
La aplicación del control de calidad de sistemas no es solamente al sistema en sí,
ésta conforma la última parte de la evaluación.
Elementos para el Proceso de Sistemas
Son tres los elementos que integran el proceso de sistemas, los cuales por su
importancia deben de considerarse para el mejor control de calidad y realización de los
sistemas, estos son:
1. Gente
2. Herramientas (Software y Hardware)
3. Tiempo disponible
Debe contarse con la gente adecuada, que tenga la suficiente capacidad para
realizar el trabajo, las herramientas de trabajo deben ser confiables, no limitadas y
debe tomarse en cuenta cuanto tiempo se dispone para la elaboración del sistema.
Componentes para la Calidad Total
•
•
•
•
•
•
•
•
•
•
Claridad
Involucración
Planeamiento
Estándares
Entrenamiento
Experiencia
Controles
Documentación
Soporte
Finalización
Claridad
La definición de lo que se tiene que hacer o lo que el usuario necesita, debe ser
clara para todos los responsables del proyecto, esto con el fin de establecer reglas a
seguir.
Involucración
Es necesario revisar cada etapa del proyecto. Para cada una de ellas es
importante dejar en claro si vale la pena continuar o no, si hay limitaciones o
restricciones que afecten o impidan el buen funcionamiento del proyecto y si se está
106
Capitulo 6 – Software de Calidad.
dando el cubrimiento adecuado a todos los requerimientos y funciones, divisiones o
departamentos que están involucrados en la realización del proyecto.
Planeamiento
En la planificación intervienen tanto los usuarios como el personal que desarrolle el
proyecto. Debe planearse el grado de integración que se requiere en las diferentes
áreas de la organización, para interpretar necesidades o requerimientos satisfactorios
con el objeto de llegar a acuerdos en caso de imprevistos en asuntos tan simples
como el método mediante el cual vamos a poner a trabajar alguna etapa o actividad en
el proyecto.
Estándares
Tiene que tomarse en cuenta la forma mediante la cual vamos a trabajar desde el
punto de vista tecnológico, ya que es necesario tener presente la definición de
diversos factores que afectarían la realización del proyecto, como son:
•
•
•
•
•
•
•
•
•
Lenguaje de programación
Manejo de librerías
Código
Instrucciones
Comentarios
Administración de backups
Administración de archivos
Periodicidad de revisiones
Documentación
Debe quedar clara su definición, los elementos que los deben integrar, así como su
estandarización. Sin una estandarización el proyecto se vendría abajo.
Entrenamiento
El entrenamiento es un factor determinante para la realización de un proyecto, ya
que mediante él se obtienen los conocimientos y habilidades que se aplicarán en el
proyecto.
Experiencia
El contar con mucha o poca experiencia, determina el tiempo de desarrollo del
sistema así como la calidad del trabajo que realice (oportunidad - calidad).
Controles
Los controles que se establezcan deben realizarse con alguna periodicidad.
Primeramente debe verse el avance del proyecto; el cumplimiento de los
requerimientos del cliente; el seguimiento y las normas de seguridad y de auditoria; la
involucración de las personas clave para el proyecto; el funcionamiento de la
aplicación; del desarrollo con el usuario y un punto importante es la mutua satisfacción
entre de la gente que realiza el proyecto con el usuario.
107
Capitulo 6 – Software de Calidad.
En este caso puede contarse con un Calendario de Entrega donde contenga los
puntos anteriormente mencionados, así como contar con Bitácoras que respalden las
reuniones que se tengan con los usuarios y los acuerdos a los que hayan llegado
ambas partes.
Documentación
Es importante que la documentación que se genere debe ser clara y útil en
cuanto al sistema, ya sean los programas, las tareas del usuario y sus procedimientos,
el manejo de la aplicación y producción y los cuidados con respecto a back-ups,
ayudas y soporte requerido, la bitácora de historia respecto a fallas, mejoras, arreglos,
etc., contribuyen a una mejor utilización del sistema, es decir, a una mayor calidad en
su operación. Manuales de usuario, técnico y de operación.
Soporte
Es indispensable tener claro quién nos puede apoyar en las áreas técnica, de
análisis, en el área usuaria, ya sea en la instalación, en el área ejecutiva o en algún
otro aspecto, ya que las aplicaciones en los proyectos de sistemas enfrentan siempre
necesidades críticas de soporte.
Finalización
La finalización del proyecto de un sistema es una de las labores más importantes
en el desarrollo del mismo, de igual manera que en cada etapa. En la finalización del
proyecto es necesario considerar cinco puntos vitales:
•
•
•
•
•
La revisión de todos los pasos realizados y de las etapas incluidas en el
proceso total;
Elaboración del proceso en forma integral;
Calidad hecha en cada uno de los pasos o etapas del proyecto;
La atención a los requerimientos del usuario en términos de hacer todo
lo que él quiera, o más;
Y, por supuesto, la satisfacción de nuestro usuario o cliente que, en
últimas, es el reconocimiento a la labor bien realizada y de alta calidad.
Cuando se desarrolle un sistema o aplicación y se instale, debe asegurarse de
hacerlo de tal manera que lo que se entregue esté completo, sea oportuno, no tenga
errores, sea confiable, útil y estable.
Administración de la Calidad
Calidad en el Nivel de Organización
La Administración de la Calidad cuenta con dos niveles de trabajo:
El nivel de entidad u organización
Donde se trata de crear y gestionar una infraestructura que fomente la calidad
de los productos software mediante la adecuación y mejora de las actividades y
108
Capitulo 6 – Software de Calidad.
procesos involucrados en su producción e, incluso, en su comercialización y en la
interacción con los clientes.
El nivel del proyecto
Donde las guías que la infraestructura organizativa prevé para las distintas
actividades y mantenimiento del software deben ser adaptadas a las características
concretas del proyecto y de su entorno para ser aplicadas a la práctica.
Dentro del primer nivel de acción, la gestión de la calidad en organizaciones de
software ha seguido dos líneas que pueden ser complementarias entre sí:
Por una parte, se ha seguido la línea marcada por las entidades internacionales
de estandarización para todas las organizaciones de producción o servicios.
Principalmente se han impuesto en la práctica las directrices marcadas por ISO
(Organization for International Standardization) a través de unas normas ISO 9000
para la gestión de calidad. En el caso del software es principalmente aplicable la
norma ISO 9001.
El sector de software difiere por la naturaleza del producto tanto del resto de
sectores productivos que ha sido necesario crear una guía específica para su
aplicación a este sector: ISO 9000-3.
El mundo del software ha creado sus propias líneas de trabajo en la gestión de
la calidad, trabaja sobre los procesos de producción como medio para asegurar la
calidad del producto software. Por ejemplo, el SEI (Software Engineering Institute),
proponiendo un modelo de clasificación y mejora de los procesos empleados por las
organizaciones de software denominado CMM (Ver anexo).
Calidad en el Nivel de Proyecto
La Calidad del Software se diseña conjuntamente con el sistema, nunca al final.
A mayor calidad, mayores son los costos, pero mayores también los beneficios
obtenidos en la fase del mantenimiento del software. Este costo hay que considerarlo
dentro de todo el ciclo de vida del proyecto.
El aseguramiento de la Calidad de Software engloba un enfoque de gestión de
calidad, tecnología de ingeniería de software efectiva (métricas y herramientas),
revisiones técnicas formales que se aplican durante el proceso del software, una
estrategia de prueba multiescalada, el control de la documentación del software y de
los cambios realizados, un procedimiento que asegure un ajuste a los estándares de
desarrollo del software (cuando sea posible) y mecanismos de medición y de
generación de informes.
La idea de la necesidad de realizar pruebas en fases tempranas, conduce a
realizar un mecanismo básico que permite las Revisiones Técnicas Formales.
Una Revisión Técnica Formal (RTF) es una actividad que garantiza la Calidad
del Software y que es llevada a cabo por los profesionales de la ingeniería de
software. Los objetivos de la RTF son:
1. Descubrir errores en la función, la lógica o la implementación de cualquier
representación del software;
109
Capitulo 6 – Software de Calidad.
2. Verificar que el software bajo revisión alcance sus requisitos;
3. Garantizar que el software haya sido representado de acuerdo con ciertos
estándares predefinidos;
4. Conseguir un software desarrollado de forma uniforme, y
5. Hacer que los proyectos sean más manejables.
La Prueba del Software
Se pueden realizar inspecciones para cada módulo o pequeño grupo de módulos
que conformen un sistema. Al limitar el centro de atención de la RTF la probabilidad de
descubrir errores es mayor.
En la figura se puede apreciar lo que sería el flujo de información necesario para
realizar correcta y completamente una prueba de software, la cual puede ser aplicada
tanto a los módulos individuales como al software en su totalidad.
Calidad por Etapas
Calidad en el Diseño
Aquí se plantean características definidas para la realización del producto
software que deberán cumplirse posteriormente. La calidad se basa en definir un
listado de especificaciones a seguir. Involucra descripción de los procesos, tareas y
responsabilidades de los equipos de desarrollo.
110
Capitulo 6 – Software de Calidad.
En esta etapa la calidad aumenta en la medida en que se realiza una alta
especificación de los procesos y se propone una estrecha tolerancia a la modificación,
estableciendo los métodos correctivos a las desviaciones ocurridas.
Esta es la medida de la calidad apreciada por los usuarios finales del
entendimiento del producto software.
Estas apreciaciones de calidad hacia un determinado producto elevarán el nivel
de confianza para la organización desarrolladora, lo que puede elevar su posición en
el mercado.
Métricas del Software
Las métricas son escalas de unidades sobre las cuales puede medirse un
atributo cuantificable. Cuando se habla de software nos referimos a la disciplina de
recoger y analizar datos basándonos en mediciones reales de software, así como a las
escalas de medición.
Clasificación de Métricas
Las métricas de software se pueden clasificar como:
Métricas orientadas a la función y Métricas orientadas al tamaño.
También se pueden clasificar según la información que entregan:
1. Métricas de productividad, las que se centran en el rendimiento del proceso de
ingeniería de software.
2. Métricas de calidad, proporcionan una indicación de cómo se ajusta el software
a los requisitos explícitos e implícitos del cliente.
3. Métricas técnicas, que se centran más en el software que en el proceso a
través del cuál se ha desarrollado (por ejemplo grado de modularidad o grado
de complejidad lógica).
Estándares y Modelos de Calidad en la Ingeniería de Software
Introducción
La estandarización es toda actividad documentada que norma el
comportamiento de un grupo de personas. Los estándares nos dan los medios para
que todos los procesos se realicen siempre de la misma forma, mientras nos surjan
ideas para mejorarlos. Son nuestra guía para la productividad y la calidad.
Expectativas de los estándares:
•
•
•
•
111
Mejora de procesos de software acorde a los objetivos estratégicos
Mejora de los productos
Protección del cliente o usuario
Protección de la organización (cultura de la organización y mejora continua)
Capitulo 6 – Software de Calidad.
Existen varias organizaciones de estandarización internacional, algunas son
regionales mientras que otras son globales. Las últimas están relacionadas con la
ONU o son independientes, como por ejemplo la International Telecommunication
Union (ITU).
La International Electrotechnical Commission (IEC) que fue fundada en el año 1906
para definir estándares en eléctrica y electrónica, mientras que la International
Organization for Standardization (ISO) fue creada en 1947 para abarcar otros temas.
Ambas tienen por objetivo facilitar el intercambio de bienes y servicios a nivel
internacional, entre otras.
En 1987, ISO e IEC decidieron formar el Joint Technical Commit (JTC), cuyo
objetivo es elaborar estándares para la tecnología de información Information
Technology (IT).
Organizaciones como la ISO, BOOTSTRAP, entre otras se han dedicado a crear
modelos para mejorar la Calidad del Software, entre ellos tenemos:
•
•
•
•
•
•
ISO 9000-3
Tick IT (Inglaterra)
CMM (Estados Unidos)
Bootstrap (Europa)
Trillium (Canadá)
ISO/SPICE (Australia)
La Norma ISO 9000
La Organización Internacional para la Estandarización (ISO) fue fundada el 23 de
febrero de 1947 con el objetivo de crear una norma internacional de calidad. El Comité
Técnico ISO/TC 176 para Aseguramiento de la Calidad fue el encargado de crear el
estándar ISO 9000.
El objetivo del estándar es desarrollar un código mínimo que contenga prácticas de
administración para garantizar el Aseguramiento y Administración de la Calidad, es
decir, qué hacer para responder a los requerimientos de un mercado cada vez más
competitivo y cómo deben responder los proveedores y compradores respecto a la
calidad de los bienes o servicios intercambiados. Para ello establece una serie de
guías para la selección y uso del estándar deseado, así como aclara conceptos en
cuanto a la calidad y las interrelaciones que se establecen.
Estructura General
De forma general la norma se divide en 4 guías o modelos fundamentales:
•
•
•
ISO 9001 Sistemas de Calidad. Modelo de Aseguramiento de la Calidad en el
diseño, desarrollo, producción, instalación y servicio.
ISO 9002 Sistemas de Calidad. Modelo de Aseguramiento de la Calidad para la
producción, instalación y servicio.
ISO 9003 Sistemas de Calidad. Modelo de Aseguramiento de la Calidad para la
inspección final y pruebas.
112
Capitulo 6 – Software de Calidad.
•
ISO 9004 Elementos para la Administración y el Sistema de Calidad. Guía para
el Sistema de Aseguramiento de la Calidad.
Además, existen otras normas y entre ellas las más relevantes son:
•
•
•
ISO 9000-1 Guía para la selección de la norma a usar.
ISO 8402 Recopilación de definiciones. Vocabulario.
ISO 9000-3 Estándares de Administración y Aseguramiento de la Calidad. Guía
para la aplicación de ISO 9001 al desarrollo, suministro y mantenimiento del
software.
De forma general los requerimientos fundamentales de ISO 9000 son:
•
•
•
•
•
•
•
•
•
Escribir un manual de calidad, describiendo el Sistema de Calidad en alto nivel.
Escribir documentos en forma de procedimientos que describan cómo debe
hacerse el trabajo en la organización.
Crear un sistema para controlar la distribución y reedición de documentos.
Diseño e implantación de un sistema de acciones preventivas y correctivas
para prevenir la ocurrencia de problemas.
Identificar las necesidades en cuanto a entrenamiento en la organización.
Determinar las medidas y equipos para realizar las pruebas.
Capacitar al personal de la organización en la operación del Sistema de
Calidad.
Planificar y llevar a cabo auditorias de calidad internas.
Tener en cuenta los requerimientos del estándar con los que no cumple la
organización.
Los factores que determinan el modelo a elegir son:
1. La complejidad del proceso de diseño. Se refiere a la dificultad para diseñar el
producto o servicio cuando éste no ha sido diseñado.
2. La madurez del diseño. Se proyecta hacia el conocimiento y aprobación del
diseño total, ya sea por las pruebas de desempeño o por la experiencia en el
campo.
3. La complejidad del proceso de producción. Está relacionado con la capacidad
del proceso de producción, las necesidades de desarrollo del nuevo proceso,
las variaciones que se requieren y el impacto en el desempeño del producto o
servicio.
4. Las características del producto o servicio. Depende de la complejidad del
producto o servicio, del número de características interrelacionadas y de su
influencia en el desempeño.
1. La seguridad del producto o servicio. Relacionado con el riesgo de ocurrencia
de fallas y el impacto de éstas.
2. Económico. Se refiere al incremento de los costos, para el suministrador o
comprador, que puede provocar desacuerdos en cuanto al producto o servicio.
113
Capitulo 6 – Software de Calidad.
El Modelo Tick IT
El Departamento de Comercio e Industria del Reino Unido (DTI: Department of
Trade and Industry) creó el esquema Tick IT.
Los objetivos primordiales de éste fueron, además de desarrollar un sistema de
certificación aceptable en el mercado, estimular a los desarrolladores de software a
implementar sistemas de calidad, dando la dirección y guías necesarias para tal
efecto.
Aunque el proyecto original estuvo a cargo del DTI1, la responsabilidad actual
por el esquema Tick IT se pasó a DISC, que es una oficina dependiente de British
Standards Institution (BSI) Standards Division, siendo esta última la única autoridad en
el Reino Unido para publicar estándares.
Ciclo de Vida del Software
Un sistema de calidad típico Tick IT deberá contener los elementos que se
enlistan a continuación:
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
Elaboración de propuestas y revisión de contratos asegurando que todos los
requerimientos estén bien especificados y de que la organización tiene la
capacidad para cumplirlos.
Análisis y especificación de los requerimientos del sistema asegurando que
sean revisados y acordados con el cliente.
Planeación, control y monitoreo del avance del desarrollo respecto al plan
comunicando a todas las partes afectadas y que avise oportunamente de
problemas potenciales.
Planeación de la calidad del proyecto, especificando las inspecciones,
revisiones y pruebas requeridas durante el desarrollo.
Inspecciones de los productos contra estándares y requerimientos aplicables y
las acciones correctivas correspondientes.
Diseño de primer nivel identificando los componentes principales y los
requerimientos que satisfacen.
Diseño detallado de todos los componentes e interfaces, construcción, y
prueba de los mismos verificando que satisfagan la especificación.
Integración, pruebas e inspecciones del sistema, demostrando que el sistema
integrado funciona correctamente y satisface su especificación.
Identificar, segregar, investigar y corregir productos no conformes.
Auditorias, pruebas e inspecciones de aceptación del sistema demostrando al
cliente que el sistema satisface los requerimientos.
Almacenamiento, replicación, envío e instalación, asegurando la integridad y
seguridad de los productos, así como el cumplimiento de los compromisos
adquiridos con el cliente.
Puesta en marcha y liberación del producto para disponibilidad del cliente.
Entrenamiento a usuarios en el uso del sistema de tal manera que pueda
operarlo y beneficiarse completamente del mismo con la mínima intervención
del proveedor.
Mantenimiento o sustitución del sistema, asegurando que se continúa
operando en conformidad con los requerimientos del cliente o usuario.
Soporte a clientes de acuerdo a lo especificado en el contrato.
114
Capitulo 6 – Software de Calidad.
Soporte y Aseguramiento de Calidad
•
•
•
•
•
•
•
•
•
•
•
•
Establecer políticas y objetivos de calidad generales de la organización que
sirvan para alinearla en todas sus actividades, procedimientos y políticas
específicas.
Implantar y mantener un sistema de aseguramiento de calidad.
Auditorias, revisiones y acciones correctivas al sistema de calidad que
aseguren que el sistema cumple con los requerimientos, es utilizado y que es
efectivo en el logro de resultados.
Definir, recolectar y analizar datos de calidad para evaluar la efectividad del
sistema de calidad e identificar mejoras potenciales.
Administración de la organización y los proyectos de tal forma que facilite los
resultados de calidad.
Administración de la configuración que identifique y controle, de manera
continua, las partes constituyentes y sus versiones, de cada instancia de un
sistema o subsistema.
Respaldos, seguridad y almacenamiento que protejan contra cualquier pérdida
o corrupción.
Sistema de control de registros y documentación para todas las actividades de
aseguramiento de calidad, de los proyectos y de soporte, incluyendo
procedimientos y registros.
Especificación y control del proceso de desarrollo incluyendo técnicas,
prácticas, convenciones, estándares, mediciones y estadísticas.
Proceso de compras, incluyendo identificación, selección, adquisición y
aceptación que asegure que los bienes y servicios adquiridos sean como se
requiere y de calidad aceptable.
Control de productos incluidos, equipo y herramientas utilizadas: Hardware o
Software, adquiridos o suministrados por el cliente, incluyendo utilización,
configuración, seguridad.
Entrenamiento, reclutamiento y desarrollo de personal que asegure su
competencia y motivación, y disminuya su rotación.
El Modelo CMM
A principios de los años 80’s el Departamento de Defensa de los Estados Unidos
enfocó sus tareas a la revisión de los problemas del software y a su mejoramiento.
Para contribuir a este programa se creó el Instituto de Ingeniería de Software (SEI)
a finales de 1984. Como parte de su trabajo, el Instituto se dio a la tarea de desarrollar
el Modelo de Madurez del Proceso de Software y para 1986 se comenzó el
Proyecto de Evaluación de la Capacidad del Software. Después de varios años de
realizar cuestionarios, evaluaciones, consulta e investigación, junto a otras
organizaciones, en 1991 SEI produce el Modelo de Capacidad y Madurez del
Software.
El Modelo de Madurez y Capacidad del Proceso de Software (CMM) ayuda a que
las organizaciones para producir de manera consistente y predecible productos de
calidad superior. La capacidad del proceso es la habilidad inherente para producir los
resultados planeados. El principal objetivo de un proceso de software maduro es el de
producir productos de calidad que cumplan los requerimientos del usuario.
115
Capitulo 6 – Software de Calidad.
Cuando se habla de madurez se entiende como el crecimiento alcanzado en la
capacidad del proceso de software y que se considera como una actividad a largo
plazo.
En una organización de software inmadura, el proceso de software es
generalmente improvisado, no existen planes rigurosos, se enfocan en resolver las
crisis que se le presentan, carecen de bases objetivas para enjuiciar la calidad de los
productos o para resolver los problemas.
Por lo contrario cuando la organización alcanza cierto grado de madurez posee
una gran habilidad para administrar el proceso de desarrollo y mantenimiento del
software, se hacen pruebas y análisis de costo-beneficio para mejorar el proceso, el
administrador monitorea la calidad del producto y la satisfacción del cliente, se llevan
registros y todos los integrantes están involucrados.
La madurez del proceso de software esta dada cuando un proceso en específico
es explícitamente definido, administrado, medido, controlado y es efectivo.
El ciclo Shewhart propone las bases para el trabajo de mejoramiento del proceso.
Este consta de 4 pasos que se repiten en forma de ciclo hasta que la implantación
produce los resultados esperados y los cambios pasan a ser permanentes.
Los pasos son:
1. Planear
a. Definir el problema
b. Establecer los objetivos a mejorar
2. Ejecutar
a. Identificar las posibles causas de problemas
b. Establecer las bases
c. Probar los cambios
3. Revisar
a. Recolectar los datos
b. Evaluar los datos
4. Actuar
a. Implementar los cambios
b. Determinar la efectividad
CMM es un modelo descriptivo en el sentido que describe los atributos
esenciales que se espera caractericen una organización dentro de un nivel de
madurez en particular. Es un modelo normativo ya que las prácticas detalladas
caracterizan el tipo normal de comportamiento que se espera de una organización que
realiza proyectos a gran escala.
No es prescriptivo ya que no dice a la organización como mejorar.
Estructura del modelo.
CMM: Marco de Trabajo Capability Maturity Model – SEI
116
Capitulo 6 – Software de Calidad.
El modelo consta de 5 niveles, diseñados de forma que los inferiores proveen
unos fuertes cimientos incrementados de manera progresiva sobre los que se
construyen los niveles superiores.
Estas 5 etapas de desarrollo son referidas como niveles de madurez y en cada
una la organización alcanza una capacidad en el proceso superior.
Los 5 niveles del modelo son:
1. Inicial: el proceso de software es un proceso improvisado y caótico. Pocos
procesos están definidos y el éxito que se pueda obtener depende de las
habilidades, conocimientos y motivaciones del personal. No existen calendarios
ni estimados de costos y las funcionalidades y calidad del producto son
impredecibles. No existe un ambiente estable para el desarrollo y
mantenimiento del software. El proceso del software es impredecible por el
continuo cambio o modificación a medida que avanza el trabajo.
2. Repetible: se establecen procedimientos de administración del proceso básico
para determinar costos, calendarios y funcionalidades. Se establecen las
políticas para la administración del proceso y los procedimientos de
implantación. El proceso se basa en repetir éxitos anteriores en proyectos de
similares características, por lo que los mayores riesgos se presentan cuando
se enfrentan a nuevos proyectos. Se exhiben problemas de calidad y carecen
de una adecuada estructura para mejorarla.
3. Definido: el proceso de software para las actividades administrativas y
técnicas está documentado, homogeneizado e integrado en un proceso de
software estándar dentro de la organización, que ayudará a obtener un
desempeño más efectivo. El grupo que trabaja en el proceso enfoca y guía sus
esfuerzos al mejoramiento de su desarrollo, facilita la introducción de técnicas y
métodos e informa a la administración del estado del proceso. La capacidad del
proceso está basada en una amplia comprensión común dentro de la
organización de las actividades, roles y responsabilidades definidas en el
desarrollo de software.
4. Administrativo: se recolectan medidas detalladas del proceso de software y
de la calidad del producto. Ambos son cuantitativamente entendidos y
controlados. El ciclo de Shewhart es constantemente utilizado para planear,
implementar y registrar las mejoras al proceso. Este nivel de capacidad permite
a la organización predecir las tendencias en la calidad del producto dentro de
los límites establecidos y tomar las acciones necesarias en caso que sean
excedidos. Los productos de dicha categoría son predeciblemente de alta
calidad.
5. Optimización: el mejoramiento continuo
retroalimentación cuantitativa y desde las
innovadoras. La organización tiene los
débiles y conocer como fortalecerlos. Su
causas de defectos y su prevención.
117
del proceso es garantizado por la
pruebas de técnicas y herramientas
medios para identificar los puntos
actividad clave es el análisis de las
Capitulo 6 – Software de Calidad.
El Modelo BOOTSTRAP
El Instituto Bootstrap es una organización no lucrativa dedicada a la mejora
continua del modelo de calidad de software llamado BOOTSTRAP, también tiene
como propósito ayudar a la industria europea del software para mejorar su
competitividad.
Bootstrap es un método para analizar, rediseñar y mejorar los procesos de negocio
del desarrollo de software.
Este se compone de: un modelo, un proceso de evaluación, una base de datos de
soporte, un proceso de mejora y los instrumentos de evaluación.
Su enfoque es evaluar el proceso, no el producto. Para eso se definen un conjunto
de características para los procesos, provee un análisis cuantitativo, produce vistas
analíticas, hace evidente fortalezas y debilidades, identifica áreas de mejora, provee
recomendaciones y sugiere un plan de implementación.
El modelo define el paradigma Organización-Metodología- Tecnología que se
usa en Bootstrap para los niveles de evaluación y agrupación de resultados.
El modelo Bootstrap se basa en evaluar las unidades de producción de
software de la organización, a través de sus proyectos para hacer un cambio a toda la
organización. Dentro de este proceso, hay cuatro etapas principales: preparación,
ejecución de la evaluación, determinación del nivel de madurez y capacidades, y la
presentación de resultados de la evaluación.
En la etapa de preparación se realizan las siguientes tareas:
1. un entrenamiento inicial para tener claros los objetivos
2. se seleccionan los proyectos a ser evaluados para obtener la mejor
cobertura de la UPS
3. se define el personal de evaluación para minimizar la subjetividad
4. se define el personal a ser evaluado para obtener la mejor cobertura de
los roles involucrados en los proyectos seleccionados y
5. se hace el acuerdo de confidencialidad.
En la etapa de ejecución, las tareas son:
1. una breve reunión de apertura, para obtener un enfoque colaborativo
con el personal a ser entrevistado;
2. el llenado de los cuestionarios con características generales de la UPS;
3. el llenado de los cuestionarios del proyecto elegido, incluyendo la
evaluación de cómo el proceso de producción es aplicado;
4. revisión preliminar de la evaluación, y
5. reunión final, con el enfoque de presentar los resultados de la
evaluación y obtener el consenso para poder pasar a la fase de
mejoras.
118
Capitulo 6 – Software de Calidad.
En la etapa de determinar el nivel de madurez y capacidades, es donde se califica
cada pregunta con uno de 5 valores posibles: nulo, débil, regular, extenso o no aplica.
Para cada atributo clave se obtiene un nivel de madurez, aplicando un algoritmo
numérico, dando como resultado uno de estos niveles: 1-inicial, 2-repetible, 3-definido,
4-administrado o 5-optimizado. Estos niveles de madurez están subdivididos en
cuatro, de forma que se obtenga una calificación más exacta. Los procesos de
organización y metodología se califican de 1 a 5, mientras que el de tecnología se
califica sólo con dos niveles A o B.
Como resultado de la evaluación, la organización recibe 2 reportes, uno con los
resultados de la evaluación de la UPS y otro con los resultados del proyecto evaluado.
El correspondiente a la UPS contiene información como: un resumen ejecutivo, los
objetivos de la UPS, los puntos débiles y fuertes, un plan de acción recomendado, etc.
El reporte del proyecto contiene: comentarios del proyecto actual detallando lo
referente a la organización, metodología y tecnología, los niveles de madurez para el
proyecto, el plan de acción recomendado, etc.
Uso de las Bases de Datos de Soporte
Una de las características principales de Bootstrap es la base de datos con que
cuenta para hacer análisis. Con esto se fundamenta el plan de mejoras, se pueden
medir las adaptaciones a la metodología, se puede comparar contra la industria y se
pueden establecer objetivos basándose en la competencia.
Proceso de Mejora
Otra parte importante de la metodología de Bootstrap, es el plan de mejora que
sugiere. El proceso para obtener el plan de mejora es,
Primero evaluar las necesidades de la organización tomando en cuenta las
mejoras deseadas e indicadores sobre calidad del producto y servicio, tiempo de
desarrollo, costos y riesgos del producto y del proyecto.
Enseguida hacer una revisión y análisis de resultados de la evaluación,
tomando en cuenta las fortalezas y debilidades detectadas.
Después definir las capacidades a mejorar, considerando un período entre 18 y
24 meses.
Enseguida, definir las prioridades de acuerdo a un análisis de impactos.
Finalmente sobre la base de las actividades definidas, modificar la
organización y responsabilidades para iniciar el cambio, estableciendo un marco de
tiempos para su desarrollo y evaluación.
119
Capitulo 6 – Software de Calidad.
El Modelo ISO/SPICE
Software Process Improvement and Capability Determination
La Organización Internacional para la Estandarización (ISO) creó el grupo de
trabajo WG 10 y le encomendó el desarrollo del estándar internacional de Valuación
de Procesos de Software.
El grupo de trabajo (Working Group) WG 10, empezó a trabajar en enero de
1993 bajo la dirección de Alec Dorling y Peter Simms, y decidieron crear el proyecto
SPICE Software Process Improvement and Capability Determination; la E de SPICE
correspondía, originalmente, a "evaluation" y fue cambiado porque en algunos idiomas
se traducía equivocadamente.
Una de las características sobresalientes de este proyecto de estandarización
es que incluyó un periodo de pruebas del estándar de 2 años. Es decir, antes de ser
publicado como estándar se había estado ajustando por la práctica.
Arquitectura del Modelo
El modelo es tridimensional: la primera dimensión es funcional (procesos), la
segunda de capacidades (niveles), y la tercera de adecuación o efectividad
(calificaciones).
La dimensión PROCESO:
Está organizada jerárquicamente de la siguiente manera: CATEGORÍA DE
PROCESOS, que agrupan procesos comunes; PROCESOS, que logran propósitos
técnicos; PRÁCTICAS BÁSICAS, operaciones que conforman un proceso.
Las categorías definidas son: Cliente-Proveedor, Ingeniería, Proyecto,
Organización y Soporte.
La dimensión CAPACIDAD:
Está organizada ordinalmente en niveles de capacidad y se han definido cinco
niveles. En la versión 1.0 estos niveles son: Desempeño informal, Planeación y
seguimiento bien definido, Cuantitativamente controlado, Mejora continua. Está en
etapa de instrumentación una versión 2.0.
La tercera dimensión es la CALIFICACIÓN:
El juicio mismo: ¿qué calificación le doy a este proceso en este atributo de
capacidad?. Las escalas que se manejan son discretas de tipo: 0 = No adecuado, 1 =
Parcialmente adecuado, 2 = Muy Adecuado, 3 = Totalmente Adecuado.
Los Elementos de Evaluación
Marco de Valor
El Modelo ISO/SPICE tiene un marco de valor explícito.
120
Capitulo 6 – Software de Calidad.
En la dimensión funcional o de proceso: las "mejores prácticas".
En la dimensión de capacidad: los atributos de proceso o prácticas genéricas
que incrementarán la capacidad del proceso.
Este es uno de los componentes más valiosos del estándar internacional.
Evidencia
La evidencia para la evaluación serán los productos producidos por las
prácticas base. Éste es un enfoque de efectividad… "por sus frutos los conoceréis…".
Cada producto tipo ha sido catalogado y sus características de calidad definidas.
Este es otro elemento filosófico fundamental de ISO/SPICE: no es un modelo
nominalista.
Recurrencia
Por último el elemento recurrencia, para fundamentar un juicio o evaluación
vendrá dada por la selección de instancias de proyectos o productos representativas, a
juicio del Evaluador, de las capacidades reales del proceso de software.
Conceptualmente el modelo ISO/SPICE es un modelo inductivo en su parte
funcional: de característica a producto, de producto a práctica, y de práctica a proceso.
En su parte de capacidades es un modelo evolutivo. Es, en general, un modelo
realista: va a ver los productos, es decir, la efectividad de los procesos no lo que está
escrito en algún manual de calidad o de procesos.
Estándares y modelos de evaluación y mejora de los procesos software.
121
Capitulo 6 – Software de Calidad.
122
Capitulo 6 – Software de Calidad.
CONCLUSIONES
Es casi imposible resumir y condensar en pocas páginas la gran cantidad de
información disponible sobre el tema. A modo de ejemplo de esos volúmenes, sobre el
tema “Programación Orientada a Objetos” en Internet quiero destacar:
Buscador
Google
Yahoo
Altavista
Proveedor de libros
Amazon.com
Sun Microsystems
Sitios encontrados
En castellano
En cualquier idioma
110.000
8.560.000
93.700
5.640.000
94.200
--volúmenes encontrados
En cualquier idioma
3.484
32
Como puede verse existe mucha información sobre el tema. La misma
simbología ya es una gran nebulosa de la Tecnología de Objetos (OT), pero al menos
se expondrán algunas consideraciones prácticas extraídas de la aplicación con los
alumnos en la cátedra Programación IV, desde el año 2000 de lenguajes, técnicas y
métodos orientados-a-objetos.
La elección del Lenguaje de Programación
Es opinión antigua, firme y consensuada que no existe la solución ideal al elegir
un lenguaje de programación, pero también se acepta de forma unánime que la
Orientación a Objetos proporciona ventajas evidentes en el desarrollo genérico de
software.
Naturalmente lo más fácil es pensar que los "nuevos" métodos que la OO
propugna van a facilitar el trabajo diario "real" en empresas y departamentos de
desarrollo de software. Los atractivos de los "objetos" aparecen innegables y, las
siguientes etapas personales se cubren de forma inexorable:
•
•
•
•
Novicio (1-3 meses): se mezcla el código eminentemente funcional
modificándolo y añadiéndole porciones de objetos.
Aprendiz (3-6 meses): aquí se produce el "despertar" del que hablan los
textos de Zen, se ve el verdadero significado de la orientación-a-objetos y se
empiezan a bosquejar diseños pertinentes.
Postulante (6-18 meses): los conceptos ya se aplican con soltura en el
desarrollo, pero todavía se dan algunos problemas con la modelización.
Experto (gurú): todo son objetos, y el sujeto se pregunta: ¿cómo pude pensar
antes de otra manera? Este estadio no siempre se alcanza, naturalmente
(afortunadamente, según algunos).
Y para fundamentar, aun más la gran profusión de métodos con bases teóricas,
veamos algunos de los actuales métodos de OOA/OOD (Análisis Orientado a
Objetos/Diseño Orientado a Objetos):
123
Capitulo 6 – Software de Calidad.
METODOS
Object Oriented Design
Object Behaviour Analysis
Methodology for Object Oriented
Software Engineering of Systems
General Object Oriented Design
Object
Oriented
Software
Engineering
Visual Modeling Technique
Texel
Object Modeling Technique
Better Object Notation
Object Oriented System Analysis
Object Oriented Structured Design
Systems Engineering OO
Syntropy
Object Oriented Jackson Structured
Design
Hierarchical Object Oriented Design
Object Oriented Analysis
Object Oriented Design
Object Oriented System Analysis
Colbert
Frame Object Analysis
Semantic
Object
Modelling
Approach
Berard
ADM3
Ptech
Object Oriented Rôle Analysis,
Synthesis and Structuring
Fusion
Desfray
Responsability Driven Design
OOD
OBA
MOSES
AUTORES
Grady Booch
Rubin & Goldberg
Henderson-Sellers & Edwards
GOOD
OOSE
Seidewitz & Stark
Ivar Jacobson
IBM
IBM
Texel
Rumbaugh y otros
Nerson
Shlaer & Mellor
Wasserman et al.
LBMS
Cook y otros
Jackson
OMT
BOM
OOSA
OOSD
SEOO
OOJSD
HOOD
OOA
OOD
OSA
FOA
SOMA
ESA
Coad & Yourdon
Coad & Yourdon
Embley y otros
E. Colbert
Andleigh/Gretzingr
Ian Graham
OOA&D
OORASS
Berard
Donald Firesmith
Martin & Odell
Reenskaug et al.
CRC
Coleman y otros
Softeam
Wirfs-Brock et al.
La realidad es que, es muy difícil diferenciar si uno es mejor que el otro. Cada
autor tiene sus flaquezas y fortalezas.
La experiencia y la revisión real de la gestión de proyectos orientados-a-objetos
en distintas empresas muestra que el éxito siempre ha ido acompañado de una visión
particularizada de OOA/OOD acorde con la política de la empresa, las peculiaridades
del equipo de desarrollo, la experiencia en determinados lenguajes de programación y
la naturaleza concreta de cada proyecto, dentro de unos márgenes más o menos
estables de actuación.
En el ámbito académico, quiero destacar, luego de 4 años de dictar en la
cátedra Programación IV el Paradigma de Objetos, las siguientes conclusiones:
Los alumnos se entusiasman con el concepto, pero tienen un pequeño grado
de dificultad al iniciar el diseño de los objetos, definitivamente, les cuesta un poco
pensar de manera abstracta. Creo que como todo en su nivel inicial, luego su práctica
124
Capitulo 6 – Software de Calidad.
profesional los pulirá con un mejor diseño. Por ahora en la cátedra pregonamos el
“enseñar la técnica”.
Conclusiones y Perspectivas a Futuro
En la etapa de investigación bibliográfica, en la que he utilizado intensamente la
fuente inagotable de Internet, he podido comprobar que hay muchísimas páginas que
explican muy someramente cada concepto de POO y luego muestran un ejemplo. En
este trabajo he tratado de revertir ese problema, brindando abundante material teórico,
como así también ejemplos pertinentes en cada caso.
Los ejemplos dados, en general, son similares a los que pueden encontrarse en la
literatura específica, esto es así porque son los más sencillos para la comprensión de
los conceptos.
Este material permitirá que los alumnos interesados en profundizar la temática de
Programación Orientada a Objeto, tengan una fuente alternativa completa, clara y
precisa sobre los principales tópicos de la POO.
Así también, el código provisto les permitirá visualizar rápida y fácilmente la
implementación de esos conceptos.
Perspectivas a futuro
Cómo actividades complementarias y, en cierta manera, como una
continuación de este trabajo, se propone la instrumentación del concepto
“SOFTWARE DE CALIDAD” ya sea en esta cátedra o como una cátedra optativa, para
afianzar contundentemente la necesidad de desarrollar software competitivo para su
venta a nivel internacional.
A nivel de asignatura, se esta desarrollando un sitio Web en el que se pondrá
disposición de los alumnos, lo siguiente:
El material de apoyo utilizado para el presente trabajo.
Direcciones de sitios de descarga de manuales y tutoriales sobre la temática
específica.
Un foro de discusión sobre el tema POO, destinado a alumnos y docentes.
El material de los trabajos prácticos, con test, resultados de los trabajos
prácticos, pizarra de novedades,
Otros trabajos relacionados con el tema.
Con esto se pretende darle mayor difusión a la temática, promover la
participación de los alumnos y los docentes del área y generar la sinergia
necesaria para lograr una mejor formación de nuestros recursos humanos, que
garanticen una exitosa inserción laboral, como así también, la seguridad y
conocimientos necesarios para que nuestros futuros egresados se atrevan a sus
propios emprendimientos, en este incipiente, exigente y apasionante mundo del
desarrollo del software.
Formosa, 14/05/2005
125
Glosario.
GLOSARIO
Abstracción por especificación: Disciplina de programación que busca que el
programador defina qué es lo que hace cada uno de los módulos que
componen un programa o sistema, aún antes de realizar la implementación.
Abstracción por parametrización: Uso de argumentos en procedimientos y
rutinas.
Abstracción: Proceso por medio del que se elimina información para lograr tratar
cosas diferentes como si fueran iguales. De esta forma se separan las
características de un ente en dos grupos, el de las características que
importa y el de las que sobran, para tomar en cuenta únicamente aquellas
relevantes a la solución de un problema.
Abstracción (2): ignorar aquellos aspectos que no sean relevantes al propósito
actual para concentrarse más profundamente en aquellos que lo son.
Asociación y mensajes: los objetos de un sistema se relacionan y se comunican
entre sí.
Asociación: Relación de uso en general.
Atributos: Datos que caracterizan las instancias de una clase.
Calificación: Limita la multiplicidad de las asociaciones.
Clases: Abstracción de objetos con propiedades comunes.
Composición: Relaciones todo/parte.
Concatenación: Unir elementos.
Constructores y Destructores: Facilidad de un lenguaje de programación que le
permite al programador definir uno o varios procedimientos especiales que
se encargan de inicializar y destruir las variables de un tipo, y que además
son invocadas automáticamente por el compilador. En general, la misión
principal de los destructores es devolver la memoria dinámica asociada a
un objeto.
Encapsulación: la interfase de cada componente del programa se define de forma
que revele tan poco como sea posible de sus particularidades interiores.
Encapsulamiento: Facilidad de un lenguaje de programación que permite definir
un tipo de datos junto a sus operaciones, con el fin de obtener un tipo
abstracto de datos.
Enlace dinámico: es la propiedad que poseen los objetos para enviar mensajes a
otros objetos sin necesidad de conocer (quizá por imposibilidad de hacerlo)
la clase a la que pertenecen, se resolverá en tiempo de ejecución.
Enlaces: Instancias de una relación. Relaciona instancias
Especificación: Proceso por medio del que se definen las características de un
ente.
Generalización. Relaciones padre/hijo.
Herencia (Extensión de tipos): Facilidad del lenguaje de programación que
permite extender un tipo de datos, agregando al final nuevos campos. El
efecto de la herencia puede simularse en la mayoría de los lenguajes
tradicionales, pero el resultado es un programa menos elegante y en
muchos casos mucho más difícil de programar.
Herencia: expresa la similitud entre clases de objetos, mostrando la
especialización de los estados y los comportamientos de las clases de
objetos de nuestro sistema en forma jerárquica.
Instancias: Cada uno de los objetos individuales.
Iteradores: Abstracción que permite obtener todos los elementos contenidos en un
tipo abstracto de datos contenedor. Los contenedores más usuales son los
arreglos, las listas, los conjuntos, los árboles y en menor grado los grafos.
127
Glosario.
Mensaje: Nombre del método con que se invoca a una operación sobre un objeto.
Los términos método y operación son sinónimos, pero uno se usa en el
contexto de Programación por Objetos y el otro en el de Abstracción de
Datos.
Método: Operación que es definida junto a un tipo de datos en aquellos lenguajes
que soportan encapsulamiento.
Módulos: Partes que componen un sistema, que generalmente se construyen de
forma que sean independientes unas de otra. En general se busca que al
hacer cambios en un módulo no sea necesario hacerlo en otros. En estos
casos, se dice que los módulos tienen una cohesión baja.
Multiplicidad: Número de instancias que intervienen en la relación.
Ocultación de Datos: Facilidad de un lenguaje de programación que permite
evitar que un programador que usa un tipo abstracto de datos pueda
manipular la estructura interna de una instancia. De esta manera se evita
que el programador usuario del tipo abstracto de datos introduzca
inconsistencias en la estructura de datos.
Operaciones: Funciones que pueden realizar las instancias.
Polimorfismo (Funciones Virtuales): Un lenguaje de programación que soporta
polimorfismo permite diferir la definición de la operación que debe aplicarse
un objeto al momento en que esa operación es necesaria. De esta manera,
el programador no necesita conocer el objeto sobre el que opera. Cuando
se usa herencia para extender de muchas formas un tipo de datos, resulta
luego conveniente que el procedimiento que se use para operar sobre un
dato dependa del dato en sí, aunque el programador no haya especificado
exactamente cuál es ese procedimiento.
Polimorfismo: es la propiedad de dos o más clases para responder al mismo
mensaje, cada una de ellas según su especificación.
Procedimiento o rutina: Facilidad sintáctica de los lenguajes de programación
que le permiten al programador aislar la solución de un problema particular
en un módulo. Mediante el uso de procedimientos es posible aplicar la
abstracción por parametrización y por especificación.
Programación Estructurada: Conjunto de prácticas y disciplinas de programación
que se basan en el uso de Abstracción por parametrización y por
especificación para construir sistemas. Además, un lenguaje de
programación soporta la Programación Estructurada si cuenta con las
construcciones sintácticas IF-THEN-ELSE, WHILE-REPEAT, CASE,
procedimientos y argumentos. Un programa estructurado nunca hace uso
del GO TO, y generalmente se descompone modularmente como una
jerarquía de procedimientos, usando la descomposición de Arriba hacia
Abajo [Top-Down].
Programación Orientada a los Objetos [OOP] ó [POO]: Uso de unas técnicas de
Abstracción de Datos en un lenguaje que también soporta
Encapsulamiento, Herencia y Polimorfismo.
Redefinición: Modificación de las propiedades heredadas.
Relaciones: Se establecen entre clases.
Roles: Indican los papeles de las clases en las relaciones.
Sobrecarga de identificadores: Facilidad de un lenguaje de programación que
permite usar el mismo identificador para procedimientos diferentes. Los
procedimientos se diferencian por sus argumentos y no por su nombre.
Sobrecarga de Operadores: Facilidad de un lenguaje de programación que le
permite al programador definir un nuevo tipo de datos que puede usarse en
expresiones. En general, la sobrecarga de operadores implica el uso de los
operadores aritméticos [+ - * / ^] en expresiones.
128
Glosario.
Tipos Abstractos de Datos: Es un el uso de una técnica de abstracción que
busca juntar en un sólo módulo toda la programación relevante a una
estructura de datos en particular. Esto se logra definiendo las operaciones
de un tipo de datos. Lo usual es que un DAT provea Ocultamiento de Datos
y alguna forma de Encapsulamiento de datos, aunque ésta última no sea
completa.
129
Bibliografía.
BIBLIOGRAFÍA
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
131
Patterson J., Stephenson C., Introduction to Programming in Java, University of Toronto
Press, 2000
Castillo, E., Cobo A., Gómez P., Solares C. Java Un lenguaje de programación
multiplataforma para Internet. Paraninfo, 1997.
Newman, Alexander. Using Java. Macmillan Computer Publishing, 1996.
Froufe, Agustín. Java 2 - Manual de Usuario y Tutorial. Alfaomega, 2000.
Campione, Mary. The Java Tutorial. Addison-Wesley, 2000.
Eckel, Bruce. Thinking in Java. Prentice Hall, 2000.
Griffith, Steven W. 1001 tips para programar con Java. McGraw-Hill, 1997.
Dpto Ampliación de Informática. Java y la POO. Universidad de Zaragoza España.
Parra Fuente, Javier. Curso Java Avanzado. Departamento de Lenguajes y Sistemas
Informáticos, Univ. Pontificia de Salamanca en Madrid
Venners, Hill. Designing with interfaces. Java World (http://www.javaworld.com/),
12/1998.
Grady Booch, Análisis y Diseño orientado a objetos. Ed. Addison Wesley
Iberoamericana.
Sitio Web Oficial de Sun Microsystem – www.java.sun.com
Página
de
descargas
de
la
Universidad
de
Castilla
La
Mancha
http://scalab.uc3m.es/~docweb/pr-inf/index.html
Revista electrónica sobre java – www.javaworld.com
Tutorial Java Avanzado – www.geocities.com
TUTOR JAVA – www.programacion.net
Guía de Páginas OO. www.well.com/user/ritchie/oo.html#index por Ricardo Devis
Guía
de
Páginas
y
Lenguajes
OO.
Universidad
Alicante,
España
www.dlsi.ua.es/asignaturas/poo/Enlaces
Jakarta Tomcat, http://jakarta.apache.org/tomcat/
Stevens, Jon S. “You Make the Decisión”,
http://jakarta.apache.org/velocity/ymtd/ymtd.html.
Hunter, J “The Problems with JSP”,http://www.servlets.com/soapbox/problems-jsp.htmp
Notas específicas sobre el tema “Sobrecargas” y “Herencias”
http://manowar.lsi.us.es/pipermail/csharp-dist/2001-March/000500.html
http://www.sc.ehu.es/sbweb/fisica/cursoJava/fundamentos/herencia/intro_herencia.htm
http://www.map.es/csi/silice/Auditr12.html
http://www.sld.cu/revistas/aci/vol5_s_97/sup04197.htm
http://www.openresources.com/es/magazine/softwareengineering/quality-control/
http://agamenon.uniandes.edu.co:80/sistemas/6707.htm
http://gente.pue.udlap.mx/~sol/docencia/ti/sesion4.html
http://www.sc.ehu.es/jiwdocoj/remis/docs/aseguracal.htm
http://www.fciencias.unam.mx/~ho/SPICE/pres1.html
http://www.pcworld.com.pa/1998/diciembre/novedades.html
http://www.ati.es/novatica/1997/125/nv125pres.html
http://www.robelle.com/smugbook/quality.html
http://www.abo.fi/~atorn/SQuality/SQ11.html
http://www.utexas.edu/coe/sqi/newsletter/
http://www.isaca.org/standard/sp.htm
http://www.csuohio.edu/accounts/Audit4/
http://www.ati.es/gt/calidad-software/presentacion.htm
Programas ejemplo. Clases abstractas.
PROGRAMAS EJEMPLO DE LOS TEMAS TRATADOS.
Redefinición de métodos
/*Programa 1
- Versión 1*/
/* REDEFINICIÓN:(OVERRIDEN) Una clase puede redefinir (volver a
* definir) cualquiera de los métodos heredados de su super-clase
* que no sean final.
* El nuevo método sustituye al heredado para todos los efectos
* en la clase que lo ha redefinido.
* Con Funciones Redefinidas en Java se utiliza siempre
* Vinculación tardía
*/
//CLASE PADRE
class Oceania {
// se definen 3 constantes de tipo cadena (variables
// finales)
final String e1="Australia",e2="Fidji",e3="Nueva Zelanda";
public void descripción() {
System.out.println("Principales Estados");
System.out.println(e1);System.out.println(e2);
System.out.println(e3);
}
}
//CLASE HIJA (derivada)
class Australia extends Oceania {
final String capital="Canberra"; // se define una
// constante(variable final) llamada
// capital y se le asigna el valor Canberra
public void descripción() {
System.out.println("La capital de Australia es:
capital);
}
}
"+
public class Geografia1 {
public static void main(String[] args) {
Australia ciudad = new Australia();
// crea un objeto "ciudad" de la clase
// hija "Australia"
ciudad.descripción();
// ejecuta el método "descripción" de la clase
// hija
}
}
/*el método descripción que estaba definido en la clase
* padre(Oceanía), se redefine en la clase hija Australia/
133
Programas ejemplo. Clases abstractas.
/*Programa 1
- Versión 2*/
//CLASE PADRE
class Oceania {
//variante
// se declara un vector de la clase String(cadena) y se lo
// inicializa con 3 posiciones
}
String estado[]=new String[3];
public void descripción() {
estado[0]="Australia";
estado[1]="Fidji";
estado[2]="Nueva Zelanda";
System.out.println("Principales Estados de Oceanía");
for(int i=0;i<3;i++){
System.out.println(estado[i]);
}
}
//CLASE HIJA (derivada)
class Australia extends Oceania {
final String capital="Canberra";// se define una constante
//(variable final) llamada “capital” y se le asigna el
valor // Canberra
public void descripción() {
System.out.println("\n"+"La capital de Australia es:
"+ capital);
}
}
public class Geografia2 {
public static void main(String[] args) {
Oceania estado = new Oceania();
//(variante) Crea un objeto "estado" de la clase
padre // "Oceanía"
Australia ciudad = new Australia();
// Crea un objeto "ciudad" de la clase Hija
// "Australia"
estado.descripción();
// (variante) ejecuta el método "descripción" de la
// clase padre
ciudad.descripción();
//ejecuta el método "descripción" de la clase hija
}
}
134
Programas ejemplo. Clases abstractas.
/*Programa 1
- Versión 3*/
//CLASE PADRE
class Oceania {
String estado[]=new String[3];
public void descripción() {
estado[0]="Australia";
estado[1]="Fidji";
estado[2]="Nueva Zelanda";
System.out.println("Principales Estados");
for(int i=0;i<3;i++){
System.out.println(estado[i]);
}
}
}
//CLASE HIJA (derivada)
class Australia extends Oceania {
final String capital="Canberra";
// se define una constante(variable final) y se le asigna
// el valor Canberra
public void descripción() {
System.out.println("\n"+"La capital de Australia es:
"+ capital);
}
}
//CLASE HIJA (derivada)
class Fidji extends Oceania {
final String capital="Suva";
// se define una constante(variable final) y se le asigna
// el valor Suva
public void descripción() {
System.out.println("\n"+"La capital de Fidji es: "+
capital);
}
}
public class Geografia3 {
public static void main(String[] args) {
/* (variante) */
Oceania ciudad;
//declara una referencia a un objeto de la clase padre
// "Oceania" . Se inicializa a null
ciudad= new Australia();
// La referencia "apunta" al nuevo objeto creado de la
// clase hija "Australia"
ciudad.descripción();
// ejecuta el método "descripción" de la clase hija
// "Australia"
ciudad =new Fidji();
// La misma referencia "apunta" al nuevo objeto creado de
// la clase hija "Fidji"
ciudad.descripción();
// ejecuta el método "descripción" de la clase hija "Fidji"
135
Programas ejemplo. Clases abstractas.
}
}
Modelos de Ejecución
Versión 1: La capital de Australia es:
Canberra
Versión 2: Principales Estados de Oceanía
Australia
Fidji
Nueva Zelanda
La capital de Australia es:
Canberra
Versión 3: Principales Estados de Oceanía
Australia
Fidji
Nueva Zelanda
La capital de Australia es: Canberra
La capital de Fidji es: Suva
136
Programas ejemplo. Clases abstractas.
/*
Programa 2
*/
/* Cuando una clase deriva de otra hereda la funcionalidad de la
* clase padre, pero además una clase hija, puede
* sobreescribirla.Podemos escribir un método en la clase hija
* que tenga el mismo nombre y los mismos parámetros que un
* método de la clase padre
*/
class Mamifero {
public void descripción() {
System.out.println("El mamífero es un Vertebrado");
}
}
class Perro extends Mamifero {
public void descripción() {
System.out.println("Mamifero carnivoro ");
}
}
public class Animales {
public static void main(String[] args) {
Perro Toby = new Perro();
Toby.descripción();
}
}
// Al ejecutar el programa veremos que se escribe el mensaje de
// la clase hija, no el del padre.
Modelos de Ejecución
Mamifero carnivoro
137
Programas ejemplo. Clases abstractas.
Sobrecarga de métodos
/*Programa 1
- Versión 1*/
/* El concepto de polimorfismo, en cuanto a cambio de forma, se
* puede extender a los métodos.
* Java permite que varios métodos dentro de una clase se llamen
* igual, siempre y cuando su lista de parámetros sea distinta
*(en cuanto a número o tipo de argumentos).
* /
/** Diversos modos de sumar (versión estática)*/
public class Sumar {
public static float suma(float a, float b) {
System.out.println("suma de reales");
return a+b;
}
public static int suma(int a, int b) {
System.out.println("suma de enteros");
return a+b;
}
public static void main(String[] args) {
float x=1,y=2;
int v = 3,w = 5;
System.out.println(suma(x,y));
System.out.println(suma(v,w));
}
}
Modelos de Ejecución
versión 1
suma de reales
3.0
suma de enteros
8
138
Programas ejemplo. Clases abstractas.
/*Programa 1
- Versión 2*/
/** Diversos modos de sumar(versión flexible) */
public class Sumar {
// método principal
public static void main(String[] args) {
try {
// controla que el usuario ingrese correctamente los
// valores
float valor1=Float.parseFloat(args[0]);
float valor2=Float.parseFloat(args[1]);
int tipo = Integer.parseInt(args[2]);
switch (tipo){
case 1:
int x=(int)valor1;
int y=(int)valor2;
System.out.println(suma(x,y));
break;
case 2:
System.out.println(suma(valor1,valor2));
break;
default:
System.out.println("El 3er valor debe ser:");
System.out.println("1:si desea sumar
enteros");
System.out.println("2:si desea sumar
reales");
}
}
catch(ArrayIndexOutOfBoundsException e){
System.out.println("Por Favor ingrese 3 valores");
System.out.println("1er valor --> 1er sumando");
System.out.println("2do valor --> 2do sumando");
System.out.println("3er valor --> 1 para sumar
enteros");
System.out.println("
--> 2 para sumar reales");
}
}
// método de clase(Static) que arroja valores REALES
public static float suma(float a, float b) {
System.out.println("suma de reales");
return a+b;
}
// método de clase(Static) que arroja valores ENTEROS
public static int suma(int a, int b) {
System.out.println("Estoy sumando enteros");
return a+b;
}
}
139
Programas ejemplo. Clases abstractas.
Modelos de Ejecución
versión 2
C:\
Por
1er
2do
3er
JAVA Sumar
Favor ingrese 3 valores
valor --> 1er sumando
valor --> 2do sumando
valor --> 1 para sumar enteros
--> 2 para sumar reales
C:\JAVA Sumar 7 7
El 3er valor debe
1: si desea sumar
2: si desea sumar
7
ser:
enteros
reales
C:\JAVA Sumar 7 7 1
Suma de enteros
14
C:\Sumar 7 7 2
Suma de reales
14.0
140
Programas ejemplo. Clases abstractas.
/** Programa 2 */
/** Calcula el área de un rectángulo */
public class Rectangulo {
private int largo,ancho;
// variables privadas: únicamente se podrán utilizar en
// esta clase
// metodo principal donde comienza la ejecución del
// programa
public static void main(String[] args) {
// Bloque TRY/CATCH controla que el usuario ingrese los
// valores correctamente
try{
int n1=Integer.parseInt(args[0]);
// n1 toma el 1er valor ingresado por el usuario
int n2=Integer.parseInt(args[1]);
// n2 toma el 2do valor ingresado por el usuario
// crea un objeto de la clase Rectángulo - se pasa
// como argumento los valores n1 y n2
Rectángulo rec1 = new Rectángulo(n1,n2);
// crea un objeto de la clase Rectángulo sin
// argumentos
Rectángulo rec2 = new Rectángulo();
System.out.println("Área Rectángulo Usuario");
// Llama implícitamente al método calcularArea(con
// argumentos)
System.out.println(rec1.calcularArea());
System.out.println("Área Rectángulo Computadora");
// Llama implícitamente al método calcularArea(sin
// argumentos)
System.out.println(rec2.calcularArea());
} catch(ArrayIndexOutOfBoundsException e){
System.out.println("Por Favor Ingrese los valores del
largo y ancho del rectangulo");
}
}
// rec1 utiliza este Método Constructor pues se pasó 2
// argumentos durante su creación
public Rectangulo(int lar, int anch) {
largo = lar;
// largo y ancho tendrán los valores ingresado por el
// usuario
ancho = anch;
}
141
Programas ejemplo. Clases abstractas.
// rec2 utiliza este Método Constructor pues no se pasó
// argumento alguno durante su creación
public Rectangulo() {
// largo y ancho tendrán valores aleatorios ingresados por la
// función random de la clase Math
}
largo = (int)(Math.random()*10);
ancho = (int)(Math.random()*10);
// método al que se llama con los objetos rec1 y rec2 en el
// método main(principal)
}
public float calcularArea() {
int area=(int)Math.abs(largo*ancho);
return area;
//e es el valor que se devuelve al programa llamador
}
Modelos de Ejecución
C:\java Rectangulo
Por Favor Ingrese los valores del largo y ancho del rectangulo
C:\java Rectangulo 5 5
Area Rectangulo Usuario
25.0
Area Rectangulo Computadora
32.0
142
Programas ejemplo. Clases abstractas.
/*Programa 3 */
/** Aproximación al Número E */
/** Este ejemplo utiliza los métodos de la clase PrintStream que
* proporciona utilidades para dar formato a la salida. Dichos
* métodos son print y println que están SOBRECARGADOS para los
* tipos primitivos, cadenas y arrays de caracteres.
* La diferencia entre ambos métodos está en que println añade un
* carácter de nueva línea. Además el método println puede
* llamarse sin argumentos, produciendo una nueva línea.
*/
class NumE {
//clase PACKAGE (por defecto)
public static void main (String args[]){
try{
// n contiene el nº ingresado por teclado con el cual
// nos aproximaremos al nº E
int n=Integer.parseInt(args[0]);
double vector[]=new double [(n+1)];
int i=0, j=0, k=0;
double numE=0;
// Se controla que el usuario no ingrese valores
// negativos o nulo
if (n<=0)
System.out.println("La formula solo permite
aproximarse al nº e con valores
positivos");
else {
vector[0]=1;//0!=1
vector[1]=1;//1!=1
for(i=2; i<=n; i++){
j=i;k=i;
while (j>1){
j--;
k*=j;
} //n! = n*(n-1)! -variable k
//contendrá el valor k!
vector[i]=k;
}
}
// Forma el número aproximado a "e"
for (i=0; i<=n; i++) {
numE+=(1/(vector[i]));
}
// Salida por pantalla
System.out.println();
System.out.println("Valor Aproximado: "+numE);
System.out.println("Numero e: "+Math.E);
System.out.println("Margen de error: "+(Math.EnumE));
} catch(ArrayIndexOutOfBoundsException e){
System.out.println("Por favor ingrese el valor
143
Programas ejemplo. Clases abstractas.
}
}
}
con el cual desea aproximarse al num E ");
Explicación del Algoritmo
Existe una forma de aproximarse al número e = 2.71828182.... mediante la
siguiente fórmula: 1/0! + 1/1! + ............+1/N! , la cual tiende al número e cuando N
tiende a infinito.
Por lo tanto en este programa se define la clase NumE, el método constructor
y las variables necesarias.
Utilizamos un vector de n+1 elementos, recorriéndolo mediante un bucle For y lo
cargamos con el factorial correspondiente a cada posición (pos 0 --> 0!, .pos N --> N! ).
Posteriormente se calcula y se da salida al número aproximado obtenido,
determinando además el margen de error existente (e - valor aproximado)
Modelos de Ejecución
C:\java NumE
Por favor ingrese el valor con el cual desea aproximarse al num
E
C:\java NumE 23
Valor Aproximado: 2.7182818330591023
Numero e: 2.718281828459045
Margen de error: -4.600057224024567E-9
C:\java NumE 30
Valor Aproximado: 2.718281831563322
Numero e: 2.718281828459045
Margen de error: -3.104276835586006E-9
C:\java NumE 50
Valor Aproximado: Infinity
Numero e: 2.718281828459045
Margen de error: -Infinity
144
Programas ejemplo. Clases abstractas.
/*Programa 4 */
/** Este ejemplo compara un número ingresado por el usuario con
* un valor aleatorio arrojado por la máquina determinando cual
* es el mayor en términos de Enteros y Decimales
*
* Métodos Sobrecargados:
* El Método "max" de la clase Math,se utiliza 2 veces con
* argumentos de distintos tipos.
* El Metodo "println" de la clase PrintStream, se utiliza varias
* veces con distintos tipos y/o número de argumentos.
*/
public class Mayor{
public static void main(String arg[]){
try {
// se controla que el usuario ingrese
// correctamente los valores
//"va" alberga el valor ingresado por el usuario
double va=Double.parseDouble(arg[0]);
// "vc" alberga un valor determinado
// aleatoriamente por la función random de la
// clase Math
double vc=(Math.random()*100
int vb=(int)va;
// se transfiere a la variable entera "vb" el
// valor real de precisión doble de la var."va"
}
145
}
int vd=(int)vc;
//salida por pantalla
System.out.println();
System.out.println("Numeros Reales");
System.out.println("Usted ha ingresado el
numero:" + va);
System.out.println("La computadora ingreso el
numero:"+ vc);
System.out.println("El Mayor es:"+
Math.max(va,vc));
System.out.println();
System.out.println("Numeros Enteros");
System.out.println("Usted ha ingresado el
numero:" + vb);
System.out.println("La computadora ingreso el
numero:"+ vd);
System.out.println("El Mayor es:"+
Math.max(vb,vd));
}catch(IndexOutOfBoundsException e){
System.out.println("Por Favor Ingrese un
Numero");}
Programas ejemplo. Clases abstractas.
Modelos de Ejecución
C:\java Mayor
Por Favor Ingrese un Numero
C:\java Mayor 55
Numeros Reales
Usted ha ingresado el numero:55.0
La computadora ingreso el numero:60.24044742541519
El Mayor es:60.24044742541519
Numeros Enteros
Usted ha ingresado el numero:55
La computadora ingreso el numero:60
El Mayor es:60
C:\java Mayor 23
Numeros Reales
Usted ha ingresado el numero:23.0
La computadora ingreso el numero:11.226574387976019
El Mayor es:23.0
Numeros Enteros
Usted ha ingresado el numero:23
La computadora ingreso el numero:11
El Mayor es:23
146
Programas ejemplo. Clases abstractas.
Clases abstractas
/** Programa 1 */
/** Una Clase Abstracta (abstract) es una clase de la que no se
* pueden crear objetos. Su utilidad es permitir que otras clases
* deriven de ella, proporcionándoles un modelo a seguir y
* algunos métodos de utilidad general.
*/
abstract class LenguajesDeProgramacion {
String[] nom=new String[4];
public abstract void detalle();
// método abstract(no se da su definición)
}
class Declarativos extends LenguajesDeProgramacion {
final String tipo="Funcionales";
public void detalle() {
nom[0] = "lisp";
nom[1] = "Hope";
nom[2] = "Miranda";
nom[3] = "Haskell";
System.out.println("Lenguajes Declarativos");
System.out.println("\n"+tipo);
for (int i=0;i<4;i++) System.out.println(nom[i]);
}
}
public class Abstractos {
public static void main(String[] args) {
Declarativos nombre = new Declarativos();
nombre.detalle();
}
}
Modelos de Ejecución
C:\ java Abstractos
Lenguajes Declarativos
Funcionales
lisp
Hope
Miranda
Haskell
147
Programas ejemplo. Clases abstractas.
/**Programa 2 – versión 1*/
abstract class Vitamina {
String alimento[]=new String[3];
public abstract void detalle();
}
class A extends Vitamina{
public void detalle() {
alimento[0]="vegetales";alimento[1]="Yema de Huevo";
alimento[2]="leche";
System.out.println("La vitamina A se encuentra en:");
for (int i=0;i<3;i++){
System.out.println(alimento[i]);
}
}
}
public class Medicina {
public static void main(String[] args) {
A vita = new A();
vita.detalle();
}
}
Modelos de Ejecución
C:\ java Medicina
La vitamina A se encuentra en:
vegetales
Yema de Huevo
Leche
148
Programas ejemplo. Clases abstractas.
/**Programa 2 – versión 2*/
abstract class Vitamina {
String alimento[]=new String[3];
public abstract void detalle();
}
class A extends Vitamina{
public void detalle() {
alimento[0]="vegetales";alimento[1]="Yema de Huevo";
alimento[2]="leche";
System.out.println("La vitamina A se encuentra en:");
for (int i=0;i<3;i++){
System.out.println(alimento[i]);
}
}
}
class E extends Vitamina{
public void detalle() {
alimento[0]="germen de trigo";
alimento[1]="aceite de soja";
alimento[2]="maiz";
System.out.println("\n"+"La vitamina E se encuentra en:");
for (int i=0;i<3;i++){
System.out.println(alimento[i]);
}
}
}
public class Medicina1 {
public static void main(String[] args) {
A vita = new A();vita.detalle();
E vita1 = new E();vita1.detalle();
}
}
Modelos de Ejecución
C:\java Medicina1
La vitamina A se encuentra en:
vegetales
Yema de Huevo
Leche
La vitamina E se encuentra en:
germen de trigo
aceite de soja
maiz
149
Programas ejemplo. Polimorfismo.
Polimorfismo
/*Programa 1 – versión1 */
/** En muchas ocasiones, cuando utilizamos herencia podemos
* terminar teniendo una familia de clases que comparten un
* interfaz común.
*/
//clase PADRE
abstract class Lenguajes {
String nom[]=new String[3];
// se declara un vector "nom[]" de la clase String y se lo
// inicializa con 3 posiciones
}
public abstract void detallar();
//clase HIJA (hereda la variable y método de la clase PADRE)
class Objetos extends Lenguajes {
public void detallar() {
// se redefine como public el método detallar
// (definido como abstract)
nom[0]="C++";
nom[1]="Java";
nom[2]="Eiffell";
System.out.println("Lenguajes de Prog.Orientados a
Objetos");
for(int i=0;i<3;i++){
System.out.println(nom[i]);
}
}
}
//clase HIJA
class Declarativos extends Lenguajes {
public void detallar() {
// se redefine como public el método detallar
// (definido como abstract)
nom[0]="Lisp";
nom[1]="Prolog";
nom[2]="Haskell";
System.out.println("\n");
System.out.println("Lenguajes de Prog.Declarativos");
for(int i=0;i<3;i++){
System.out.println(nom[i]);
}
}
}
public class Informatica1 {
public static void detalle(Lenguajes m) {
151
Programas ejemplo. Polimorfismo.
// el método detalle utiliza un objeto "m" de la
// clase Lenguajes
}
m.detallar();
// se llama al método detallar con un argumento
// Implícito objeto.método
// método PRINCIPAL
public static void main(String[] args) {
Objetos leng1 = new Objetos();
// crea un objeto "leng1" de la clase Objetos
Declarativos leng2= new Declarativos();
// crea un objeto "leng2" de la clase Declarativos
detalle(leng1);
// llama al método detalle pasando el objeto "leng1"
// como argumento explícito
}
}
detalle(leng2);
// llama al método detalle pasando el objeto "leng2"
// como argumento explícito
/*el método detalle llama al método detallar ejecutándose las
* sentencias que componen este último método para el objeto
* especifico que lo llama.*(primero llama a un leng. Orientado a Objetos y luego a un
* leng. Declarativo)
*/
Modelos de Ejecución
Lenguajes de Prog.Orientados a Objetos
C++
Java
Eiffell
Lenguajes de Prog.Declarativos
Lisp
Prolog
Haskell
152
Programas ejemplo. Polimorfismo.
/*Programa 2 – versión 1 */
/** CALCULA AREAS DE DISTINTAS FIGURAS (versión estática)*/
abstract class Figura{
abstract public double area();
}
//clase abstracta
class Circulo extends Figura{
//clase derivada de Figura
private double radio;
// var private solo podrá ser utilizada por la clase
// Círculo
}
final double pi=3.1416;
// var final: (constante) no puede cambiar su valor durante
// la ejecución del prog.
// método constructor de la clase Circulo utiliza un
// argumento pasado por referencia desde el método main
// (principal)
Circulo(double r){
radio=r;
}
// redefinición del método area(),
public double area(){
return pi*radio*radio;
// valor que retorna al programa llamador
}
class Cuadrado extends Figura{
private double lado;
Cuadrado(double l){
lado=l;
}
public double area(){
return lado*lado;
}
}
class Triangulo extends Figura{
private double base;
private double altura;
Triangulo(double b, double a){
base=b; altura=a;
}
public double area(){
return 0.5*base*altura;
}
}
class polimorfismo{
// constructor principal (donde comienza la ejecución del
// programa)
153
Programas ejemplo. Polimorfismo.
public static void main(String args[]){
// control de Excepciones producidas por el usuario
try{
double n1=Double.parseDouble(args[0]);
double n2=Double.parseDouble(args[1]);
double ñ=Double.parseDouble(args[2]);
double m=Double.parseDouble(args[3]);
Triangulo tri=new Triangulo(n1,n2);
// se crea un objeto de la clase "Triangulo"
Circulo cir=new Circulo(m);
Cuadrado cua=new Cuadrado(ñ);
// salida por pantalla a la información obtenida
// en la ejecución del programa
n2);
System.out.println();
System.out.println("TRIANGULO");
System.out.println("base: "+n1 + " altura: "+
System.out.println("Area "+tri.area());
System.out.println();
System.out.println("CUADRADO");
System.out.println("lado: "+ñ);
System.out.println("Area: "+cua.area());
System.out.println();
System.out.println("CIRCULO");
System.out.println("radio: "+m);
System.out.println("Area "+cir.area());
System.out.println();
}
}
}catch(ArrayIndexOutOfBoundsException e){
System.out.println("Por Favor,ingrese 4 valores");
System.out.println("\n"+ "1er valor: Base
Triangulo");
System.out.println("\n"+ "2do valor: Altura
Triangulo");
System.out.println("\n"+ "3er valor: Lado
Cuadrado");
System.out.println("\n"+ "4to valor: Radio
Circulo");
}
154
Programas ejemplo. Polimorfismo.
Modelos de Ejecución
C:\java polimorfismo
Por Favor, ingrese 4 valores
1er valor: Base Triangulo
2do valor: Altura Triangulo
3er valor: Lado Cuadrado
4to valor: Radio Circulo
C:\java polimorfismo 7 5 8 2
TRIANGULO
base: 7.0
Area 17.5
altura: 5.0
CUADRADO
lado: 8.0
Area: 64.0
CIRCULO
radio: 2.0
Area 12.5664
155
Programas ejemplo. Polimorfismo.
/**Programa 2 – versión 2 */
/**CALCULAR AREA DE DISTINTAS FIGURAS (versión flexible)*/
abstract class Figura{
abstract public double area();
}
// clase abstracta
class Circulo extends Figura{
private double radio;
final double pi=3.1416;
// método constructor de la clase Circulo
// utiliza un argumento pasado por referencia desde el
// método main(principal)
Circulo(double r){
radio=r;
}
// redefinición del método area(), se declara como publico
// para que pueda ser accedido por todas las clases del
// paquete
}
public double area(){
return pi*radio*radio;
//valor que retorna al programa llamador
}
class Cuadrado extends Figura{
private double lado;
Cuadrado(double l){
lado=l;
}
public double area(){
return lado*lado;
}
}
class Triangulo extends Figura{
private double base;
private double altura;
Triangulo(double b, double a){
base=b; altura=a;
}
public double area(){
return 0.5*base*altura;
}
}
156
Programas ejemplo. Polimorfismo.
class Polimorfismo{
// constructor principal (donde comienza la ejecución del
// programa)
public static void main(String args[]){
// control de Excepciones producidas por el usuario
try{
double n1=Double.parseDouble(args[1]);
// n1= 2º valor ingresado por el usuario
int picture=Integer.parseInt(args[0]);
//picture = 1º valor ingresado por el usuario
switch(picture){
case 1:
Circulo cir=new Circulo(n1);
System.out.println("\n"+"CIRCULO");
System.out.println("radio: "+n1);
System.out.println("Area "+cir.area());
System.out.println("\n"+"Desea calcular el
area de otra figura?");
System.out.println("\n"+"Para calcular area
de un: ");
System.out.println("CIRCULO: ingrese 1 y el
valor que desea darle al Radio");
System.out.println("CUADRADO: ingrese 2
y el valor que desea darle al Lado");
System.out.println("TRIANGULO: ingrese 3 y
los valores que desea darle a la Base y a
la Altura");
break;
case 2:
Cuadrado cua=new Cuadrado(n1);
System.out.println("CUADRADO ");
System.out.println("lado: "+n1);
System.out.println("Area: "+cua.area());
System.out.println("\n"+"Desea calcular el
area de otra figura?");
System.out.println("\n"+"Para calcular area
de un: ");
System.out.println("CIRCULO: ingrese 1 y el
valor que desea darle al Radio");
System.out.println("CUADRADO: ingrese 2
y el valor que desea darle al Lado");
System.out.println("TRIANGULO: ingrese 3 y
los valores que desea darle a la Base y a
la Altura");
break;
157
Programas ejemplo. Polimorfismo.
case 3:
double n2=Double.parseDouble(args[2]);
Triangulo tri=new Triangulo(n1,n2);
System.out.println("TRIANGULO");
System.out.println("base: "+n1 + " altura: "+
n2);
System.out.println("Area "+tri.area());
System.out.println("\n"+"Desea calcular el
area de otra figura?");
System.out.println("\n"+"Para calcular area
de un: ");
System.out.println("CIRCULO: ingrese 1 y el
valor que desea darle al Radio");
System.out.println("CUADRADO: ingrese 2 y el
valor que desea darle al Lado");
System.out.println("TRIANGULO: ingrese 3 y
los valores que desea darle a la Base y a
la Altura");
break;
}
}catch(ArrayIndexOutOfBoundsException e){
System.out.println("\n"+"Ingrese 2 valores, si
desea calcular el area de:");
System.out.println("\n"+"un CIRCULO");
System.out.println("1er valor: 1 " + "\t" +
"2do valor: RADIO CIRCULO");
System.out.println("\n"+"un CUADRADO");
System.out.println("1er valor: 2 " + "\t" +
"2do valor: LADO CUADRADO");
System.out.println();
}
}
}
System.out.println("\n"+"Ingrese 3 valores, si
desea calcular el area de:");
System.out.println("\n"+"un TRIANGULO");
System.out.println("1er valor: 3 " + "\t" +
"2do valor: BASE TRIANGULO; 3er valor:
ALTURA TRIANGULO");
/** Este programa interactúa con el usuario brindádole la
* posibilidad de elegir de que figura desea calcular el área
*/
158
Programas ejemplo. Polimorfismo.
Modelos de Ejecución
C:\java Polimorfismo
Ingrese 2 valores, si desea calcular el area de:
un CIRCULO
1er valor: 1
2do valor: RADIO CIRCULO
un CUADRADO
1er valor: 2
2do valor: LADO CUADRADO
Ingrese 3 valores, si desea calcular el area de:
un TRIANGULO
1er valor: 3
2do valor: BASE TRIANGULO; 3er valor: ALTURA
TRIANGULO
C:\java Polimorfismo 1 10
CIRCULO
radio: 10.0
Area 314.16
Desea calcular el area de otra figura?
Para calcular area de un:
CIRCULO: ingrese 1 y el valor que desea darle al Radio
CUADRADO: ingrese 2 y el valor que desea darle al Lado
TRIANGULO: ingrese 3 y los valores que desea darle a la Base y a la
Altura
C:\java Polimorfismo 2 10
CUADRADO
lado: 10.0
Area: 100.0
Desea calcular el area de otra figura?
Para calcular area de un:
CIRCULO: ingrese 1 y el valor que desea darle al Radio
CUADRADO: ingrese 2 y el valor que desea darle al Lado
TRIANGULO: ingrese 3 y los valores que desea darle a la Base y a la
Altura
159
Programas ejemplo. Polimorfismo.
/**Programa 3 */ /**subprograma 1*/
package polimorfismo;
public class Polimorfis {
public static void main(String[] args) {
Espinacas espi=new Espinacas();
que_aporta(espi);
Tomate tomate=new Tomate();
que_aporta(tomate);
try {
}
}
//espera la pulsación de una tecla y luego
RETORNO
System.in.read();
}catch (Exception e) { }
static void que_aporta(Vitaminas alimento){
alimento.aporte();
}
/** subprograma 2*/
package polimorfismo;
public abstract class Frutas {
}
class Citricos extends Frutas implements Vitaminas{
public void aporte(){
System.out.println("Los cítricos aportan vitamina
C");
}
}
/** subprograma 3*/
package polimorfismo;
// clase Base implementa la interfaz Vitaminas
public abstract class Verduras implements Vitaminas{
public abstract void aporte();
//define un método abstracto
}
// clase derivada de Verduras
class Espinacas extends Verduras{
public void aporte(){
System.out.println("Las Espinacas aportan vitamina
B9");
}
}
160
Programas ejemplo. Polimorfismo.
// clase derivada de Verduras
class Tomate extends Verduras{
public void aporte(){
System.out.println("El tomate aporta vitamina k");
}
}
/** subprograma 4*/
package polimorfismo;
public interface Vitaminas {
public abstract void aporte();
}
/* En el lenguaje Java solamente existe la herencia simple, pero
* las clases pueden implementar interfases, las cuales
* incrementan el polimorfismo del lenguaje más allá del que
* proporciona la herencia simple.
* Si solamente hubiese herencia simple, Citricos tendría que
* derivar de la clase Verduras (lo que no es lógico) o bien no
* se podría pasar a la función que_aporta.
* Con interfases, cualquier clase en cualquier familia puede
* implementar la interfase Vitaminas, y se podrá pasar un objeto
* de dicha clase a la función "que_aporta".
* Esta es la razón por la cual las interfases proporcionan más
* polimorfismo que el que se puede obtener de una simple
* jerarquía de clases.
*/
Modelos de Ejecución
Las Espinacas aportan vitamina B9
El tomate aporta vitamina k
161
Programas ejemplo. Ligaduras Dinámicas.
Ligaduras dinámicas
/** Programa 1 */ /** Subprograma 1*/
package Figura;
public abstract class Figura {
public abstract double area();
}
class Circulo extends Figura{
protected double radio;
public Circulo(double radio){
this.radio = radio;
// variable miembro radio alberga el valor del
// argumento radio
}
public double area(){
//redefine el método abstracto de la clase Base
return Math.PI*radio*radio;
//retorna el valor del área del círculo
}
}
class Cuadrado extends Figura{
protected double lado;
public Cuadrado(double lado){
this.lado=lado;
}
public double area(){
return lado*lado;
}
}
/* variables PROTECTED: sólo la clase en la que se define, las
* clases que deriven de ella y las clases del propio package
* tienen permiso para utilizarlas.
* JAVA permite declarar una variable dentro de un bloque con el
* mismo nombre que una variable miembro.
* La variable declarada dentro del bloque oculta a la variable
* miembro en ese bloque.
* Para acceder a la variable miembro oculta será preciso
* utilizar el operador this, en la forma this.varname.
* De esta forma, this.radio se refiere a la variable miembro,
* mientras que radio es el argumento del constructor.
*/
163
Programas ejemplo. Ligaduras Dinámicas.
/**Subprograma 2*/
package Figura;
public class FiguraApp {
public static void main(String[] args) {
// enlace temprano: tiempo de compilación
Circulo c=new Circulo(5.5);
System.out.println("Area del circulo "+ c.area());
Cuadrado r=new Cuadrado(5.5);
System.out.println("Area del cuadrado "+r.area());
// enlace tardío 1: tiempo de ejecución
// valores predefinidos
Figura f=new Circulo(2);
System.out.println("Area del circulo "+f.area());
f=new Cuadrado(3);
System.out.println("Area del cuadrado "+f.area());
// enlace tardío 2
// valores ingresados por teclado
try {
Figura fig[]=new Figura[2];
int variable=System.in.read();
// toma el entero correspondiente a alt+variable
}
}
System.out.println("las areas se calcularan con el
valor: "+variable);
fig[0]=new Cuadrado(variable);
fig[1]=new Circulo(variable);
for(int p=0;p<2;p++)
System.out.println(fig[p].area());
}catch (Exception e) {}
Modelos de Ejecución
C:\ java
Area del
Area del
Area del
Area del
FiguraApp
circulo 95.03317777109123
cuadrado 30.25
circulo 12.566370614359172
cuadrado 9.0
164
Descargar