Notas de Java
Analisis, Diseño y Programación Orientada a Objetos con Java
Índice de contenido
Introducción
¿Qué es el paradigma de la Orientación a Objetos?
Ventajas del Paradigma de la Orientación a Objetos
Desventajas del Paradigma de la Orientación a Objetos
Conceptos Generales de la OO.
Características de la Orientación a Objetos.
Abstracción.
Encapsulamiento.
Herencia.
Polimorfismo.
Polimorfismo Aparente
Coerción / casting
Sobrecarga.
Paramétrico.
Polimorfismo Real
Real.
Solución orientada a Objetos.
Representación de una Clase.
La Plataforma JAVA.
Versiones de la Plataforma JAVA.
Modos de Ejecución de JAVA.
Código Administrado.
Características del Lenguaje JAVA
ENTRY-POINT
Ejecutable
Java Native Interface
Primer Programa en JAVA.
Variable de Entorno CLASSPATH
Clases
El Constructor.
¿Cómo se destruyen los Objetos?
Paradigma: Paquetes.
Tipos de Datos en Java
Tipos Primitivos.
Las Referencias.
Los Objetos.
Modelo de Memoria de Java
Área de Código/Datos.
Stack.
El Heap.
Alcance o Scope.
Alcance y Perdurabilidad de los Tipos Primitivos / Referencias
Alcance y Perdurabilidad de los Objetos.
Como se obtienen Referencias a Los Objetos?
Tipos en Java.
Tipos Primitivos.
Referencias.
Objetos.
Paradigma
IDENTIDAD vs IGUALDAD.
Herencia.
Aplicaciones de la Herencia.
Mecanismo de Especialización.
Mecanismo de Generalización.
Overriding (redefinición) y Overloading (sobrecarga)
this – super
this
super
Calificadores / Modificadores de Acceso.
Regla de Oro.
Calificadores de Pertenencia.
De Instancia.
De Clase (metadatos).
Clases y Métodos Abstractos.
Clase Abstracta.
Método Abstracto.
Clases y Miembros Finales.
Clases en UML.
Upcasting vs Downcasting
Upcasting.
Downcasting.
Binding.
Binding Estático
Binding Dinámico.
Polimorfismo ó Métodos Polimorficos.
Asociaciones.
Asociaciones Generales.
Asociaciones Especiales.
Agregación
Composición
Clases Asociativas.
Interfaces.
Sobre JAVA. Resumen de Sintaxis.
Clase.
Interface.
Atributo.
Método.
Paquetes.
Dominios de Internet (DNS)
¿Qué es el Nombre Completo de una Clase?
Keyword import.
Java.lang
Default Package
Archivo .JAR
Organización de Fuentes y Clases
Identidad e Igualdad en Java.
Método Equals.
Identidad.
Igualdad.
Otros Métodos de la Clase Object
Método HashCode.
Método toString.
Método clone.
System.out
Integer
int Value
parseInt
valueOf
toString
Hola Mundo
Clases Wrapper
Arreglos en Java.
Sintaxis.
Arreglos de tipos primitivos.
Arreglos de Objetos.
Clase Vector.
Arquitectura Cliente-Servidor.
Arquitectura Multitier
Arquitectura de Tres Capas.
Excepciones
Concepto
Que sucede cuando ocurre una Excepción?
Concepto de Excepción
Lanzar Excepciones
Manejador de Excepciones
Excepciones en RUN-TIME
Interfaz Throwable
Clase Error
Bloque Finally
RTTI (OO)
Clases internas / Clases anidadas
Clases anidadas
Clase interna (inner class)
Características
Clases Internas Anónimas
Características.
JFC – Java Fundation Classes
AWT vs SWING
JAVA BEANS
Características
Convenciones.
Jerarquía de Clases BEANS
JFrame
JApplet
Modelo de Eventos de JAVA
LayoutManager
Generics
Métodos y Constructores Genéricos
Patrones De Diseño
Patrón de Diseño (definición general)
Patrón de diseño (en la OO)
Composición de un patrón
Organización de los patrones (GoF)
Clasificación por propósito
Clasificación por alcance
Problema que se encara
Definición de Framework
Patrones de Creación
Abstract Factory
Singleton
Builder
Factory Method
Prototype
Patrones Estructurales
Adapter
Bridge
Decorator
Versión 1
Versión 2
Composite
Facade
Flyweight
Proxy
Patrones de Comportamiento
Chain of Responsability
Command
Interpreter
Iterator
Mediator
Memento
Observer
State
Strategy
Template Method
Visitor
7Análisis y diseño Orientado a Objeto.
Análisis y Diseño
UML (Unified-Modeling-Languaje)
Análisis
Identificación de Actores (IA)
Casos de Uso (CU)
Documentación de un Caso de Uso.
Curso de Eventos:
Excepciones:
Diagrama de Actividad (DA)
Diagrama de Caso de Usos (DCU)
Ranking de casos de uso
Modelo Conceptual
Asociaciones.
Atributos
Glosario
Diagrama de eventos de sistema
Contratos
Volviendo sobre el modelo conceptual.
Diagrama de colaboración.
Interfaz Gráfica de Prototipo
-
Introducción
-
¿Qué es el paradigma de la Orientación a Objetos? {#¿qué-es-el-paradigma-de-la-orientación-a-objetos?}
-
Con el término Paradigma nos estamos refiriendo a una forma de Pensamiento. Cuando veamos “POO” significa “Programación” Orientada a Objetos, por lo tanto para referirnos al Paradigma, usaremos “OO”.
El Paradigma de OO es un modo de desarrollar software, basándose en el concepto de objeto. Concibiendo el mundo real como un conjunto de objetos reales que interactúan entre sí, el Paradigma propone, mediante un proceso de abstracción, la construcción del software como un conjunto de objetos abstractos que interactúan entre sí. Dichos objetos presentan características (Estado), responsabilidades (Comportamiento) y relaciones (Asociaciones) que en su conjunto dan lugar a una solución al problema a resolver.
Ahora yo comprendo el problema y descubro objetos REALES (NO Procesos), veo que hay interacciones entre éstos. Me concentro en la “cosa” del mundo real. Esto es lo que denomina el proceso de Abstracción, o sea, nos quedamos con la esencia de las cosas, y llegamos a objetos abstractos.
Ilustración 1 - Paradigma OO
-
Ventajas del Paradigma de la Orientación a Objetos
1) Aumenta la capacidad de:
1) Abstracción: Me permite poder asociar cosas abstractas sin fijarme en detalles que no aportan para la solución. Cuanto más abstractos sean los objetos de la solución, va a ser de mejor calidad.
2) Encapsulamiento: Potencia a la OO., porque el objeto tiene características y responsabilidades, y esa composición es una unidad inseparable. Las funciones que son coherentes entre sí se unen. El encapsulamiento es necesario para que todo funcione bien, cuando tengo cosas que quiero que trabajen como una unidad; me permite controlar el software. Los detalles de la implementación interna de datos y procedimientos, están ocultos al mundo exterior (ocultamiento de la información). Esto reduce la propagación de efectos colaterales cuando ocurren cambios.
3) Reusabilidad: Volver a utilizar segmentos de código de un programa en otro.
2) Extensibilidad: Es la capacidad de adaptación a cambios en los problemas del mundo real, con el menor costo, y todo sigue haciendo lo que debía hacer. Establece una homogeneidad a través del Análisis, Diseño e Implementación
A (Qué) --> D (Cómo) --> I (Hacer)
Estos 3 aspectos: el Análisis, el Diseño y la Implementación son cosas muy diferentes. Con la OO se vuelve un proceso muy fluido, ir del análisis a la implementación. Es un camino homogéneo, tiene las mismas características durante todo el camino, es continuo.
3) Facilita el manejo de la complejidad: Mundo real más complejo --> Software más complejo. La complejidad es más controlada.
4) Disminuye y facilita el comportamiento: Extensibilidad fácil --> mantenimiento fácil.
5) Mejora la calidad: Si aumento todas las ventajas anteriores, habrá mejor calidad. Si el SW no hace lo que debería hacer, entonces no sirve en un 100%. Un producto nunca es perfecto, pero debemos encontrar sus puntos fuertes para poder defenderlo. El SW debe ser fácil de usar, y el programador es el que tiene que pensar en la calidad. Primero somos los programadores quienes evaluamos la calidad del SW.
6) Provee una variedad de: Técnicas, métodos (idealmente iterativos e incrementales), procesos, estándares, modelos, patrones, notaciones, herramientas, componentes, lenguajes, ambiente (IDE’s), ejemplos, comunidad, práctica y experiencia.
7) Reditúa en beneficios económicos.
8) Permite una menor concentración en la implementación: El análisis y el diseño son las etapas más importantes, por que en cuanto a la implementación ya se sabe como se hace.
-
Desventajas del Paradigma de la Orientación a Objetos
1) Requiere una mayor concentración en el requerimientos, análisis y en el diseño: El análisis y diseño OO requiere por parte del profesional una determinada cantidad de experiencia. Si se requiere X experiencia en el diseño, entonces se requiere XeY experiencia en el análisis. Luego la implementación es la menos importante de todas, además la implementación depende de las dos anteriores. La calidad del SW está en el Análisis y Diseño.
2) Requiere un esfuerzo inicial en entender el paradigma y culturizarse en sus técnicas.
3) Si bien “teóricamente” es apta para resolver problemas en cualquier contexto, “prácticamente” NO: Cualquier solución con OO es más lenta que una implementación no OO, en consecuencia creer que su sola utilización garantiza la construcción de un sistema correcto (silver bullet) no es así. La culpa en primera instancia no es del lenguaje en sí, sino de las plataformas de implementación.
4) Creer que la orientación a objetos es sólo programación.
-
Conceptos Generales de la OO.
1) Objeto: Entidad de tiempo de ejecución provista de un estado y un comportamiento. Los Objetos sólo existen como tales en tiempo de ejecución (run-time), de lo contrario son solo código.
2) Atributo: Dato perteneciente a un objeto, el conjunto de atributos de un objeto (con sus valores específicos) definen su estado.
3) Método: Operación que un objeto puede llevar a cabo. El conjunto de métodos de un objeto define su comportamiento. La firma de un método es definida por su: nombre, sus parámetros y su retorno.
4) Mensaje: Estímulo enviado a un objeto para que ejecute uno de sus métodos. Dicho estímulo resulta de algún evento (suceso en el sistema).
5) Interfaz: Conjunto de firmas de métodos de un objeto. A su vez, describe un conjunto de mensajes que el objeto puede responder.
6) Tipo: Nombre asignado a una determinada interfaz.
7) Clase: Descripción de los atributos y métodos que un objeto puede poseer. Toda clase define un tipo.
8) Instanciación: Proceso de lectura de la definición especificada de una clase y la creación de un objeto a partir de ella. El objeto creado a partir de una clase pasa a ser una instancia de ella con un tipo determinado. La instanciación se realiza en tiempo de ejecución, o sea, se trata de un proceso en run-time.
9) Sistema Orientado a Objetos: Conjunto de objetos que interactúan entre sí a través del intercambio de mensajes.
-
PILARES (Características) de la Orientación a Objetos. {#pilares-(características)-de-la-orientación-a-objetos.}
-
Abstracción.
-
Se trata de la capacidad de exponer a un objeto como una entidad abstracta capaz de comportarse, informar y cambiar su estado, sin necesidad de revelar cómo es su implementación interna.
-
Encapsulamiento.
Capacidad de reunir el estado y comportamiento del objeto como una unidad indivisible, en un mismo nivel de abstracción.
Ilustración 2 - Objeto y Mensajes
-
Herencia.
La herencia es un mecanismo de Reusabilidad. Es la capacidad de definir una clase a partir de otra clase, creando así, una jerarquía de clases, donde la clase definida se denomina clase derivada (clase hija, subclase) y la clase de la cual se define se denomina clase base (clase padre, súper clase). La clase derivada no sólo define inmediatamente los mismos atributos y comportamientos de la clase base, sino también puede definir atributos y comportamiento nuevos (este proceso se lo denomina Especialización) y/o re-definir ya existentes (proceso denominado Redefinición). Si se permite que una clase herede de más de una clase, se denomina herencia múltiple, caso contrario, herencia simple. Esta capacidad produce que un objeto, si bien es instancia de una sola clase, posee más de un tipo asociado, específicamente aquellos correspondientes a las clases definidas en la jerarquía de clases.
Ilustración 3 - Clases y Herencias
-
Polimorfismo.
El polimorfismo se define como un mecanismo de Extensibilidad y existen cuatro tipos, divididos en dos categorías:
-
Polimorfismo Aparente
Coerción / casting {#coerción-/-casting}
O también definido como casting, es la capacidad de promover un tipo, o sea, múltiples tipos para un mismo valor.
Sobrecarga.
Sobrecarga de funciones, por ejemplo, tener una función F(x) con un determinado nombre pero con distintos parámetros. Se puede decir que una misma funciona tiene múltiples implementaciones, una firma es distinta en cada una.
Paramétrico.
Capacidad de ciertos lenguajes de interpretar un segmento de código genérico y en momento de compilación se reemplaza por el segmento de código que le corresponde.
-
Polimorfismo Real
Real.
Se dirá simplemente, polimorfismo y es la capacidad de sustituir en tiempo de ejecución (run-time) implementaciones de interfaces concordantes, permitiendo así responder un mismo mensaje de distintas maneras y que los objetos puedan interactuar sin conocer su tipo exacto anticipadamente. El polimorfismo real se basa en 4 (cuatro) conceptos, los dos primeros se definen en tiempo de compilación y los dos últimos en tiempo de ejecución:
1) Tiempo de compilación:
1) Las interfaces concordantes corresponden a tipos de clases dentro de la jerarquía de clases definida, es decir, en la herencia.
2) Una clase derivada debe re-implementar el método a ser polimorfo, o sea, es la Redefinición.
2) Tiempo de Ejecución:
3) Se debe considerar un objeto como un tipo superior correspondiente a la jerarquía de clases de la clase de la cual se instancio el objeto. Esto se denomina upcasting.
4) Se debe asociar la implementación específica a la firma del método invocado y esto se denomina binding dinámico.
El binding dinámico (o ligamiento dinámico) es lo que sucede cuando se le asocia un segmento de código particular a una referencia de una función que es llamada. Esto se debe a que en tiempo de compilación es imposible determinar qué implementación será la que se ejecute.
El polimorfismo, en el fondo, es polimorfismo de métodos, o sea, el enlace en tiempo de ejecución de un mensaje a una determinada implementación de un método. A estos se los llama métodos polimórficos.
Se puede hacer una diferenciación entre los lenguajes monomórficos, o sea, aquellos en donde cada componente del lenguaje solo puede tener un tipo y los lenguajes polimórficos (todos los orientados a objetos) en donde los objetos tienen potencialmente múltiples tipos.
El polimorfismo es un concepto de programación orientada a objetos que permite que un objeto pueda ser tratado como si fuera otro tipo de objeto en tiempo de ejecución. En otras palabras, se refiere a la capacidad de una clase de tomar varias formas o comportarse de manera diferente según el contexto en el que se utilice.
Por ejemplo, si tienes una clase “Animal” y dos clases que heredan de ella, “Perro” y “Gato”, ambos pueden ser tratados como “Animal” y tener métodos y atributos en común como “comer” o “dormir”. Sin embargo, cada uno de ellos tiene su propia implementación específica para esos métodos y atributos, lo que les permite comportarse de manera diferente según su tipo.
El polimorfismo es una técnica muy poderosa en la programación orientada a objetos ya que permite escribir código más genérico y reutilizable, y también facilita la implementación de patrones de diseño como el patrón de diseño “Strategy” o el patrón “Visitor”
-
Solución orientada a objetos.
Toda solución para un software Orientado a Objetos está compuesta por un conjunto de clases, con atributos y métodos, donde cuando se instancia una clase, los atributos que la componen son los necesarios para representar al objeto (su estado), y los métodos que la componen representan su comportamiento.
Generalmente, la línea de acción a tomar para la solución de un problema en la orientación a objetos es:
1) Plantear las clases.
2) Plantear los métodos.
3) Plantear los atributos.
4) Plantear las relaciones.
-
Representación de una Clase.
1) La primera letra de los nombres de las clases en mayúscula.
2) La primera letra de los nombres de los atributos/métodos, en minúscula. Si el nombre del Atributo/método esta compuesto por varias palabras, entonces NO se colocan espacios en blanco, ni guiones, ni nada, o sea, todas las palabras una seguida de la otra y cada nueva palabra comienza con mayúscula.
3) Los atributos se componen de la siguiente manera:
- nombre : tipo
El signo (-) define un atributo oculto, o sea, no puede ser visto desde afuera de la clase.
4) Los métodos se componen de la siguiente manera:
+ nombre ( nombreArg : tipoArg, … ) : tipoDeRetorno
5) Toda clase tiene al menos, 1 (uno) constructor y el mismo es otro método más.
6) Para acceder a los atributos ocultos de una clase se utilizan métodos especiales denominados getter y setter.
Ilustración 4 - Clases y Relaciones
Mascota es un atributo de la clase persona, pero por convención no se coloca éste atributo en persona, por lo tanto, se lo referencia desde la clase de origen a destino.
Al principio se dio que la especificación decía que la persona puede tener un perro. Pero también cabe la posibilidad de que tenga un gato. Entonces, tanto perro como gato tienen cosas en común y se hizo lo que se llama, una generalización de estas dos clases hacia una clase base llamada mascota. Entonces se puede decir que una persona tiene mascotas.
-
La Plataforma JAVA.
Por definición, Java es: Una plataforma de desarrollo de software desarrollada por la empresa SUN Microsystems.
Java como plataforma de desarrollo tiene 3 (tres) componentes principales:
1) Un Lenguaje de Programación Orientado a Objetos, llamado JAVA.
2) Una API, o sea, una librería de clases de Java hechas justamente en Java. Esta API esta compuesta por clases de bajo nivel (JNI) y de alto nivel. La JNI (Java Native Interface) es una API por si misma cuya función es poder acceder a la API de la plataforma base, o sea, el Sistema Operativo y el Hardware donde esta corriendo la JVM (Java Virtual Machine).
3) JVM. La maquina virtual de Java permite ejecutar las aplicaciones empleando el lenguaje java y las API’s a disposición.
-
Versiones de la Plataforma JAVA.
Desde el punto de vista de plataforma, existen 3 versiones:
- JAVA SE: Java Standar Edition. Es una plataforma pensada para desarrollar aplicaciones de escritorio, simples dentro de todo.
- JAVA EE: Java Enterprise Edition. Es la plataforma de Java pensada para el desarrollo de aplicaciones que brinden servicios (Servidores).
- JAVA ME: Java Micro Edition. Plataforma de Java muy reducida, pensada para el desarrollo de aplicaciones para dispositivos móviles, como por ejemplo, celulares, Palms, etc.
Ilustración 5 - Arquitectura C/S y Plataformas JAVA
Para cada plataforma definida, el fabricante deja a disposición para bajar, los siguientes componentes:
- JRE: Java Runtime Enviroment. Este esta compuesto por la JVM y la API de Java. Este componente de la plataforma esta pensado para ejecutar aplicaciones Java.
- JDK o SDK: Java Development Kit o Software y el mismo se compone de la JRE, el compilador de Java y un conjunto de herramientas más.
Es importante remarcar que ninguno de estos componentes incluye un IDE.
Con respecto a las versiones de Java, la mismas iniciaron con la 1.0, la cual tenia ciertos componentes de la plataforma NO-OO. Cuando este inconveniente fue arreglado, se lanzo la versión 1.1.
A partir de la versión 1.2, se modifico la API de Java y también el lenguaje (su gramática). Desde este punto, se dice que ocurrió un sismo y a partir de aquí, se comenzó a llamar a la plataforma Java con la numeración 2 (dos), o sea, JAVA 2 y así comenzó esta nueva numeración donde:
Java 1.3 --> Java 3
Java 1.4 --> Java 4
Y así sucesivamente.
-
Modos de Ejecución de JAVA.
La plataforma Java tiene un modelo de ejecución cuyas características son: compilado, interpretado y multiplataforma.
1) Compilado: Aplica el tradicional proceso de compilación: código fuente --> código objeto --> linker --> archivo ejecutable.
2) Interpretado: Al igual que el código HTML que leen todos los navegadores Web y lo muestran de la misma forma, sin importar la plataforma. El código del archivo ejecutable es parseado y se lo enviar a la JVM.
3) Multiplataforma: Toda aplicación Java se puede ejecutar en cualquier plataforma nativa (Sistema Operativo + Hardware Base) gracias a la Maquina Virtual de Java (JVM).
Gracias a la interpretación y la JVM, Java se vuelve multiplataforma y por medio de la compilación, puesto que cualquier lenguaje interpretado conlleva de por si una menor performance al momento de la ejecución, posee una performance de calidad.
Ilustración 6 - Modelo de Ejecución de la Plataforma JAVA
La maquina virtual de Java lee el archivo de código fuente y por medio de la compilación genera código objeto llamado BYTECODE (acrónimo que denota BYTE + OPCODE), el cual es el equivalente al código objeto generado por un compilador de C.
Debido a que la interpretación es generalmente más lenta que la compilación, se han desarrollado varios métodos para poder acelerar el proceso de interpretación del código objeto a fin de aumentar la performance. Uno de estos métodos es el denominado JIT (Just In Time). El cual consiste en leer el código mientra se va ejecutando y usa probabilidades para determinar segmentos de código que se repite y que son de cierta complejidad, entonces, estos segmentos de código ya pueden ser preformados y enviados directamente a la JVM. Este proceso es implementado por la JVM HOTSPOT (puntos-calientes).
-
Código Administrado.
El modelo de ejecución que adopta la plataforma Java es lo que se denomina de código administrado y se lo llama así por que durante el proceso de ejecución existe una entidad quien es la encargada de administrar la ejecución de las aplicaciones. En este caso, este administrador es la JVM. Digamos que la JVM, que es un software que se encuentra por encima del SW base (HW + SO), y es quien ejecuta el código (BYTECODE) que se genera del código fuente. Todo esto implica que existe un control mayor que el que provee el SW de base, por ejemplo el multithreading es mucho más poderoso. Otra arquitectura que es de código administrado es el .NET Framework de Microsoft.
-
Características del Lenguaje JAVA
El lenguaje JAVA es un lenguaje puro de Orientación a Objetos. En el mundo OO existen clasificaciones de lenguajes: lenguajes puros y lenguajes híbridos. Por ejemplo, lenguajes en un principio estructurados fueron extendidos para acercare a la orientación a objetos. Esto es lo que se dice un lenguaje híbrido, por lo tanto existen mayores probabilidades de que se introduzcan componentes que no sean OO y por lo tanto pueden traer problemas (C++, Object-Pascal-Delphi).
Lenguaje puro OO es todo aquel nacido bajo el paradigma de la OO, no tiene nada funcional. El más puro es SMALTALK. JAVA es un lenguaje 99,99% OO, así que se considera como puro. No tiene variables, funciones, etc.
La entidad principal que tiene el lenguaje para poder decir: desarrollo, es el concepto de CLASE (+Interfase). Son dos formas de generar código. Por el momento nos enfocaremos en las clases. En la clase colocamos todos los elementos que va a tener nuestro programa y estos elementos son atributos y métodos. Conceptualmente, un programa de JAVA es un conjunto de clases (muchas). La idea central es que todo esta encapsulado en clases, tanto los atributos como los métodos. Una clase puede tener atributos que apuntan a otras clases, esto permite la comunicación entre ellas, o sea, que los objetos instanciados a partir de ellas se comuniquen.
-
ENTRY-POINT
Existe una clase especial que tiene un método especial, el cual se denomina ENTRY-POINT (punto de entrada) del sistema, que se llama main(). Cuando empieza a ejecutarse el SW OO, lo primero que se ejecuta es este método main(). Esta es la primera línea de código que ejecuta la JVM.
El código de un programa JAVA es un conjunto de archivos con extensión JAVA (algo.java). Este código contiene la definición de un conjunto de clases, al menos una. Ahora, cuando se compila este archivo, por cada clase dentro del código fuente, se genera un archivo extensión class (es un código binario), esto es el bytecode. Esto ayuda a decidir como organizar el código JAVA. Siempre es mejor separar cada clase en un archivo de código fuente distinto.
-
Ejecutable
El ejecutable es el conjunto de archivos de extensión .class. No existe un archivo EXE. Lo importante es que alguno de estos archivos .class tiene el ENTRY-POINT. Solo uno de estos archivos tiene un punto de entrada y es este archivo el que se tiene que informar a quien ejecuta el programa (JVM) que tiene el punto de entrada.
Para facilitar la distribución del programa hecho en java, se tomo el enfoque de compactarlos en un solo archivo (.JAR, una especie de archivo comprimido), se le dan una seria de propiedades especiales y se lo entrega a la JVM junto con la indicación de ejecutar determinado archivo .class que contiene el punto de entrada. Una de las características más importantes de este empaquetamiento es que se incluye junto con todo los archivos .class, un archivo de texto llamada manifiesto, el cual determina (de una determinada manera y entendible para la maquina virtual) cual de los archivos que están empaquetados es quien contiene el ENTRY-POINT.
Todo hasta aquí, es código “mío”, pero este código también tiene que llamar a la API de java, la cual es también un conjunto de archivos .class, o sea, clases. Estos viven en otro tipo de JAR, llamado RT.JAR (Run-Time.JAR).
-
Java Native Interface
Tanto, las clases que uno desarrolla, como las que están en la API de JAVA, tienen el mismo camino de compilación, empaquetamiento, etc. Pero también, es cierto, que se necesita salir fuera del ámbito de JAVA para poder comunicarse con el entorno del SO, funciones de librerías de la plataforma nativa. El JNI (java Native Interface) es quien pide servicios a la plataforma nativa (sistema operativo). Por ejemplo, para dibujar algo en la pantalla necesito ingresar a la API de Windows. La API de Java, si bien son archivos .class, por dentro necesitan una forma de escapar de la JVM, entonces aquí se puede decir que se viola el código administrado, por que así se violan cosas que son puramente de la API de java. Entones si algo falla tratando de usar la API de la plataforma nativa, es un error del programador, NO de la maquina virtual. La API es dependiente de la plataforma, por lo tanto, cuando uno baja el JRE, en realidad baja la JVM y la API para comunicarse con una determinada plataforma. Por fuera, la cáscara de la API cambia por plataforma, pero por dentro es distinta en cada plataforma.
Ilustración 7 - Mapa del Lenguaje JAVA
-
Primer Programa en JAVA.
Archivo: HolaMundo.java
public class HolaMundo{
// la firma del método main() tiene unas características especiales.
public static void main(String[] args){
System.out.println(“Hola, Mundo!!!”);
}
}
El nombre del archivo .java debe ser idéntico al nombre de la clase que contiene el entry-point. Ahora que tenemos el archivo, lo compilamos. El compilador en Windows se llama javac.exe y en la línea de comando deberíamos hacer:
c:\javac.exe “HolaMundo.java”
Para ejecutarlo, en la misma línea de comandos debemos hacer:
c:\java.exe “HolaMundo”
-
Variable de Entorno CLASSPATH
El System.out.println es parte de la API y el java.exe, no sabe nada sobre como interpretarla. Entonces existe una variable de entorno, llamada CLASPATH, que tiene de minina, donde vive la API de java y se debe configurar para que apunte al directorio de la API, por ejemplo:
CLASSPATH = C:\[home java]\JRE\Lib\RT.jar;.
Como se le esta pasando un archivo, esto implica que dentro de este archivo esta la API y es aquí donde el compilador debería buscar el System.out.println y si no se encuentra aquí, el punto (.) significa que busque en el directorio actual, esto también puede ser modificado, pero es aquí donde primero busca, en el CLASSPATH (directorio de las clases). En el directorio BIN vive el JVM.
Cuando uno quiere ejecutar aplicación en el escritorio, debe configurar el archivo dentro del directorio CLIENT de JVM por que esta optimizado para este tipo de aplicaciones. Si uno quiere implementar una aplicación que brinde servicios, debería usar el la carpeta SERVER.
-
Clases
Una clase encapsula un conjunto de atributos (el estado de la clase) y un conjunto de métodos (el comportamiento). Al momento de definir una clase, hay que preguntarse:
1) ¿Que conjunto de atributos definen mi clase?
2) ¿Que conjunto de métodos definen mi clase?
Este es un proceso cíclico que puede volver sobre si, y se lo denomina refractorización, por que la clase original puede ser modificada. Todo Desarrollo OO es iterativo por naturaleza. Análisis-diseño-programación, y nuevamente el mismo ciclo hasta llegar a un producto final.
Ilustración 8 - Clase Persona
Si tomamos el ejemplo de la persona que puede leer. Leer es una especificación de lo que una persona tiene que hacer, siempre dentro del dominio del problema. El leer implica que debe existir algo que sea legible, por lo tanto este es un argumento más. También se puede plantear que la persona cumpleaños y que se debe saber su edad.
En un principio, el nombre y la edad son atributos privados, por lo tanto, se requieren métodos especiales, setter y getter, que me permita modificar estos atributos.
En Java, el código seria algo así:
public class Persona{
private String nombre;
private int edad;
*public Persona(String pNombre, int pEdad){*
*nombre \= pNombre;*
*edad \= pEdad;*
*}*
*public void leer( Leer pLibro){*
*//algo*
*}*
*public void cumplirAños(){*
*edad\++;*
*}*
*public void setNombre(String pNombre){*
*nombre \= pNombre;*
*}*
*public String getNombre(){*
*return nombre;*
*}* *}*
Ya mostramos como es gráficamente una clase, por medio de un diagrama UML de clases y su equivalente en código Java. Pero también existe la forma de mostrar, también con un diagrama UML, como es un objeto instanciado a partir de una determinada clase:
Ilustración 9 - Objeto Instanciado a partir de la Clase Persona
Para instanciar objetos, en java se utiliza la palabra reservada new y se hace de la siguiente manera:
new Persona (“Juan”, 32);
Cuando uno instancia un objeto a partir de una clase, lo que se hace es reservar y ocupar una determinada cantidad de memoria en el heap. Si bien ya tenemos al objeto en la memoria, ahora nos hace falta un mecanismo que nos permita identificar a un determinado objeto de entre el conjunto que vive en el heap. Esto es lo que llamamos una Referencia. Esta referencia, es en realidad, un elemento dentro del código que me permite identificar a un objeto, ver su interfaz y así poder enviarle mensajes.
A las referencias también se las conoce como handlers. Es una especie de analogía a los punteros en C.
Ilustración 10 - Objeto Instanciado y referenciado
La referencia en si, NO es un objeto, es algo que me permite acceder a un objeto, por ejemplo en el siguiente código Java, P1 es la referencia que me permite acceder al objeto instanciado a partir de la clase persona.
Persona P1;
P1 = new Persona (“Juan”, 32);
P1.cumplirAños();
-
El Constructor.
El constructor es un método especial y como cualquier método puede estar sobrecargado. Java permite no poner un constructor y en este caso Java incluye uno por default.
-
¿Cómo se destruyen los Objetos? {#¿cómo-se-destruyen-los-objetos?}
1) Destrucción Explicita --> No en JAVA.
2) Destrucción Implícita --> Si en JAVA.
En el primero de los casos, es el programador quien decide cuando destruir un objeto. Por ejemplo el comando free en C. Esta forma de liberar memoria tiene sus complicaciones, por que por ejemplo, pueden existir otras referencias en el programa hacia el objeto que se esta liberando y estarían quedando referencias no validas, o sea, hacia direcciones con basura o con datos de otros programas.
En el segundo caso, la única forma de que un objeto quede sin referencias es asignarle null. Luego de esta asignación, el objeto queda perdido en la memoria sin posibilidad de poder volver a acceder a él. Para poder liberar esta memoria ocupada por un objeto sin referencia, Java implementa lo que llama un garbage collector, quien tiene la función de recorrer el heap en buscar de objetos que no tengan referencias que apunten a ellos. Para esto, cada objeto implementa una suerte de contador, quien determina la cantidad de referencias hacia el objeto. El garbage collector, en caso de encontrar objetos sin referencias, lo coloca en una lista de objetos candidatos a ser destruidos. Quien implementa todas estas lógicas/procedimientos es la Java Virtual Machina, o sea, es la JVM quien decide el momento adecuado para poder llamar al Garbage Collector y esta es otra ventaja de la metodología de código administrado.
Entonces, si bien pueden quedar objetos sin referencias, aun estos existen en la memoria hasta el momento en que es llamado el garbage collector o en el caso extremo, que se finalice la ejecución del programa.
Hay que mencionar que en Java existe un método para destruir objetos Explicitamente, este método se llama finalize(). La idea de este método es que el código que lo compone se ejecute instantes antes de que el Garbada Collector destruya el objeto. La idea de este método es que se utilice cuando existen objetos que contengan información delicada o por ejemplo, implementen conexiones a base de datos, periféricos, etc. Es una Forma prolija de destruir determinados objetos. O sea, que se debe utilizar cuando existen recursos especiales, reservados.
-
Paradigma: Paquetes.
Un paquete en Java, es un conjunto de clases cooperando entre sí. Una clase puede pertenecer si y solo si a un determinado paquete. Esto produce que la clases pertenecientes a un determinado paquete sean cooperantes entre se (friendly). Para indicar que una clase pertenece un paquete en particular, en Java se hace:
Ilustración 11 - Archivos fuentes y Paquetes
Si bien ambos paquetes fueron definidos en archivos fuente distintos, pertenecen o forman parte del mismo paquete general. Gráficamente seria:
Ilustración 12 - Paquete que contiene paquetes y clases
En sí, el paquete Animales es el mismo para todas las clases, lo que varia en realidad es la forma en que se agrupan los sub-paquetes. Esta agrupación permite mayor prolijidad. Por ejemplo, dentro del paquete Animales, podemos caracterizar a los herbívoros, carnívoros, etc.
-
Tipos de Datos en Java
En Java existen 3 categorías de datos. Cada tipo de datos es independiente de su representación y permiten almacenar distintos tipos de información.
-
Tipos Primitivos.
Son tipos de datos que permiten almacenar valores atómicos. Un valor atómico es un número, letra, valor booleano, etc. Las cadenas NO son tipo primitivos. Esto hace que Java no sea 100% puro OO.
-
Las Referencias.
Las referencias tienen un tipo y son “EL” elemento en el lenguaje Java que permite acceder al objeto (concepto genérico en realidad). El tipo de la referencia debe coincidir con al menos uno de los tipos del objeto al que apunta. Si esto no se cumple, hay un error. Esto es por que Java es un lenguaje fuertemente tipado. Existen lenguajes como visual fox, que es un lenguaje débilmente tipado, o sea, que una referencia de cualquier tipo puede apuntar a un objeto de cualquier tipo. En Java si o si deben coincidir.
-
Los Objetos.
Son la entidad que posee un estado (conjunto de atributos que determinan su estado) y un comportamiento (se le puede enviar mensajes para invocar un método)
NUNCA COMO PROGRAMADORES TRABAJAMOS DIRECTAMENTE CON EL OBJETO.
Los atributos del objeto son básicamente de dos tipos: primitivos o referencias. Todas las asociaciones entre clases se mapean en tipos referencias. Un control remoto tiene un atributo referencia a televisor.
La referencia se representa con un rectángulo y los tipos de datos primitivos también se representan con un rectángulo.
Ilustración 13 - Representación de Tipos primitivos y Referencias
Los objetos se representan con círculo en donde se listan los atributos que posee con sus valores. Es en esta representación donde pueden aparecer atributos que sean referencias.
Ilustración 14 - Representación de Objeto Referenciado
Siempre se habla en términos de referencias, nunca se puede decir que se trabaja directamente con objetos. Los objetos no se pueden alcanzar de manera directa.
Ilustración 15 - Referencia hacia un Objeto
-
Modelo de Memoria de Java
Java presenta conceptualmente 3 modelos de memoria (siempre administrado por el JVM):
-
Área de Código/Datos. {#área-de-código/datos.}
Es el lugar donde se almacena las clases. Primero se guarda el témplate de la clase, o sea, cuando se quiere instancia un objeto de la clase, este témplate es la guía de pasos a realizar para poder generar un objeto. Entonces, con definición de clase nos referimos a la lista de atributos y los métodos (ya compilados) a BYTECODE. La diferencia con C, es que esta área de código es dinámica.
En el área de datos van los atributos de clase, que son una artificio de java para poder tener variables globales: Clases + Definición + Atributos de Clase.
-
Stack.
En el Stack se guardan: variables locales + parámetros + registros varios + variables de retorno.
En Java la unidad ejecutable son los métodos, al igual que el modelo de ejecución que se tiene en C. Con la diferencia que dentro del modelo de ejecución de stack además se debe incluir la referencia hacia el objeto a cual pertenece. En el Stack es donde se realiza la Ejecución de Métodos.
Cuando se invoca un método, el mismo es cargado en el stack, junto con la información necesaria para saber, por ejemplo, el Objeto al cual pertenece.
La forma en que los métodos son cargados en el stack determina la secuencia de ejecución de los mismos.
Ilustración 16 - Modelo de ejecución de stack
-
El Heap.
En C, el heap se utiliza para el trabajo con memoria dinámica, o sea, memoria que no restringida por la ejecución de una determinada función. En el heap, solo viven los objetos y su estado, debido a que el código de los métodos vive en el área de código/datos y el modelo de ejecución de los métodos es igual a las funciones en C. Además, se incluye en el objeto información acerca de su estado y una especie de referencia hacia el área de código donde esta el código fuente de los métodos.
Se puede decir entonces que los objetos viven en el heap, junto con sus atributos y estos atributos pueden ser de dos tipos, primitivos o referencias. Y se puede dar si es un tipo primitivo este en el heap o en el Stack si se encuentra dentro del modelo de ejecución de un determinado método. Y si se encuentra una referencia en algún lado, se puede decir que la misma o esta en el Stack o dentro de un determinado objeto.
-
Alcance o Scope.
-
Alcance y Perdurabilidad de los Tipos Primitivos / Referencias {#alcance-y-perdurabilidad-de-los-tipos-primitivos-/-referencias}
-
Los tipos primitivos y/o referencias que pertenecen a un método, tiene una vida que es propia del modelo de ejecución del Stack. Son accesibles, siempre y cuando lo que se este ejecutando sea ese método. Las variable locales o referencias solo son accesible mientras se este ejecutando ese método. Scope del Stack.
Los tipos primitivos y referencias que pertenecen a un objeto, si son propios del objeto, NO de los métodos, son accesibles siempre y cuando se pueda acceder a un objeto. También depende del nivel de acceso que se tenga a un determinado atributo.
El alcance de los tipos primitivos/referencias de un objetos tienen el tiempo de vida de el objeto en si. Suponiendo el caso de que un objeto muera y el mismo tenga referencias a otros objetos, los cuales el contador llego a CERO, produce una reacción en cadena.
-
Alcance y Perdurabilidad de los Objetos.
Siempre que tenga una referencia a un objeto, el objeto vive.
-
Como se obtienen Referencias a Los Objetos? {#como-se-obtienen-referencias-a-los-objetos?}
Cuando uno crea un objeto, se debe agarrar (apuntar) con una referencia. El ámbito de creación de un objeto, trasciende el ámbito de Instanciación de un objeto. O sea, que un objeto puede ser apuntado por referencias, que desde un principio llamaban a un método que recién era este el que creaba el objeto en si. Y Finalmente, este objeto puede ser referenciado por alguien que desde un principio no sabia que existía en un principio.
El objeto Trasciende su ámbito de Instanciación.
UnTipo unaReferencia;
unaReferencia = new UnTipo()
t = unaReferencia;
El pasaje de parámetros de java es por valor.
Entonces se pueden tener miles de objetos, y en el Stack solo tener 3 métodos y esto se dio por que en distintas instancias y tiempos se crearon objetos y referenciados de distintas maneras.
-
Tipos en Java.
-
Tipos Primitivos.
-
Dos cosas son importantes para recordar acerca de los tipo primitivos en java, la primera es que todos los tipos primitivos siempre tienen signo, o sea, no existe el unsigned y lo segundo para recordar es que el tamaño de cualquier tipo primitivo en Java siempre es el mismo, debido a que es la JVM quien administra esto y la misma es multiplataforma. En la ultima columna se específica lo que se denomina Wrapper Type, cuya interpretación al castellano seria Tipo Englobado, en el sentido de un encapsulamiento. Es necesaria la utilización de Wrapper, o sea, clases que encapsulan clases primitivas para determinadas tareas.
Para recordar: No existe el SIZEOF, por que no tiene sentido.
Ilustración 17 - Tipos Primitivos en Java
Otra cosa importante para recordar, también sobre los tipos primitivos en Java, es que siempre vienen con un valor por default que Java garantiza que poseerá una variable dentro de una clase.
Ilustración 18 - Valores por Default de Tipos Primitivos en Java
-
Referencias.
Las referencias tienen un tipo, el nombre de la clase. El tipo de una referencia es único y solo tienen uno de dos posibles estados, o apunta o no apunta a un objeto. Se puede decir que una referencia apunte a nada, a null. Esto hace que la referencia exista pero no apunte a nada, entonces para decir que una referencia deje de apuntar a un determinado objeto, simplemente le asigno null.
Ilustración 19 - Referencias y Objetos que Referencian
-
Objetos.
Dijimos que los objetos son polimórficos, por que por lo menos tiene un tipo. Y Toda la herencia correspondiente, hasta llegar a la herencia única. También sucede que un referencia solo apunta a un tipo de objeto. Entonces, siempre que se pregunte el tipo de un objeto, se debe responder depende, por que puede tener muchos tipos. Entonces, siempre, por lo común, cuando se pregunta el tipo de un objeto, se refiere a la clase de más baja jerarquía de la que fue Instanciado, a esto se lo llama tipo real.
-
Paradigma
-
IDENTIDAD vs IGUALDAD.
-
Dos objetos son iguales si su estado es el mismo, dos objetos son idénticos si son realmente el mismo objeto, en este caso lo que sucede es que al comparar dos referencias, se comprueba que ambas referencian al mismo objeto.
Ilustración 20 - Identidad e igualdad
- P es Igual a Q:
Son objetos diferentes, pero el estado es el mismo.
- R es idéntico a T:
El objeto al que apunta R es el mismo al que apunta T.
- R igual a T
Si, son el mismo objeto en si.
La identidad implica igualdad
No el caso inverso, o sea la igualdad no implica identidad necesariamente.
-
Herencia.
La relación de herencia se especifica en el momento de definir la clase derivada.
La herencia puede ser, por un lado:
- Múltiple: al momento de aplicar el mecanismo de herencia a la clase derivada, se pueden especificar más de un padre.
- Simple: siempre se aplica el mecanismo sobre un solo padre (este es el caso de java).
Por otro lado, puede ser:
- Mono-Raíz: todas las clases, de una u otra manera, derivan de una misma raíz, por ejemplo: object (Java). Esto es una ventaja, por que si desconozco el tipo del objeto, al menos lo puedo agarrar con un handler de tipo object.
- Multi-Raíz: En lugar de derivar de un solo objeto, tengo un conjunto de clases predefinidas de clases tipo object y se puede definir objetos en cualquiera de estas raíces.
-
Sin Raíz: una clase solo hereda de su propia clase definida, o sea, class perro {}, es solo de clase perro y nada más.
-
Aplicaciones de la Herencia.
-
El mecanismo básicamente permite realizar dos actividades, o sea, ¿para que aplico herencia?:
Mecanismo de Especialización.
Por ejemplo, tengo la clase animal, esta es demasiado genérica. Entonces, defino la clase perro que extienda de animal y la especializo para que ese animal se convierta en perro. La especialización es decir, hago herencia hacia abajo.
Mecanismo de Generalización.
Es el proceso inverso al de especialización. Entonces, la herencia se puede dar, ya sea por especialización o generalización.
El hecho de que perro herede de animal, crea la frase: perro es un tipo de animal. Las clases derivadas son un tipo de las clases padres. Tiene que ver, con que la clase derivada posee un conjunto de tipos.
Cuando se hereda, se hereda el conjunto de atributos de la clase padre y se hereda el conjunto de métodos de la clase padre y cuando se aplica la herencia, se arma una jerarquía de clases que en este caso es un árbol de clases con una raíz central que se llama object. Esto es en el caso de Java, en otros lenguajes se crea una jerarquía de clases y deberá estudiarse si esta jerarquía es un grafo o alguna otra topología de organización.
Por ejemplo: Queremos representar Perros y Gatos y todas las jerarquías que esto conlleve.
Ilustración 21 - Diagrama de Herencia (Generalización)
Cualquier objeto que sea animal, sabemos que puede caminar o correr.
Por ejemplo, la interfaz de la clase Labrador, seria: cazar(), ladrar(), caminar(), correr().
Supongamos que instanciamos un objeto del tipo labrador:
new Labrador();
Dado un objeto que es instanciado en base a un tipo, los tipos que posee ese objeto, son todas las jerarquías hacia arriba que posee esa instanciación. Esto también incluye decir, en JAVA que es de tipo object.
El labrados ES un Labrador, ES un perro, ES un animal y ES object.
En código JAVA, el ejemplo seria: Si animal no extiende de nada, automáticamente extiende de object.
abstract class Animal{
protected String nombre;
protected int edad;
*public void caminar() { /\* código \*/ }*
*public void correr() { /\* código \*/ }* *}*
class Perro extend Animal{
int volumenLadrido;
*void ladrar(){ /\* código \*/ }* *}*
class Labrador extend Perro{
// nueva implementación del método ladrar
void ladrar(){ /* código */ }
void cazar(){ /* código */ }
}
-
Overriding (redefinición) y Overloading (sobrecarga) {#overriding-(redefinición)-y-overloading-(sobrecarga)}
Los métodos y atributos que se definen en clases superiores (en los diagramas UML), en caso de herencias, NO se deben volver a copiar en los diagramas de más baja jerarquía, salvo en el siguiente caso: Supongamos que el labrador no ladra como un perro genérico. La implementación de ladrar en Labrador no nos satisface, por lo tanto necesitamos otra implementación. Cuando queremos modificar la implementación de un determinado método hacemos lo que se denomina redefinición (overriding). Esto NO es lo mismo que la sobrecarga de métodos (overloading). Una redefinición es sobre una misma firma de método de una clase derivada, se cambia la implementación. Esto exige necesariamente que haya herencia, la sobrecarga NO necesariamente necesita la Herencia. Entonces, cuando debemos redefinir un método que es heredado de una clase superior, lo que hacemos es volver a escribir la firma del método a redefinir en el cuadro de clases UML. Esto queda implícito cuando se lee, o sea, si uno vuelve a ver el mismo método escrito en una subclase, es por que se tiene que redefinir.
Ahora, suponemos que agregamos un parámetro al método ladrar(int tono), aquí estamos en presencia de una sobrecarga. Esto quiere decir que la sobrecarga puede ocurrir en cualquier escala de la jerarquía de clases.
En la sobrecarga, cuando se preguntan cuantos métodos hay, son tantos como métodos se hayan modificado, en cambio en la redefinición, solo existe un método que es implementado de distintas maneras.
JAVA también soporta que se realice overriding de atributos, aunque desde el punto de vista del paradigma, no lo debería permitir.
-
this – super {#this-–-super}
-
this
-
Existen dos referencias implícitas dentro del lenguaje JAVA llamadas: this y super. This se utiliza para referirse dentro de la instancia de una objeto a cualquier atributo o método de esa instancia.
Supongamos que implementamos el método caza() de Labrador.
void cazar(){
int volumenLadrido;
volumenLadrido = 3;
System.out.println(volumenLadrido);
}
Cuando se quiere hacer referencia al atributo heredado o creado fuera del scope del método, si se llama igual, entonces utilizo dentro del método el operador this para hacer referencia al objeto y al atributo de ese objeto, NO del método.
void cazar(){
int volumenLadrido;
this.volumenLadrido = 3;
System.out.println(volumenLadrido);
}
Con el this, se hace hincapié en que se trata de un atributo propio del objeto y que es un atributo o variable propio de un determinado método.
El operador this es una referencia a mi mismo. Es una referencia.
-
super
Este operador trabaja sobre un método heredado, donde, si bien el método es correcto, necesito de aumentarle capacidades, NO se habla de redefinirlo, sino de aumentarle las capacidades que se están heredados. Esta es una pseudo-referencia. Super es una referencia a mi mismo, pero que indica que quiero acceder a la implementación de mi padre.
void ladrar(){
super.ladrar();
/*
* nueva funcionalidades que se agregan a ladrar de Perro.
*
*/
}
void cazar(){
int volumenLadrido;
this.volumenLadrido = 3;
System.out.println(volumenLadrido);
}
Con super, solo se puede acceder a la implementación de la clase padre directa. Con super, se dice: anda a la implantación más inmediata que define ese método.
El super-abuelo, delega la correcta implementación de las redefiniciones al programador.
En el caso de que fuera el constructor, no podemos poner super.constructor, lo que hacemos es poner super(…) y entre paréntesis todos lo parámetros que reciba el constructor del padre directo. Es como si fuera la llamada a mi padre directo.
Cuando no se ponen los constructores, lo que Java hace es colocar constructores por default y este, si estamos en la jerarquía mas baja, debe llamar al constructor (implícitamente) del padre.
Class Aminal
{
public Animal()
{
/* Algún código genérico */
}
}
class Perro()
{
// Llama a la implementación de la clase padre “Animal”
Super();
}
classLabrador()
{
// Llama a la implementación de “perro”
Super();
}
-
Calificadores / Modificadores de Acceso. {#calificadores-/-modificadores-de-acceso.}
Para cualquier miembro de una clases, uno pude indicar el tipo de acceso que se tiene a ese miembro y básicamente existen 4 calificadores de acceso.
- Private: Significa que es accesible solo desde el interior de la clase donde es definido. Ni los hijos, ni del exterior se puede acceder. Aplicable tanto a atributos como métodos.
- Protected: sólo es accesible desde las subclases derivadas. Es accesible vía herencia, pero esta oculto para el exterior.
- Friendly: Hacia el exterior se comporta como “public”, pero para las clases que pertenecen al mismo paquete de donde pertenece la clase donde se define el friendly, es visible. En realidad no existe el keyword friendly, este viene por default cuando no se coloca un modificar de acceso al atributo/método.
-
Public: Es accesible a cualquiera. Tanto en la herencia como desde fuera de la misma clase o paquete.
-
Regla de Oro.
-
En un atributos, uno puede decir que el mismo es friendly o public. Pero esto no es bueno, por que si hago esto, estoy haciendo una exposición hacia el exterior de la implementación concreta del estado de un objeto. Desde el punto de vista del control y encapsulamiento, solo debería ser controlado por una clase y cualquier cambio del objeto debería ser solo posible desde un mensaje. No se pueden implementar reglas de control en cuanto al estado. TODOS los atributos deben ser ocultos para el exterior, para que la implementación del estado de un objeto esté oculta.
Vía el uso de métodos, que si son públicos, se debe tener acceso a los atributos de un objeto, esto son los llamados getters y setters. Esto permite incluir reglas de control al estado del objeto. El acceso al estado puede no ser igual a la implementación del estado de un objeto, por lo tanto aquí se ve claro que queda separada la implementación del acceso a estado interno del objeto.
Los mismos modificadores de acceso pueden ser clasificados para las clases. Esto es:
- public class: Desde cualquier lugar se puede instanciar un objeto a partir de esa clase, ya sea, dentro del paquete o no.
- “friendly” class: En esta clase, un objeto es solo instanciable desde clases que pertenezcan al mismo paquete.
- protected class: No tiene sentido.
- private class: Esto NO se puede decir, sin embargo, existe una forma de definir una clase de modo tal que sea accesible, solo y solamente, desde otra clase. Esta se llama, clase interna.
Miembro | clase | |
---|---|---|
Private | Ok | (implícito = clase interna) |
Protected | Ok | NO |
Friendly | (implícito) | (implícito) |
Public | Ok | Ok |
-
Calificadores de Pertenencia.
Se aplican solo a los miembros de una clase, es decir, atributos o métodos. Existen dos tipos:
-
De Instancia.
- Si es un atributo de instancia, lo que significa es que ese atributo es accesible y pertenece a la instancia, es decir al objeto instanciado. Cada instancia, tiene su versión del atributo.
-
Si es un método de instancia, es exactamente lo mismo. Cuando invocamos ese método, este se invoca para el objeto de la instancia que lo esta invocando. Estos son los que reciben el parámetro explicito this.
-
De Clase (metadatos). {#de-clase-(metadatos).}
-
- Atributo de clase, es un atributo que pertenece a la clase, esto significa que ese atributo no pertenece a ninguna instancia en particular, o sea, esta asociado a la clase (al template), y todos los objetos instanciados de esa clase, pueden acceder a ese atributo, pero si cambian su valor, están tratando con un mismo espacio de memoria que es accesible por distintos objetos y además se puede acceder a ese atributo, aun si no existen instancias de esa clase.
- Método de clase, es lo mismo que el atributo de clase. Pertenece a la clase. La diferencia es que este método no esta asociado a ninguna instancia en particular, no se le puede asociar el this, es lo más parecido a una función común y corriente. No esta en el contexto de ninguna instancia. Al no recibir el this no se puede acceder a los atributos de instancia o métodos de instancia.
Los métodos de clases sirven para tener funciones al mejor estilo de C, por que no están asociados a instancias. Esto es por que estas funciones no están asociadas a ningún objeto.
Esto justifica que: una cosa es objeto y otra cosa es instancia, instancia en verdad es un objeto que se crea a partir de una clase, en cambio un objeto es algo que posee atributos y métodos. La clase, en realidad esta VIVA como un objeto, por ejemplo, existe una clase que se llama CLASS, vía esta clase, se le puede preguntar a una clase cualquiera como esta compuesta.
Por cada clase que se levanta de la JVM, se crea una instancia a partir de CLASS de esa clase.
Para que un miembro (en java) sea de clase, se usa la palabra reservada static, por ejemplo, si queremos que correr() sea un método de clase de animal, quedaría: public static void correr()
Ilustración 22 - Diagrama Calificadores de Pertenencia
-
Clases y Métodos Abstractos.
-
Clase Abstracta.
-
Es aquella que no puede instanciar objetos a partir de ella. Existen ciertas clases que son tan genéricas (dentro del contexto) que no tiene sentido hacer instancias de ellas. No tiene sentido tener objetos de este tipo de clases, son muy abstractas. Esto impide que se realicen new de estas clases, por ejemplo, Animal.
También se puede dar que las subclases también sean abstractas, por ejemplo, Perro, estas también pueden ser abstractas.
-
Método Abstracto.
Es un método del cual no poseo una implementación, sino que poseo solo su firma. Cualquier clase que contenga al menos 1 método abstracto, fuerza a que la clase se convierta a abstracta.
En Java se utiliza, para ser abstracto algo, el keyword: abstract.
Abstract class Animal
{
protected String nombre;
protected int edad;
*public abstract void caminar();*
*public abstract void correr();* *}*
abstract class Perro extend Animal{
int volumenLadrido;
*void ladrar(){ /\* código \*/ }* *}*
class Labrador extend Perro{
void caminar(){ /* código */}
void correr(){ /* código */ }
void cazar(){ /* código */ }
}
Si esto se realiza de esta manera, en el gráfico UML, se debe colocar en el diagrama de clase de Labrador los métodos que acabamos de implementar: caminar(), correr().
Ilustración 23 - Métodos abstractos
-
Clases y Miembros Finales.
Uno puede declarar que uno o todos los métodos de una clase sea final, esto indica que esos métodos o la misma clase no se pueden redefinir por subclases ni por nadie más. Este mecanismo es particularmente útil cuando se requiere proteger cierto código de modificaciones. Otra cosa a tener en cuenta es que todos los métodos que son llamados desde un constructor deberían ser finales, debido a que si una subclase llegará a redefinir uno de esos métodos, la propagación sería en cascada.
- Que una CLASE sea final, significa que esa clase NO puede tener clases derivadas, NO puede haber extend de esta clase. No quiero que se reutilice mi código.
- Un MÉTODO es final, significa que en la clase hija NO lo puedo redefinir, en toda la escala de la jerarquía.
- Un ATRIBUTO final, significa que una vez que alcanzo su primer valor (al momento de declararlo), el mismo no puede ser modificado nunca (una constante). Entonces, si el atributo es final, por lo general se dice que es de clase.
Por ejemplo (constante global):
public static final int UNA_CONSTANTE = 0;
Se utiliza la misma nomenclatura que en C. Para acceder a esta constante se hace:
Clase.UNA_CONSTANTE;
En el segmento de CÓDIGO/DATOS van todos los static.
-
Clases en UML.
Ilustración 24 - Diagrama de Clase Completo.
-
Upcasting vs Downcasting
El tipo de la referencia es uno solo, y tiene que estar incluido en los tipos del objeto al cual hace referencia dentro de la jerarquía de clases. Si pasa que la referencia no coincide con el tipo del cual fue instanciado un objeto, entonces puede haber problemas.
-
Upcasting.
El caso feliz es que el tipo de la referencia sea el mismo que el tipo del objeto. El caso en donde hay discordancia, es donde aparece el upcasting: la acción de referenciar un objeto con una referencia, cuyo tipo es de un tipo superior en la jerarquía de clases de la cual se instancia el objeto.
En el ejemplo de los Animales:
Perro unPerro; //referencia
Labrador unLabrador;
unLabrador = new Labrador();
unPerro = unLabrador; //aquí, labrador esta dentro del conjunto de tipos de perro
Depende del tipo de referencia, aun si se esta referenciando a un mismo objeto, se pueden dar interfaces diferentes. La razón de esto es que el compilador es fuertemente tipado.
Ahora, si vemos el caso en donde redefinimos el método ladrar(), la referencia unPerro ejecuta la implementación de ladrar() que corresponde a labrador.
En el primer caso, la interfaz oculta el método cazar() de la referencia unPerro, pero NO lo pierde. El objeto siempre es el mismo.
-
Downcasting.
RTTI sirve para identificar el tipo de un objeto, sirve para preguntarle: ¿dentro del conjunto de tipo de un objeto, se encuentra el tipo X?.
El downcasting es la acción de referenciar un objeto con una referencia cuyo tipo es de un tipo inferior en la jerarquía de clases del tipo de la referencia de la cual se toma el objeto.
Perro unPerro; //referencia
Labrador unLabrador;
Labrador unLabradorMas;
unLabrador = new Labrador();
unPerro = unLabrador; //aquí, labrador esta dentro del conjunto de tipos de perro
unLabradorMas = (Labrador)unPerro; // aquí, unLabradorMas es de un tipo inferior
// también se puede hacer
((Labrador)unPerro).cazar(); //SI funciona
Siempre uno debe mirar el TIPO de un objeto y luego su INTERFAZ. Una vez hecho esto, se debe analizar el upcasting y downcasting.
El upcasting es siempre seguro, por que se controla en tiempo de compilación. (Siempre se puede mirar para arriba de la jerarquía y por lo tanto siempre existe un camino)
El downcasting es más peligroso por que se tiene que mirar para arriba y para abajo, si no existe un camino posible, el compilador tira un error.
((Chihuhua)unPerro).esconderse();
Aquí se complica, por que el downcasting compila sin errores pero al momento de ejecutar el método el compilador se da cuanta de que NO existe este método en el objeto Labrador.
El RTTI tiene la capacidad de preguntar en tiempo de ejecución a un objeto, de que tipo es.
Perro unPerro;
If(getNumero_0_1() ==1)
unPerro = new Labrador;
else
unPerro = new Chihuhua;
((Labrador)unPerro).cazar();
Aquí hay una posición de incertidumbre por que NO sabemos en tiempo de ejecución que sea factible el downcasting. Se produce una excepción llamada: ClassCastException.
Ilustración 25 - Binding, Upcasting y Downcasting
-
Binding.
Es la asociación entre el mensaje que recibe un objeto y la implementación a ejecutar como respuesta a ese mensaje. Existen dos estrategias:
-
Binding Estático
La asociación entre mensaje e implementación se hace en tiempo de compilación/linqueo. (ésto es en C).
-
Binding Dinámico.
La capacidad de poder postergar esa asociación entre el mensaje y la implementación al tiempo de ejecución. Cuando estoy a punto de llamar a ese método, recién se realiza la asociación.
unPerro.ladrar();
Solo se sabrá en tiempo de ejecución. Este código compila, pero recién se sabe cuando se haga la elección entre un Labrador o Chihuhua (en el caso de que ladrar haya sido redefinida en ambas clases).
Todos los métodos se resuelven en tiempo de ejecución.
- Binding Estático: C
- Binding Dinámico: Java
- Opcional: C++
Aquí se ve el principio de Extensibilidad.
-
Polimorfismo ó Métodos Polimorficos.
Por ejemplo:
Ilustración 26 - Diagrama Clase Abstracta
En código Java seria:
abstract class Perro extend Animal{
Protected int volumenLadrido;
*Public void ladrar(){*
*System.out.println("soy un perro");*
*}* *}*
class Chihuhua extend Perro{
public void esconderse(){
/* algo */
}
*// redefinición*
*public void ladrar(){*
*System.out.println("soy un chihuhua");*
*}* *}*
class Labrador extend Perro{
public void cazar(){
/* algo */
}
*// redefinición*
*public void ladrar(){*
*System.out.println("soy un ladrador");*
*}* *}*
El mecanismo de decir: al momento de ejecutar el método asociado a un mensaje, la implementación que debo buscar, es siempre, la más cercana al tipo al cual se instanció el objeto al cual estoy apuntando, se denomina: Polimorfismo.
El polimorfismo requiere de 4(cuatro) conceptos:
1) Herencia: si no existe herencia, no estaríamos hablando de “acercarnos al tipo más superior, inferior, etc.”, no habría jerarquía de clases.
2) Redefinición de Método: ocurre en tiempo de compilación, tienen que existir las alternativas para seleccionar entre ellas.
3) Posibilidad de Hacer Upcasting: en tiempo de ejecución, mirar hacia arriba los caminos posibles.
4) Binding Dinámico: También en tiempo de ejecución. Si no existiera, no tendría la opción de buscar entre distintas implementación del mismo método.
Para que ocurra el polimorfismo, se deben cumplir estas cuatro condiciones.
-
Asociaciones.
-
Asociaciones Generales.
-
Una asociación es una relación estructural que especifica que, objetos de una clase están conectados con objetos de otra clase. Pudiendo ser la segunda, la misma que la primera.
Una asociación posee:
- Un Rol: una descripción del papel que desempeña una clase frente a la otra.
- Una Multiplicidad: La cantidad de objetos con que puede estar asociado un objeto al otro lado de la asociación.
- Dirección: unidireccional o bidireccional.
- En el caso de la unidireccionalidad: Determina quien es el responsable de implementar la asociación en el código mismo (la persona tiene un arreglo con todas las posibles empresas).
Ilustración 27 - Asociación Unidireccional
- En la bidireccional: la implementación la hacemos en las dos puntas. En ambas clases existe un arreglo, una con las personas y una con empresas.
Ilustración 28 - Asociación Bidireccional
En general, el sentido del rol es el mismo que de la dirección.
0..1 : cero o un objeto con la clase que se esta asociando.
1 : solo uno
0..* : cero o muchos objetos.
1..* : al menos uno, nunca cero.
n : con un numero fijo
0..n : cero hasta N inclusive.
1..n : uno hasta N inclusive.
-
Asociaciones Especiales.
Existen dos tipos de asociación especiales:
-
Agregación
Modela una asociación: todo-parte. En la que un lado de la asociación especifica el elemento contenedor (el todo) y el otro lado de la asociación específica los contenidos (las partes).
Ilustración 29 - Asociación de Agregación
El departamento es quien contiene los empleados, se debe poner la multiplicidad. El rombo es blanco y si no se especifica la multiplicidad, implícitamente es 1.
Nota: Este tipo de asociación es común.
-
Composición
Es una forma fuerte de agregación, en la que el contenedor es completamente responsable de sus contenidos y cada contenido esta asociado a uno y solo uno, contenedor. Es más, el tiempo de vida de las partes contenidas es coincidente con el tiempo de vida del contenedor.
Ilustración 30 - Agregación de Composición
La factura se compone de 1 o muchos ítems. Aquí, la multiplicidad de factura, si o si, es 1. si muere el objeto factura, mueren todos los objetos contenidos en factura, o sea, mueren todos los ítems.
Para este caso de asociación, se deben dar muchas condiciones.
-
Clases Asociativas.
Una clase asociativa es una clase con propiedades de clase y asociación. Pueden ser vistas como asociaciones con propiedades de clase o como clases con propiedades de asociación. Es el equivalente a una tabla asociativa en el modelo relacional. Esta clase mapea la relación de asociación, además puede atribuir calificaciones, estados. Por ejemplo, que sea buen o mal empleado, no es propio de persona, este atributo queda en la clase asociativa.
Ilustración 31 - Clase Asociativa
-
Interfaces.
Una interface nos permite definir un conjunto de firmas de métodos, asignarle un nombre y con esto definir un tipo, sin tener que pasar por definir una clase e instanciar un objeto cuyo tipo sea el conjunto de firmas de esa clase.
La interface es parecida a una clase, pero sin implementación. Al carecer de implementación, es imposible instanciar un objeto a partir de una interface. Ahora, en la clase, cuando la defino le indico que la clase, por ejemplo, Perro, va a implementar esta interface, esto quiere decir que la clase debe implementar la interface que toma, o sea, se le adhiere el tipo a la clase Perro (en el caso de que Animal haya sido definida como Interface).
El objeto instanciado a partir de una interface, va a tener el tipo de que es instanciado más el tipo de la interface que implementa. El objeto, por lo tanto se puede castear tanto al tipo de la clase como de la interface.
El hecho de que las interface no posean código, hace que los problemas en Herencia múltiple se complican mucho. La implementación de interfaces múltiples está permite, por que no va a haber problemas en cuanto a qué implementación tomar, debido a que los métodos están vació.
La implementación de interfaces produce que una clase pueda adquirir muchos tipos sin hacer herencia múltiple.
La implementación de interface es un mecanismo de reusabilidad.
Re-uso TIPOS. Abstractamente, se genera una matriz de tipos: verticalmente, por herencia y horizontalmente, por implementación de interfaces.
En Java, la implementación de interface se declara con el keyword interface. En Java, esta permitido poner atributos, pero estos atributos deben ser constantes. Como decir: static final. Static queda por que no se pueden instanciar objetos de clases que se definan como interface.
Los métodos siempre son públicos y abstractos, los keyword no se colocan por que se sobreentiende gracias al keyword interface. Cuando se implementa la interface, se debe poner el keyword public. En el caso de los atributos, SIEMPRE son públicos y constantes.
Una interface es como una clase con todos sus métodos públicos y abstractos y todos sus atributos públicos y finales, AQUÍ estoy perdiendo, por la herencia, la capacidad de tener más tipos. Entonces, si veo una clase de esta manera, me conviene ponerla como interface.
Java permite hacer Herencia de interfaces y además permite que la herencia sea múltiple. Por Ejemplo: Perro podría ser una interfaz:
public interface Perro{
int CANTIDAD_DE_PATAS = 4; //de por si, ya es “static final”
*void comer();*
*void ladrar();* *}*
public class Labrador implements Perro{ // si hubiera herencia, seria Perro, Gato, Pato
private String nombre;
*public void comer(){*
*/\* algo \*/*
*}*
*public void ladrar(){*
*/\* algo \*/*
*}*
*public void cazar(){*
*/\* algo \*/*
*}*
*// faltan los GET/SET* *}*
Gráficamente, quedaría:
Ilustración 32 - Representación de Interface
Perro miPerro;
miPerro = new Labrador
miPerro.comer();
((Labrador)miPerro).cazar();
Perro.CANTIDAD_DE_PATAS;
Cuando instanciamos un objeto por medio de un clase, siempre estamos haciendo UPCASTING. Entonces, en este ejemplo, para cazar(), debemos hacer un casting.
-
Sobre JAVA. Resumen de Sintaxis.
-
Clase.
-
[public][final | abstract] class <nombreClase> [extends <claseBase>] [implements <inter1>[<inter2>[<inter3]]] {
…
}
-
Interface.
[public] interface <nombreInterfaz> [extends <inter1>[<inter2>[<inter3>]]]{
…
}
-
Atributo.
[public | protected | private ] [static] [final] <tipo> <nombreAtributo>; |
-
Método.
[public | protected | private ] [static] [final | abstract] <tipoRetorno> <nombreMétodo> (<parámetros = <tipo> <nombreParam>, …> ){
<variablesLocales = <tipo> <variable>, <…> >
/*
Código
*/
}
Si el método es abstracto, se pone al final un punto-y-coma.
Todos los parámetros se pasan por VALOR. Si el parámetro, es una referencia, entonces la referencia del parámetro copia el valor de la referencia que se esta pasando, entonces se esta pasando por referencia OBJETOS. Los Objetos NO se pueden pasan por valor, por que no se puede copiar el OBJETO en si. Se pasa una copia del handler.
Dentro del código, Java permite definir variables en cualquier lugar. También, dentro del código se puede hacer bloques anidados.
Método {
…
…
{
… //bloque anidado 1
{
… //bloque anidado 2
}
…
}
}
-
Paquetes.
Una paquete es una unidad que permite organizar y agrupar un conjunto de clases e interfaces congruentes. Una clase pertenece a uno y solo un paquete y un paquete es un conjunto de clases. En java, esta es la primera línea que se declara en el código.
// archivo1.java
Package herramientas;
/*
código
*/
// archivo2.java
Package Herramientas;
/*
otro código
*/
En este caso estamos hablando del mismo paquete.
Existe una relación directa entre la ubicación del archivo físico .class y el nombre del paquete al cual pertenece. Si el paquete se llama, pirulo, entonces el archivo .class debe residir en la carpeta pirulo. Entonces, todas las clases que pertenezca al paquete herramientas, deben estar si o si en una carpeta que tenga el nombre del paquete Herramientas.
Ilustración 33 - Estructura de Directorios de Paquetes 1
Ahora, si el nombre de un paquete tiene un punto, entonces se crea un subdirectorio, por ejemplo:
herramientas.matemáticas es en realidad herramientas \ matemáticas.
Toda esta subdivisión, es una forma de organizar mejor las clases y la utilidad que tienen estas clases. Entonces, entre las clases que componen esta subdivisión, hay que analizar los métodos de acceso.
Ilustración 34 - Estructura de Directorio de Paquetes 2
Ahora, el CLASSPATH queda:
CLASSPATH = ………RT.jar………..;c:\Librerias\java\;.
Aquí estoy haciendo vivir la carpeta de herramientas en el disco C.
Import Java.lang;
Import herramientas.matemáticas;
New funcion2D();
Lo primero que hace el compilador es buscar dentro del Path absoluto que le indica el import. Se mete en el RT.JAR
Aquí, java busca dentro de la primer parte del CLASSPATH:
\java\lang\..
\herramientas\matematicas\..
Si agota esta parte, entonces busca dentro de la segunda parte del CLASSPATH y ahí busca dentro del disco C.
La única forma de asignar un nombre único para un directorio personal, es con un dominio de Internet.
Ar.edu.universidad.herramientas.matematicas
Queda un directorio conformado como:
As\edu\universidad\herramientas\matematicas
Luego, esta dirección relativa, se concatena con la segunda parte del CLASSPATH y se genera una dirección absoluta.
-
Dominios de Internet (DNS) {#dominios-de-internet-(dns)}
Los dominios de Internet (DNS1), dan etiquetas únicas en el mundo, entonces para que el nombre del paquete sea único, tendremos el siguiente dominio por ejemplo: ar.edu.universidad.herramientas.matematicas. En el file system, tendremos un pack relativo: c:\librerias\java\ar\edu\universidad\herramientas\matematicas;
-
¿Qué es el Nombre Completo de una Clase? {#¿qué-es-el-nombre-completo-de-una-clase?}
La realidad es que quedaría:
New ar.edu.universidad.herramientas.matematicas.funcion2d();
Gracias al import, nos ahorramos de hacer esto, lo cual seria muy tedioso.
-
Keyword import.
Uno puede poner:
ar.edu.universidad.herramientas.matematicas.*
Con el punto asterisco, queremos indicar que el import busque dentro de todo el árbol de clases. El poner el .* no esta del todo recomendado por que podríamos traer clases que no sean nuestras y generar problemas.
Entonces el orden de los import es importante, tanto como el orden de las carpetas.
-
Java.lang
EL import java.lang, siempre esta implícito y es donde viven todas las clases mas fundamentales que tiene Java, por lo tanto, siempre esta implícito, de lo contrario seria MUY complicado hacer cualquier código. Son clases, muy básicas y fundamentales.
-
Default Package
Cuando no se pone un nombre específico de paquete, en verdad, en la carpeta donde residen los archivos LANG, en esta carpeta, todas las clases son friendly entre si. O sea, si tenemos una carpeta y metemos muchas clases sin especificar un paquete, todas las clases son friendly entre si.
-
Archivo .JAR
Es un zip que tiene comprimido un montón de archivos .class para poder distribuirlos en un solo archivo.
Por ejemplo, cuando uno ejecuta un applet, uno debe bajarse los archivos .class para poder ejecutarse desde el navegador. Entonces es mucho más bajar un archivo .JAR que muchos .class.
-
Organización de Fuentes y Clases
Producto: ANT es un producto open-source que trabaja como un MAKE, el cual se utiliza para determina el orden en que se deben compilar los archivos de código fuente, librerías, etc. Para poder generar un “ejecutable” y evitar los problemas de dependencias.
Todo el código fuente .java debe estar en una carpeta llamada src y todos los archivos .class van a ir a parar a la carpeta bin. Esta organización es una relación 1 a 1. El javadoc es una aplicación que mira todo el código fuente y genera la documentación necesaria (en el mismo formato que el help nativo de Java). El javadoc incluye todo el comentario que este en el código.
Lo importante en todo esto es el concepto de CLASSPATH
-
Identidad e Igualdad en Java.
-
Método Equals.
-
Identidad.
-
-
Si tengo la referencia R y la referencia T, lo único que debo hacer es preguntar:
( r = = t )
Aquí se pregunta, si las direcciones de memoria son las mismas, entonces se esta a puntando al mismo objeto.
-
Igualdad.
No existe un operador para saber si dos objetos son iguales. Todo lo que esta en object esta heredado de cualquier objeto. Algunos métodos son protected y otros friendly. Por ejemplo, finalize(). Para determinar si dos objetos son iguales, se utilizar, por medio de la API del lenguaje, el método equals(Object obj). La implementación por defecto, es aplicar identidad. Entonces el equals:
public bolean equals (Object obj){
return (this == obj);
}
Entonce, si yo tenia:
Perro p;
Perro px;
P = new Perro();
Px = new Perro();
/*
pasan cosas y puede cambiar el estado.
*/
if (p.equals(px){
/* son iguales */
else
/* son distintos */
}
/* Esta implementación da como resultado FALSE */
La respuesta a saber si se puede saber por medio de algún método si dos objetos son iguales es: se tiene que hacer la implementación por uno mismo. Implementar la igualdad. Si uno quiere implementar la igualdad, el método equals(Object obj) heredado de Object, se deben seguir ciertas reglas, criterios:
1) Nulidad: si digo x.equals(NULL), esto tiene que ser, SIEMPRE, igual a FALSE.
2) Reflexibilidad: x.equals(x), SIEMPRE debe ser TRUE.
3) Simetría: (x.equals(y) == TRUE), si y solo si, (y.equals(x) == TRUE).
4) Transitividad: (x.equals(y)==TRUE) && (y.equals(z)==TRUE) (x.equals(z)==TRUE).
5) Consistencia: si el estado de los objetos NO cambia, entonces, invocaciones sucesivas del método equals, deberían retornar el mismo valor. La invocación del método debe ser independiente del tiempo.
Si la relación es Reflexiva, Simétrica, Transitiva, quiere decir que son EQUIVALENTES. Esto forma lo que se llama “particiones”, son subconjuntos dentro del universo y cada elemento de este universo es equivalente entre sí.
El método equals(Object obj) nos ayuda a orden/organizar el universo de objetos dentro de nuestro sistema.
Ejemplo: implementamos la clase “punto” en dos dimensiones.
Public class Punto2D{
Private double x;
Private double y;
*Public Punto2D(double pX, double pY){*
*/\**
*This.x \= x;*
*This.y \= y;*
*\*/*
*x \= pX;*
*y \= pY;* *}*
// getter / setter
public boolean equals (Object obj){
Punto2D p;
*if (obj \== null)*
*return False;*
*p \= (Punto2d) obj; // RTTI*
*if ( ( x \== obj.**getX()**) && ( y \== obj.**getY()**))*
*return True;*
*else*
*return False;* *}*
La igualdad en Java es responsabilidad del programador. Uno mismo es quien debe aplicar todas las reglas necesarias para poder determinar si dos objetos (su estado) son iguales.
Al momento de implementar la igualdad, se debe analizar QUE atributos conforman el estado del objeto, por lo tanto, dentro de la implementación de Equals podrían no estar todos los atributos de la clase a igualdad.
-
Otros Métodos de la Clase Object
-
Método HashCode.
-
Retorna un número entero que representa al objeto desde el cual fue invocado el hashCode. La implementación de object, retorna un valor único por objeto. La implementación interna de hashCode es básicamente devolver la dirección de memoria del Objeto. Para redefinir hashCode, se deben tener 3 características en particular:
1) Durante una misma ejecución debe retornar siempre el mismo valor.
2) Si dos objetos son iguales, en términos de equals, entonces debe retornar el mismo valor.
3) Si dos objetos NO son iguales (en términos de equals), entonces se recomienda que debe retornar valores diferentes.
-
Método toString.
Devuelve un String (texto) que de una u otra manera denota al objeto del cual fue invocado. La implementación por defecto que tiene toString es: Poner el nombre de la clase, seguido de un @ y luego en formato hexadecimal la dirección de memoria donde vive el objeto. Y en el help aparece como:
getClass().getName() + ‘@’ + Integer.toHexString(hashCode())
toString, es un método muy importante, por que cuando uno ve en alguna parte “hola”, lo que en realidad se esta haciendo es crear un objeto tipo String que contiene el texto “hola” y este texto es inmutable, o sea, no lo puedo modificar directamente.
Este método se lo llama automáticamente cuando se concatena una referencia con un String.
“hola” + p1.toString();
Esto es valido y devuelve:
“holaPunto2D@0x019A”
Esto no es del todo bonito y por lo tanto redefinimos el método toString para que retorne algo parecido a < x ; y >:
Public String toString(){
Return “<” + getX() + “;” + getY() + “>”;
}
-
Método clone.
Cuando uno dice:
P1 = p2;
Lo que en realidad pasa es que P1 es una copia de la dirección de memoria de P2. En otros lenguajes, lo que puede suceder es que se copia el Objeto P2 y a P1 se lo apunta a P2. En java no se clonan los objetos como la asignación de referencias. Pero, en la clase object, el método clone hace una copia del OBJETO en si que vive en memoria y se lo asigna a P1.
P2 = P1.clone(); // esto NO funciona, por que clone es protected.
Entonces, se debe redefinir el método clone:
*@Override*
*public Object clone() throws CloneNotSupportedException*
*{*
*return super.clone();*
*}*
Donde Super llama a la implementación mas cercana en la escala de jerarquías. Si existe una jerarquía mayor se debe controlar que desde arriba se haya implementado de la misma manera.
P2 = (Punto2D) P1.clone();
Pero Java exige que se haga una implementación que se llama Interface Marcada, donde se obliga a la clase a implementar la clase clonable para indicar que la clase implementa la clonación. Estas interfaces NO tienen métodos y lo que hacen es caracterizar objetos de estas clases.
-
System.out
public static final PrintStream out
The “standard” output stream. This stream is already open and ready to accept output data. Typically this stream corresponds to display output or another output destination specified by the host environment or user.
For simple stand-alone Java applications, a typical way to write a line of output data is:
**System.out.println(data)**
A PrintStream adds functionality to another output stream, namely the ability to print representations of various data values conveniently. Two other features are provided as well. Unlike other output streams, a PrintStream never throws an IOException; instead, exceptional situations merely set an internal flag that can be tested via the checkError method. Optionally, a PrintStream can be created so as to flush automatically; this means that the flush method is automatically invoked after a byte array is written, one of the println methods is invoked, or a newline character or byte (‘\n’) is written.
Tanto el print como el println están sobre cargados.
Llamo a la clase y a su atributo de clase “out”, que es un atributo de instancia (referencia). Puedo llamar a los métodos del objeto que creé.
-
Integer
The Integer class wraps a value of the primitive type int in an object. An object of type Integer contains a single field whose type is int.
In addition, this class provides several methods for converting an int to a String and a String to an int, as well as other constants and methods useful when dealing with an int.
Integer, no tiene un método para cambiar el valor del int, los Wrappers son inmutables. Por ejemplo:
Integer wI; //wrapper de int
int i;
wI= new Integer(3);
wI.intValue; // retorna 3
wI= new Integer (wI.intValue++); //esto da 4, tira el objeto anterior y crea uno nuevo
//intValue está en todos los wrappers, c/u con su tipo
orden de ejecución:
- intValue
- wI.intValue++
- new Integer (wI.intValue++)
-
wI= new Integer (wI.intValue++);
-
int Value
-
Integer(int value)
Constructs a newly allocated Integer object that represents the specified int value.
-
parseInt
static int parseInt(String s)
Parses the string argument as a signed decimal integer.
i=Integer.parseInt(“3”); //método de clase
-
valueOf
static Integer valueOf(String s, int radix)
Returns an Integer object holding the value extracted from the specified String when parsed with the radix given by the second argument.
-
toString
Está sobrecargado:
String toString()
Returns a String object representing this Integer’s value.
static String toString(int i)
Returns a String object representing the specified integer.
static String toString(int i, int radix)
Returns a string representation of the first argument in the radix specified by the second argument.
String s;
s=Integer.toString(3); //3 como cadena
-
Hola Mundo
public class HolaMundo {
public static void main(String[] args) {
System.out.println(“Hola Mundo!”);
}
}
En este caso, out es un atributo de la clase System, es un atributo de tipo referencia estática que es instanciado en el API de Java. Entonces es esto lo que me permite llamar al System.out.println();. Donde println() es un método de la clase PrintStream.
System.out es un objeto.
-
Clases Wrapper
Para modificar cualquier valor de los wrapper (siempre son inmutables), siempre se los debe instanciar un nuevo objeto.
String s;
Int i;
Integer wI;
wI = new Integer(3);
wI.intValue();
wI = new Integer(WI.intValue(wI.intValue++));
I = Integer.parseInt(“3”);
S = Interger.toString(3);
-
Arreglos en Java.
Java desde el punto de vista gramatical concibe el concepto de arreglo. Es un concepto del lenguaje mismo, no de la API o externo.
Se trata de una secuencia ordenada, indizada, contigua de cosas del mismo tipo de una misma categoría.
Existen dos tipos fuertes de arreglos:
- Primitivos: int, flota, doble.
- Objetos: todos los elementos deben ser del mismo tipo, pero, por ejemplo un arreglo que tenga objetos de tipo animal. Entonces, un arreglo de tipo animal puede tener perros y gatos.
Existen arreglos por cada tipo definido en el sistema. El Tipo se define por la CLASE o por la INTERFACE. Cada arreglo tiene un tipo en especial. Los arreglos de objetos, en realidad son arreglos de referencias. Un arreglo es un objeto, es una referencia a referencias.
-
Sintaxis.
-
Arreglos de tipos primitivos.
Int [] arregloInt; // referencia a un array de enteros. “array of int”, por lo tanto, a partir de
*// este momento se pueden crear objetos de este tipo.*
arregloInt = new int [10];
arregloInt[3] = 8;
-
Se crea en el heap un objeto de tipo int[] que NO hereda de nadie. No se aplica el modelo de jerarquía de clases.
Ilustración 35 - Objeto Arreglo en Memoria
Para redimensionar un objeto tipo arreglo (array) de tipos primitivos, hay que crear un nuevo objeto array y pasar los datos del primero al nuevo arreglo, manualmente.
arregloInt = new int[20];
Ahora, para no perder el contenido del primer arreglo, hay que crear un nuevo objeto, reverenciarlo con otra referencia y copiar su contenido.
Double[] ad;
-
Arreglos de Objetos.
Perro[] ap;
Ap = new Perro[10];
Automáticamente, la JVM aprehende que Perro es una clase creada por nosotros y crea automáticamente el tipo Perro Array y aplica el mismo esquema que en tipos primitivos, con la excepción que ahora cada casillero es una referencia.
Ap[4] = new Labrador;
Ap[3].ladrar();
Ap[4].ladrar();
((Labrador)ap[4]).cazar();
También se puede decir.
Animal[] aa;
Aa = new Animal[10];
Aa[0] = new Perro;
Aa[1] = new Gato;
Aa[2]= new Perro;
.
.
.
El arreglo más genérico es hacer un arreglo de Object.
Object[] aa;
Aa = new Object[10]
Aa[0] = new Casa;
Aa[1] = new Lapiz;
Ahora, si se necesita extender el array:
Object[] aux = new Object[2];
For(int i= 0; i<10 ; i++)
Aux[i] = aa[i];
Aa = aux;
Auc = null;
-
Clase Vector.
Ahora, todo este trabajo se puede evitar si tenemos una clase que nos haga todo esto por nosotros y es aquí donde aparece el concepto de vector.
Vector v;
V = Vector();
v.size(); // devuelve 0
v.add(new Perro());
v.add(new Gato());
v.size(); // devuelve 2
v.get(0); // devuelve algo de tipo Object
v.get(0).ladrar(); // esto NO esta permitido por que ladrar no esta en la interface de Object
((Perro)v.get(0)).ladrar(); // se debe hacer downcasting
Vector internamente tiene un Object[]. Todos los arreglos tienen una sola propiedad, un atributo. Length, o sea, el total de elementos reservados para el arreglo.
Cuando tengo que hacer un vector de tipos primitivos hay que hacer que lo tipos primitivos sean wrappers.
-
Arquitectura Cliente-Servidor.
La arquitectura es un MODO de construcción de un sistema de software en donde los componentes de ese sistema están claramente identificados bajo dos roles específicos. Ciertos componentes se llaman clientes y ciertos componentes servidores. Un cliente solicita servicios a un componente de software servidor y un componente de software servidor atiende solicitudes de clientes. Y el modo de organización de este sistema de software C-S define una jerarquía de niveles/capas, en donde una capa del nivel superior siempre actúa como cliente de la capa de nivel inferior. Como resultado de esto queda que una capa puede eventualmente ser cliente y servidor simultáneamente, dado que cumple el rol de cliente frente a una capa inferior y como servidor frente a una capa superior.
Todo el software se construye en COMPONENTES, en donde cada elemento del todo tiene una determinada tareas. Cada capa se dedica a una determina área de aplicación:
- Interfaces Externas.
- Reglas de Negocio (lógica) --> el negocio --> las reglas del negocio que dependen del Paradigma.
- Persistencia/Almacenamiento de Datos.
Del Problema al Análisis = genera lo que se llama Negocio.
El modo en que los elementos del problema se definen para solucionar el problema es lo que define la regla de negocio. El negocio es aquella parte del software que está representando el problema real a resolver. La primera cosa siempre en el momento de hacer el software es delegar o armar arquitectónicamente el software en si:
- Una función con lógica
- Una función con printf
- Una función que almacene los datos.
Ilustración 36 - Arquitectura C-S de 3 Capas
-
Arquitectura Multitier
Al organizar el software de esta manera, en capas, el software empieza a ser una arquitectura multitier, el cual no es otra cosa que una arquitectura cliente-servidor aplicada iterativamente en varios niveles.
Ilustración 37 - Arquitectura Multitier
La diferencia en arquitectura cliente-servidor siempre es UNIDIRECCIONAL.
Ilustración 38 - Arquitectura Cliente-Servidor
El cliente conoce perfectamente quien es el servidor, pero no a la inversa, o sea, el servidor no conoce a los clientes.
También se definen en esta arquitectura el cliente fino y el cliente grueso, o sea, cual es la carga de trabajo que se le aplica a los clientes.
El esquema de Arquitectura Multitier lo único que hace es implementar iterativamente la arquitectura C-S en varios niveles.
Entonces el Software naturalmente se DEBE dividir en capas.
Ilustración 39 - Direccionalidad de la Arquitectura
-
Arquitectura de Tres Capas.
Entonces surgió el tema de dividir el software en 3 capas, debido a que el software se puede categorizar en tres niveles:
- INTERFAZ GRÁFICA: set de elementos que permitan al usuario interactuar con la aplicación. GUI, cuya función es servir como servidor al Cliente. Para el usuario, lo que muestra el GUI es el software (en una forma abstracta).
- CAPA DE NEGOCIO: recibe instrucciones que el usuario envía vía el GUI y es esta capa quien tiene la capacidad y la lógica del negocio para poder realizar cosas.
- PERSISTENCIA: se trata de poder almacenar datos de forma persistente en un medio de almacenamiento que permita recuperar los datos.
Ilustración 40 - Arquitectura de tres Capas
En JAVA por ejemplo cada capa puede ser un paquete.
Ilustración 41 - Detalle de las Capas y sus aplicaciones.
Por ejemplo, la arquitectura WEB es un ejemplo, otro es un aplication Server (SOAP, Web Métod), tiene un montón de objeto y me piden servicios, entonces yo uso los servicios de los objetos para atender esas solicitudes. Los objetos son quienes crean el negocio.
También se pueden tener mas capas, pero el secreto es ver que desde afuera se tienen que visualizar las 3 capas. La diferencia esta cuando se acerca a la arquitectura y es aquí donde pueden aparece mas capas. Pero, por ejemplo, lo que un usuario solicita a la capa de negocios y esta le responde al cliente, pero entre la capa de negocio y la GUI tiene que haber una capa que permita comunicar lo que el negocio le responde a los distintos cliente y lo mas importante es COMO le responde. Esta capa, que se le pega sobre la capa de negocio, se llama controller, pero además esta tiene dos capas, una semi-capa sabe como interpretar la llamada del GUI y la otra, la que sabe hablar con la capa de negocio para comunicarse con ella.
Esta subdivisión, NO visible desde afuera es una gran ventaja debido a que le da una independencia a cada capa en particular, permitiendo, por ejemplo, cambiar los GUI y la capa de NEGOCIO siempre puede ser la misma.
Bajo este concepto, la capa de controller pasa a ser una capa más, con una mayor pertenencia al negocio que a la capa física. Por esto, se dice que el controller forma mas parte del NEGOCIO. Dentro de la capa del controller viven los DTO (data transfer objets), quienes son los encargados de transportar información entre el GUI y el Controller.
Ahora, entre la capa de Negocio y Persistencia, también existe un sub-capa llamada BROKER, que es muy similar al controller, solo que a otro nivel, pero a la vez, el broker también esta mas cerca de la capa de negocio que de la capa de persistencia.
Por lo tanto, con éstas cinco capas, se puede decir que la arquitectura del SW implementa una independencia de las capas y conceptualmente y fundamental, la aplicación de SOFTWARE es básicamente la capa de NEGOCIO, el CONTROLLER y el BROKER. Dejando la capa de aplicación totalmente independiente de las tecnologías que implementen las otras dos capas.
Ilustración 42 - Arquitectura de tres Capas Completa
-
Excepciones
-
Concepto
-
Una excepción es un evento que ocurre durante la ejecución de un programa, que interrumpe el flujo normal de las instrucciones del programa.
El concepto de excepción establece que si ocurre un error en tiempo de ejecución, entonces se lanza, (throw) una excepción siendo capturada (catch) por un manejador de excepciones (exception handler), dentro de la cadena de llamadas en el intercambio de mensajes (stack de llamadas) para su tratamiento y corrección. El concepto de excepción es un mecanismo que viene integrado dentro del entorno de ejecución.
Ejemplo:
Class Operaciones{
/* Contiene una serie de métodos matemáticos*/
static double dividir(double m, double d){
return m/d;
}
}
operacion.dividir(3,0); /* Acá vemos que la división por cero es un error*/
Una alternativa es acoplar a la firma de métodos otro atributo para devolver el resultado, y que se generen código de errores para verificar las variables.
Otra forma es separar la administración de errores, de lo que es la implementación, así se mantiene la semántica de la capa de negocio.
En algún lugar ocurre el error y el mismo se maneja como una excepción y el mismo interrumpe el flujo de ejecución normal, es una especie de GOTO (muy sofisticado). El error salta en la cadena hacia arriba de la cadena de llamadas en el stack (burbujeo de excepciones).
El mecanismo de burbujeo evita ensuciar la firma de los métodos, no modifica la semántica.
Nota: la JVM ejecuta ciertas excepciones integradas.
-
Que sucede cuando ocurre una Excepción? {#que-sucede-cuando-ocurre-una-excepción?}
- En el punto donde haya un error, un objeto EXCEPCION es creado y lanzado.
- El flujo de control se interrumpe y se vuelve al contexto de llamada hasta encontrar un manejador de excepciones adecuado.
- Al llegar al manejador de excepciones, el mismo captura la excepción, interrumpiendo la serie de vuelta en contexto y continúa la ejecución dentro del mismo. En general el código del manejador de excepciones debe solucionar el problema.
Si la excepción agarrada no da suficiente información al manejador de excepciones, el mismo la rebota hacia arriba (viaja hacia arriba). Cuando una excepción llega al main, donde no se hace nada, entonces la rebota a la JVM y ésta se cuelga por que no tiene el código necesario para solucionar la excepción.
-
Concepto de Excepción
Class exception
La clase Exception hereda de la superclase Throwable, la cual es la superclase de todos los errores y excepciones del lenguaje java. Todas las clases que hereden de ésta son excepciones. El nombre de la excepción tiene que ser suficientemente representativo para identificar el tipo de excepción
Class DivisionCeroException extend Exception{} /* solo se define el tipo*/
Todas las excepciones son instancias de alguna clase que deriva de Exception.
-
Lanzar Excepciones
El keyword utilizado es throw, el mismo toma el objeto y lo tira (lo lanza).
Throw new DivisionCeroException(); /* Acá ocurre el paso 1*/
Si un método tiene en algún lugar un throw, entonces estamos obligado a poner en la firma del método, que el mismo es capaz de lanzar “algo”.
Class Operaciones{
/* Contiene una serie de métodos matemáticos*/
static double dividir(double m, double d) throws DivisionCeroException {
if(d == 0)
throw new DivisionCeroException();
return m/d;
}
}
El lenguaje obliga a poner el throws por que ayuda a mantener la idea que el control de errores es absoluto. Mantiene todo estable.
-
Manejador de Excepciones
try{
/* Código */
}catch (tipo1_E e){ /* ahora vienen los manejadores de excepciones */
/* “e” es el nombre del tipo de excepción */
/* código de error del handler excepción para el tipo1_E */
}catch(tipo2_E){
/*ME tipo2_E */
}catch(Excepcion e){
/*ME para el tipo Excepción, por que a la larga todas las excepciones */
/* son de este tipo */
}finaly{
/* la excepción */
}
Entonces en el ejemplo anterior, quedaría:
Try{
Operación.dividir(3,0);
}catch(DivisionCeroException e){
/* ME para el error */
}
Que pasa, si en un determinado nivel, uno ejecuta un método, pero en este nivel, no existe un código bien definido para manejar el error. Entonces para que se administre en un nivel superior el Exception Handler, se debe hace que en una clase de nivel superior, la misma es quien se le pide que tire la excepción.
Class x{
Static void main() thrws DivisionCeroException{
Operacion.dividir(3,0);
}
}
Aquí se ve el concepto de burbujeo, por que un error que ocurre en un nivel muy inferior, el error sube varios niveles hasta que alguien tiene la habilidad de poder administrar el error en un nivel superior. En el main NO SE PONEN THROWS.
-
Excepciones en RUN-TIME
Haciendo esto a rajatabla, es imposible que un programa se cuelgue. Pero pasa algo: en ciertas ocasiones, el concepto de Exception se vuelve un poco molesto. Volvemos al ejemplo de Perro:
Perro P;
p.ladrar(); /* aquí se lanza NullPointerException */
Cualquier invocación a un mensaje es capaz de tirar un NullPointerException y debió en tal caso exigir TRY-CATCH y hacer esto en todo lado es muy molesto. Entonces existe un tipo especial de excepciones llamadas RunTimeException, las cuales están exentas del TRY-CATCH. Por lo tanto, todo lo que herede de RunTimeException simplifica la vida, pero complica la detección de errores y conduce a muchos errores. Otro ejemplo es el RunCastException.
-
Interfaz Throwable
Es una interfaz de tipo de marca, y Exception es una clase que implementa ésta interfaz. Y todo lo que sea de tipo Throwable es candidato ser administrado por el throw.
-
Clase Error
Tiene el mismo comportamiento que Exception. Pero la diferencia es que se trata de una situación fatal, que no amerita que se implemente un TRY-CATCH.
-
Bloque Finally
El bloque finally, que viene seguido después de try-catc, significa que este bloque de código siempre se ejecutará, lo cual es una buena práctica de programación, debido a que incluyo cuando no exista un catch apropiado para a excepción, en base al código del bloque finally, se puede llevar al programa a un punto estable, y no dejar que las excepciones y los errores se propaguen sin control.
-
RTTI (OO) {#rtti-(oo)}
- Run
- Time
- Type
- Identification
Es la capacidad de un lenguaje OO, de permitir en tiempo de ejecución, determinar el tipo dinámico de una referencia. Con esto tenemos la capacidad de identificar los tipos que tiene asociado una referencia en tiempo de ejecución.
Java implementa RTTI con el keyword instanceof. Se trata de un keyword lógico:
Perro P;
If(p instanceof Labrador){ /* devuelve TRUE o FALSE */
(Labrador)p.cazar(); /* es un downcasting seguro por que usamos RTTI */
}
Existen dos formas más de hacer RTTI
- No hacer nada, y se lanza un ClassCastException, por lo tanto se da cuenta que no es del tipo que se solicito.
- Se verifica por medio de la clase de la cual se instanció objeto, a través de los métodos de la clase Class. Nos da un objeto que denota la clase de la cual fue instanciado el objeto. Esto al derivar de Object. La clase Class es parte de algo que se llama Reflexión, el cual es la capacidad de un objeto de reflejar (informar) sus características. La reflexión se usa mucho en la JavaBeans, es que un entorno gráfico de Java. Sirve cuando se tienen objetos desconocidos y se quieren saber sus características. Se pueden encontrar en Java.lang o Java.refletion. Se utiliza también en entornos distribuidos.
Perro P;
P = new Labrador();
If(p.getClass().getName().equals(“Labrador”))
((Labrador p).ladrar();
}
Nota: La reflexión es comúnmente utilizado por los programas que requieren la capacidad de examinar o modificar el comportamiento en tiempo de ejecución de aplicaciones que se ejecutan en la máquina virtual Java. Esta es una característica relativamente avanzada y debe ser utilizada sólo por los desarrolladores que tienen un buen conocimiento de los fundamentos de la lenguaje. Con esta advertencia en mente, la reflexión es una técnica poderosa y puede permitir que las aplicaciones realicen operaciones que de otro modo sería imposible.
-
Clases internas / Clases anidadas {#clases-internas-/-clases-anidadas}
El lenguaje de programación Java permite anidar clases dentro de la definición de otras clases. Existen dos tipo de anidamiento: el estático, en donde las clases son llamadas clases anidadas (nested classs); y el no-estático, en donde las clases son llamadas clases internas (inner class).
-
Clases anidadas
Las clases anidadas, a diferencia de las clases internas, son partes miembro de la clase que las contiene. Las clases internas pueden acceder a todos los atributos y métodos de la clase que la contiene, aún cuando éstos sean privados, pero las clases anidadas no pueden acceder.
Como las clases anidadas son miembros de las clases que las contiene, pueden ser declaras public, static, private o friendly, a diferencia de las clases internas que sólo pueden ser public o friendly.
-
Clase interna (inner class) {#clase-interna-(inner-class)}
Es una clase definida dentro de la definición de otra clase, resultando la primera, interna respecto de la segunda. (esto es solo en JAVA).
Si una clase es sólo útil a otra clase, es lógico embeber la primera dentro de la segunda, para mantener todo el código junto y encapsulado.
Class ClaseExterna{
// código de la clase externa
private static class claseAnidada{
// Código de una clase anidada
}
*public class ClaseInterna{*
*// código de una clase interna.*
*}*
*/\**
*void método(){*
*ClaseInterna ci;*
*Ci \= new ClaseInterna();*
*}* *}* *// o se puede instanciar desde fuera de la clase* *class otraClase* *{*
*// Acá instanciamos una clase anidada desde otra clase.*
*ClaseExterna.ClaseAnidada objetoAnidado \= new ClaseExterna.ClaseAnidada();* *}*
La clase interna sólo puede ser nombrada como TIPO (referenciada) en el contexto de la clase que la contiene. No es accesible fuera de ese ámbito. En el HEAP, funciona todo igual a como si se instanciaran dos objetos independientes. La clase interna es similar a poner private a la definición de esa clase.
Además las clases anónimas no pueden definir atributos estáticos
-
Características
- Permite organizar la estructura interna de una clase, no solo en atributos y métodos, sino también en clases, potenciando aun más el concepto de encapsulamiento. Por ejemplo, si existe un código muy especializado y quiero dejarlo en sólo una clase en particular, lo hago utilizando una clase interna.
- Las clases internas son privadas respecto del exterior. De esta manera la organización interna del código de una clase sigue respectando el concepto de abstracción.
- Cada clase interna puede heredar independientemente de otras clases internas o de clases que la contienen.
- Toda clase interna posee un acceso completo a los atributos, métodos y clases internas de la clase que la contiene.
Concepto: Las clases internas proveen un mecanismo para dar soporte interno a la clase que la contiene.
-
Clases Internas Anónimas
Supongamos el siguiente código:
Interfaz InterfazExterna{
*Int getAtributo();*
*Void setAtributo(int valor);*
}
class ClaseExterna{
*InterfazExterna getIE(){*
*Return new ClaseInterna();*
*}*
*class ClaseInterna implements InterfazExterna{*
*private int atributo;*
*public int getAtributo(){*
*return atributo;*
*}*
*void setAtributo(int valor){*
*atributo \= valor;*
*}*
*}* *}*
InterfazExterna ie; // declaro una referencia del único tipo que conozco
ClaseExterna ce; // declaro una referencia tipo claseExterna
Ce = new ClaseExterna();
Ie = ce.getIE(); // Consigo la interfaz que la clase interna implementa
Ie.setAtributo(3); // Solo así, puedo acceder a la clase interna, por INTERFAZ
Todo esto de clase interna se puede ver en el ITERATOR. Todos los iteradores en java son implementados con clases internas.
Mirando el código nos damos cuenta que el nombre clase interna es irrelevante, por que el objetivo es acceder a la clase interna. Entonces se puede hacer que se defina una clase interna sin tener que ponerle el nombre a la clase interna.
class ClaseExterna{
*InterfazExterna getIE(){ // método para obtener la clase interna*
*Return new InterfazExterna(){*
*private int atributo;*
*public int getAtributo(){*
*return atributo;*
*}*
*void setAtributo(int valor){*
*atributo \= valor;*
*} ; // acá termina la definición de la clase interna anónima*
*}*
Entonces directamente se retorna la definición de una interfaz y a la vez la definición de la clase interna, pero sin nombre. Luego de cerrar la última llave, se debe colocar el punto_y_coma.
Este método se usa mucho porque es la administración de los eventos de la interfaz gráfica en Java.
La motivación de las clases internas anónimas permiten declarar objetos dentro de la definición de una clase, y este objeto retornarlo al exterior. Se define código AL VUELO. Y queda que el objeto sólo tiene dos tipos
-
Características.
- permiten organizar el código de una clase para dar soporte interno a esa clase y esta organización del código es transparente desde el exterior.
- Permiten definir objeto al vuelo (sui estructura) que son retornados por métodos de una clase, siendo la implementación de ese objeto transparente al exterior.
-
JFC – Java Fundation Classes {#jfc-–-java-fundation-classes}
En JAVA existen tres generaciones de API orientadas a la GUI’s. Estas generaciones se las conoce como:
- AWT vieja (Abstract Windows Toolkit )
- AWT nuevo simplemente AWT
- Swing
AWT viejo, corresponde a JAVA 1.0. Si bien no era multiplataforma, permitía construir GUI que se veían igualmente mal en todas las plataformas. Además proveía un set de interfaz gráfica limitada. Además el modelo de administración de eventos, como entorno gráfico administra los eventos del usuario, no era orientado a objetos. Básicamente, era un switch, todos eran if-else-if apareados.
Luego surge JAVA 1.1 y surge una nueva versión de AWT, donde los cambios mas significativos son:
- El modelo de eventos SI es orientado a objetos, vía un concepto llamado LISTENERS,
- Segundo, introdujo a JAVA en el mundo de la programación visual, mediante el componente JAVA BEANS. Es un componente que de alguna manera, se integra en la aplicación y desde ese momento, el componente esta listo para usarse.
NOTA: Modelo Basado en componente es un modelo en donde el software es el resultado de integrar varias cosas. Es programación visual, pero no necesariamente. En los entornos RAD (Netbeans, Eclipse), te presenta una paleta de objetos de arrastre-soltar, estos son componente por ejemplo JAVA BEANS.
AWT toma el mínimo común denonimador de las plataformas, y utiliza esos componentes. Entonces JAVA wrappea determinado objetos de cada plataforma. Trabaja como el control remoto y la televisión. Todos los componentes JAVA son wrapper de los componentes nativos de cada una de las plataformas.
La estrategia AWT delega todo al sistema operativo. La otra estrategia es NO delegar nada, y montar sobre la plataforma java un motor gráfico pesado. Desde la versión 1.2 (JAVA 2), esta estrategia es implementada por SWING. Toda la implementación de esta estrategia por medio del SWING es el JFC, y esta es una API especializada a resolver la interfaz gráfica del usuario. Además el modelo JFC sigue basando en Java Beans.
También se incorporó el concepto de LOOK-AND-FELL, lo que permite a la interfaz gráfica dibujarse como si fuera otra plataforma.
-
AWT vs SWING
AWT (wrapperar, delegar) SWING (No delega)
Como en todo momento, se debe seguir charlando con la plataforma subyacente, AWT aun es válido.
Uno debe pensar en contenedores AWT y componentes SWING que se introducen en los contenedores AWT. Una de las cosas más importantes por la que se mantiene el AWT es por los LISTENER, que aparecieron con esta estrategia. Por lo tanto, todos los componentes AWT siguen existiendo.
- A los componentes AWT se los llama heavyway (siempre estará por encima del SWING).
- A los componentes SWING se los llama lightway.
Ambos componentes se pueden combinar, pero no conviene hacerlo, por el problema del solapamiento entre capas de distintos componentes.
IBM implementó el AWT mejorado, llamado SWT. Este toolkit sigue implementando la idea de la delegación a la plataforma subyacente. Explota al máximo la riqueza de esa plataforma.
-
JAVA BEANS
Permite poder tener un componente que se integre al IDE de desarrollo y de ahí mostrar todas sus propiedades y métodos, que ya están listos para usarse. Hacer el JAVA BEAN, es lo mismo que crear una clases, pero siguiendo una serie de convenciones.
-
Características
- Introspección: la herramienta de desarrollo puede indagar sobre un componente. Preguntarle ¿Cómo trabajas? Esto se implementa por medio de la reflexión. El IDE instancia un objeto de ese componente e indaga.
- Personalización: le debe permitir al IDE (el JAVA BEAN) que pueda personalizar su apariencia y comportamiento.
- Propiedades: los objetos deben poder exponer sus propiedades / atributos.
- Eventos: Son los comportamientos que se pueden implementar bajo determinados sucesos.
- Persistencia: El JAVA beans debe ser capaz de guardar el estado de los objetos. Se aplica el concepto de SERIALIZACIÓN.
Un java BEAN es una clase o un conjunto de clases que son wrappeadas en otras clases.
-
Convenciones.
- La clase que implementa el JAVA BEANS debe implementar la interfaz SERIALIZABLE
- Debe tener un constructor sin argumentos.
- Las propiedades debe ser accesibles mediante métodos GETTER y SETTER.
- Debe tener un conjunto determinado de métodos para el manejo de eventos.
- A todos los atributos se los hace PRIVATE y métodos (getter y setters) PUBLIC.
JFC es un framework gráfico para la construcción de GUI’s que se compone de las siguientes cosas:
- AWT (parte fundacional)
- SWING (componente = listas, botones, etc)
- JAVA 2D. Es una API para hacer dibujos.
- LOOK & FELL (apariencias)
- Accesibilidad, provee comportamientos que permitan incorporar a los componentes elementos que ayuden a personas con capacidades diferentes.
-
DRAG & DROP (arrastrar y soltar)
-
Jerarquía de Clases BEANS
-
Existen dos sistemas de GUI en JAVA
- Interfaz gráfica stand-alone. SDI (single document interface, cada ventana en su propia área); MDI (una ventana contiene múltiples ventanas, es un solo programa). En JFRAME se utiliza SDI. Para utilizar MDI se debe usar JInternalFrame.
- La otra estrategia es el JApplet. Implementa en un navegador un pequeño plug-in que interpreta los objetos tipo APPLET de java. Entonces la aplicación es administrada por el plug-in. Las restricciones están del lado del usuario. (APPLET VIEWER, software buscar).
-
JFrame
Está compuesto por un JRootPane: es un panel que engloba todo lo que va a tener adentro el Jframe. Esta va a tener un GlassPane, un LayeredPane, un contentPane y por último un menuBar.
El glassPane es un panel que sirve para interceptar cosas, por que todos los eventos pueden ser interceptados (por componente invisibles) antes de llegar al objeto concreto. Por defecto esta INACTIVO.
Para acceder al contentPane, dado un JPANEL f:
f.getContenPane()
-
JApplet
Es básicamente un PANEL, que adentro se le ponen cosas. Dentro de un JApplet existen 4 métodos, que se deben redefinir:
- init() es llamado una sola vez al momento de construir el JApplet en el Web Browser
- start() Se llama cada vez que la APPLET es visible dentro del browser.
- stop() Lo contrario al anterior.
-
destroy() El Némesis del INIT(), cuando se cierra el web browser, antes de que se muera, se llama al método destroy.
-
Modelo de Eventos de JAVA
-
Se basa en tres conceptos:
-
Concepto de Evento: cada componente es capaz de generar un evento y un evento es un objeto de una determinada clase, todas estas clases van a ser x…xEvent, siendo x…x, la naturaleza del evento, o sea, con el nombre se identifica el tipo de evento. Dicho objeto va a tener un estado, este estado va a tener información acerca del evento. Los eventos la crean la JVM.
-
Concepto de Listener: cuando un evento es generado, es escuchado por uno o más listener asociados al componente donde ocurrió el evento. Es el escucha quien va a estar asociado al componente en el cual ocurrió el evento. Existe un listener para cada tipo de evento. Un listener también es un objeto. Acá la simetría esta en: x..xListener. Los listener pueden ser, en lugar de una interfaz, una clase ADAPTER.
-
Registración:
1) La registración de listener con componentes se hacen con métodos que se llaman “ADD” algún listener, y se le pasa por parámetro el objeto.
2) Des-registración de listener: existen los métodos REMOVEx..xListener y se le pasa por parámetro el objeto que se quiere que se desasocie.
En el ejemplo:
ActionEvent Evento
ActionListener => método actionPerformed(ActionEvent e);
addActionListener(actionListener) Registration
removeActionListener(actionListener) Des-Registración
Un adapter es una clase que nos libera de tener que implementar una clase que tiene muchos métodos que deben ser redefinidos, haciendo que el código se esos métodos este vacío.
-
LayoutManager
La diferencia que tiene JAVA en los LayoutManager es que este componente es el administrador de presentación, o sea, es el encargado de dibujar los componentes en las diferentes plataformas. Estos LayoutManager se asocian a los paneles y cada panel tiene su propio layoutManager.
La clase container, tiene tres métodos importantes:
1) setLayout(LayoutManager lm)
A este método le pasamos un objeto que indica el tipo de estrategia a utilizar para dibujar las cosas dentro de ese contenedor.
2) Add(..)
Muy sobrecargado. Sirve para indicar al contener que componentes va a tener. Influye el orden en que se ponen las cosas. Esta sobrecargado de 4 maneras:
- add(Component c) // siempre coloca al final
- add(Component c, int index) // lo coloca en una determinada posición
- add(Component c, Object constraints) // con restricciones
- add(Component c, Object constraints, int index) // con restricciones y pos.
3) validate()
Cada vez que modificamos algo que un panel, para que el panel se compute, en cuanto a como dibujar el panel.
Los distintos tipos de LayoutManager son:
- BorderLayout
- FlowLayout
- GridLayout
- GridBagLayout (no se usa)
- BoxLayout (swing)
Cuando el layoutManager recibe null, entonces no se esta utilizando un layout, y en este caso se utiliza el posicionamiento relativo, donde uno mismo especifica las coordenadas X e Y. Esto no se debe realizar, por que se pierde la capacidad de multiplataforma.
-
Generics
El uso de “generics” agrega estabilidad al código haciendo que los errores sean mejor detectados en tiempo de compilación. La idea principal de generics es que permite abstraer del tipo.
Para usar generics se definen lo que se denomina variable de tipo: “public class Box<T>”; donde la T se refiere a “Tipo”. Luego, <T> puede ser usada en cualquier lugar de la clase, pero no se puede tratar de un tipo primitivo.
Ahora, cuando se quiere referenciar a esta clase desde algún lado, se tiene que hacer una invocación de tipo genérica donde se reemplaza <T> por algún tipo (que no sea primitivo). Parece una invocación de método, pero en realidad lo que se le esta pasando es un “tipo”, en lugar de una argumento.
A una invocación de tipos generics se la conoce como parametrización de tipos. Entonces, para crear una instancia de esta clase se usa el siguiente código:
| integerBox = new Box<Integer>(); | | :—- |
Ahora es posible, invocar a un método de Box, sin la necesidad de hacer un cast.
| public class BoxDemo3 { public static void main(String[] args) { Box<Integer> integerBox = new Box<Integer>(); integerBox.add(new Integer(10)); Integer someInteger = integerBox.get(); // no cast! System.out.println(someInteger); } } | | :—- |
Si se trata de agregar a la “caja” algún objeto que no sea de tipo Integer, la compilación va a tirar un error.
Inclusive es posible definir una clase con más de un tipo genérico, pero ambos deben ser si o si diferentes, de lo contrario se genera un error.
Existe una convención que define que los tipos parametrizados se deben escribir con mayúsculas y se definen de la siguiente manera:
- E - Element (used extensively by the Java Collections Framework)
- K - Key
- N - Number
- T - Type
- V - Value
-
S,U,V etc. - 2nd, 3rd, 4th types
-
Métodos y Constructores Genéricos
-
También se pueden crear métodos y constructores genéricos, como un ejemplo:
| /** * This version introduces a generic method. */ public class Box<T> { private T t; public void add(T t) { this.t = t; } public T get() { return t; } public <U> void inspect(U u){ System.out.println(“T: “ + t.getClass().getName()); System.out.println(“U: “ + u.getClass().getName()); } public static void main(String[] args) { Box<Integer> integerBox = new Box<Integer>(); integerBox.add(new Integer(10)); integerBox.inspect(“some text”); } } | | :—- |
Además de definir el tipo de los parámetros que puede recibir un constructor o un método, estos parámetros pueden ser parametrizados aun más, indicando un determinado rango de elementos aceptables. Para ésto, también se usa la palabra reservada extends, lo cual, en el ejemplo indica que los parámetros aceptados deben extender de Number.
| /** * This version introduces a bounded type parameter. */ public class Box<T> { private T t; public void add(T t) { this.t = t; } public T get() { return t; } public <U extends Number> void inspect(U u){ System.out.println(“T: “ + t.getClass().getName()); System.out.println(“U: “ + u.getClass().getName()); } public static void main(String[] args) { Box<Integer> integerBox = new Box<Integer>(); integerBox.add(new Integer(10)); integerBox.inspect(“some text”); // error: this is still String! } } | | :—- |
Además, se puede parametrizar aún más, indicando con el símbolo “&” otras interfaces: por ejemplo: <U extends Number & MyInterface>.
-
Patrones De Diseño
El análisis es algo psicológico con base en la experiencia y viceversa. La programación es algo estructural que se aprende, en cambio el Diseño es algo técnico, se necesitan herramientas, metodologías, etc. No es algo tan objetivo que luego se debe plasmar en procedimientos, reglas, buenos modos.
Uno de los problemas centrales del diseño es si decidir hacer un diseño particular o hacer un diseño general (el cual requiere un mayor esfuerzo) que abarque más casos posibles. Pero tampoco se puede hacer todo particular, por que ante cualquier cambio en los requerimientos, implican una modificación en el diseño.
Es la experiencia y la capacidad de sintetizar de la persona, lo que hace que el diseño sea llevado como un proceso de fábrica de software. Desde el diseño se pueden llegar, en base a la experiencia, a conceptos generales.
- Acoplamiento: en el diseño, es donde una cierta parte de un subsistema esta fuertemente enlazado con otra parte del sistema. Siempre hay que bajarlo, minimizarlo.
- Cohesión: expresa que si se analiza una unidad del software, esta unidad solo hace una cosa y esa cosa es bien definida. Cada componente hace una y solo una tarea. Esta propiedad hay que maximizarla.
Si no se cumplen estas dos características, el diseño esta mal hecho.
El acoplamiento en la OO es cuando se tienen dos clases que forman parte de subsistemas diferentes, el enganche, la dependencia debería ser muy baja. Para entenderlo: si uno hace un cambio en una clase, la pregunta que debería formularse es: ¿que tanto afecta este cambio a la otra clase?
La cohesión es ver la interfaz de una clase y determina que tipo de problema resuelve esa clase: ¿hace muchas cosas o una sola?
El patrón Expert se basa en el concepto de decir que una clase debe hacer lo que sabe hacer. No debe poseer métodos que no sean propios de la clase. Para determina este patrón (que en realidad es un principió para limpiar una clases, es un patrón de nivel CERO), se debe preguntar si todos los atributos son necesario para llevar a cabo los métodos que la clase prosee y viceversa.
Lo patrones de diseño son una de las características más importantes de la Orientación a objetos. Estos patrones son las RECETAS realizadas en base a la experiencia en la construcción y resolución de problemas en sistemas de software.
-
Patrón de Diseño (definición general) {#patrón-de-diseño-(definición-general)}
1) Solución a un problema en un determinado contexto que ocurre una y otra vez. 2) Dicha solución se describe genéricamente y no específicamente, 3) de modo que dos aplicaciones de esta solución darán implementaciones diferentes.
- Cuando un problema se puede extraer de su esencia, no de su aspecto.
- Describe el problema en general.
-
La aplicación del problema da la identidad al problema en particular. Pero si se ven las soluciones desde lejos, son las mismas.
-
Patrón de diseño (en la OO) {#patrón-de-diseño-(en-la-oo)}
-
Descripciones de clases y objetos (ente vivo que se comporta ejecución) que se comunican, las cuales se adecuan para resolver un problema general de diseño en un contexto particular.
-
Composición de un patrón
- Nombre: identificador corto que identifica al patrón. Nombre y apellido a un montón de ideas. Es una abstracción que permite acelerar el proceso de comunicación técnico.
- Descripción del problema: tiene que decir la pinta del problema, su esencia, lo fundamental, no lo particular.
- Descripción de la Solución: aparecen: clases, interfaces, objetos, comunicación, responsabilidades. Descripción de todos los elementos que componen la solución del problema. Componentes de clases y objetos, de interfaces (métodos), etc.
-
Consecuencias: todas las cosas que pasan luego de aplicar el patrón. Se deben evaluar para determinar que tanto afecta el aplicar el patrón al resto del diseño. Son críticos para evaluar las alternativas de diseño o para entender los costos y beneficios de aplicar el patrón.
-
Organización de los patrones (GoF) {#organización-de-los-patrones-(gof)}
-
Ámbito | Clase (herencia) | Propósito | ||
---|---|---|---|---|
Creación | Estructura | Comportamiento | ||
Factory Method | Adapter (clase) | Interpreter Templade Method | ||
Objeto (composición) | Abstract Factory Builder Prototype Singleton | Adapter(objeto) Bridge Composite Decorator Façade Flyweight Proxy | Chain of Responsibility Command Iterator Mediator Memento Observer State Strategy Visitor |
1. ### **Clasificación por propósito** {#clasificación-por-propósito}
- Creación: Encarar problemas de creación de objetos, de instanciación. Se utilizan cuando uno no sabe como instanciar determinados objetos.
- Estructura: Encarar problemas de cómo organizar los objetos. Como se asocian entre objetos.
-
Comportamiento: Como interactúan y que responsabilidades deben poseer determinados objetos. ¿Cómo deben charlar entre sí? O ¿que responsabilidades deben asumir como propias?
-
Clasificación por alcance
-
Clase: la solución se encara planteando una solución estática. Es decir vía la definición de las clases, por herencia, por jerarquía de clases. Son soluciones que están plasmadas en diagramas de clases y lo más importante, son soluciones estáticas. En tiempo de diseño, se plasma la solución.
Objeto: la solución se encara vía la interacción que tienen los objetos entre si, la interacción que existe en ellos. Solución en tiempo de ejecución.
- Clase y Creación: encara la creación de clases vía la herencia, no composición.
- Clase y Estructura: organiza los objetos vía la herencia de clases.
-
Clase y Comportamiento: Representa la solución de cómo los objetos se comportan vía la jerarquía de clases. Distribuye en la jerarquía de clases el flujo de control.
- Objeto y Creación: resuelven problemas de instanciación de objetos a través de otros objetos. Son otros objetos los que toman la responsabilidad de instanciar.
- Objeto y Estructura: La organización de los objetos es vía composición.
-
Objeto y Comportamiento: Describe como un grupo de objetos coopera para realizar una tarea que un objeto solo no podría (coordinan las responsabilidades entre ellos). Resuelven problemas donde las clases no son cohesivas (hacen muchas cosas) y se deben fragmentar.
-
Problema que se encara
-
- Encontrar que objetos forman parte de la solución, NO el negocio. Son objetos tecnológicos que se deben agregar para poder encarar la solución. No son objetos del dominio del problema.
- Permiten determinar la granularidad de los objetos (¿los objetos hacen pocas o muchas cosas?). Se lo denomina factorización del software orientado a objetos. Dado un diseño, se debe agrupar o descomponer cosas.
- Determinar la interfaz de un objeto. ¿Que método deben o no ir en la interfaz?
- Determinar la implementación de los objetos. ¿Se hacen clases o Interfaces?
-
¿Como aplicar el polimorfismo?
-
Definición de Framework
-
Es un conjunto de clases e interfaces cooperante que conforman un todo reusable para proveer un servicio específico. Se pueden usar y rehusar. Por lo general los Framework son un conjunto de patrones aplicados.
Existen Framework cerrados y abiertos. Los cerrados simplemente se usan y listo. Los abiertos son los que permiten seguir generando clases vía herencia, utilizando polimorfismo.
Patrón de diseño | Concepto, idea, metodología, soluciona determinados problemas. |
---|---|
Framework | Algo concreto, real, implementado, Presta servicios, implementa Patrones. |
-
Patrones de Creación
-
Abstract Factory
-
Propósito: proporciona una interfaz para crear familias de objetos relacionados o que dependen entre sí, sin especificar sus clases concretas.
Este patrón es útil cuando un sistema debe ser independiente de cómo se crean, componen y representan sus productos. También cuando una familia de productos relacionados está diseñada para ser usada conjuntamente, y es necesario hacer cumplir esta restricción.
Generalmente las fábricas abstractas suelen ser “Singleton”.
La clase FabricaAbstracta define un método de fabricación por cada tipo de objeto que va a fabricar.
-
Singleton
Propósito: Garantiza que una clase sólo tenga una instancia, y proporciona un punto de acceso global a ella.
Intención: Etimológicamente hablando, es el nombre que se le da a un conjunto cuando solo tiene un elemento. El patrón de diseño resuelve el problema de tener una y solo una instancia en ejecución y proveer un punto de acceso global a ella. Ejemplo: el spooler de impresión de una impresora.
//SingletonTest.java
class Singleton{
private static Singleton s;
private int n;
*private Singleton(){*
*n \= 0;*
*}*
*public static Singleton getInstancia(){*
*if(s \== null)*
*s \= new Singleton();*
*return s;*
*}*
*public void setValor(int p){*
*n=p;*
*}*
*public int getValor(){*
*return n;*
*}* *}*
class SingletonTest{
*public void mostrar(){*
*Singleton miSingleton \= Singleton.getInstancia();*
*//miSingleton.getInstancia();*
*System.out.println("Valor: " \+ miSingleton.getValor());*
*miSingleton.setValor(10);*
*System.out.println("Valor: " \+ miSingleton.getValor());*
*}*
*public static void main(String\[\] args){*
*new SingletonTest().mostrar();*
*}* *}*
-
Builder
Propósito: separa la construcción de un objeto completo de su representación de forma que el mismo proceso de construcción pueda crear diferentes representaciones.
Este patrón se debe utilizar cuando el algoritmo para crear un objeto complejo debe ser independiente de las partes de que se compone dicho objeto y de cómo se ensamblan; o cuando el proceso de construcción debe permitir diferentes representaciones del objeto que está siendo construido.
-
Factory Method
Propósito: Define una interfaz para crear un objetos, pero deja que sean las subclases quienes decidan qué clases instanciar. Permite que una clase delegue en sus subclases la creación de objetos.
Intención: Utiliza la herencia y delega a la clase especializada la creación de objetos. Estos problemas aparecen cuando existen jerarquías de clases paralelas. En ocasiones el método Factory puede ser private y en esos casos “parece” oculto.
-
Prototype
Especifica los tipos de objetos a crear por medio de una instancia prototípica, y crea nuevos objetos copiando dicho prototipo.
Este patrón se debe usar cuando un sistema debe ser independiente de cómo se crean, componen y se representan sus productos. Generalmente, se usa cuando las clases a instanciar sean especificadas en tipo de ejecución.
Se debe tener cuidado en la implementación del método Clone(), debido a que existen dos tipos de clonación: “copia superficial frente a copia profunda”. La copia profunda puede traer problemas si el objeto a clonar posee referencias circulares o si sus referencias son otros objetos que no se pueden clonar.
Cuando el número de prototipos de un sistema no es fijo, se puede usar un gestor de prototipos, el cual puede llevar el control de todos los objetos prototipos disponibles.
-
Patrones Estructurales
-
Adapter
-
Propósito: Convierte la interfaz de una clase en otra interfaz que es la que esperan los clientes. Permite que cooperen clases que de otra forma no podrían por tener interfaces incompatibles.
Existen dos tipos de adapter’s:
- Los de clases (estáticos), usan la herencia múltiple para adaptar una interfaz a otra. No sirve cuando se quiere adaptar una clase y todas sus sub-clases.
- Los de objeto (dinámico), se basan en la composición de objetos.
En este patrón, los clientes llaman a operaciones de una instancia de Adaptador. A su vez, el adaptador llama a operaciones de Adaptable, que son las que satisfacen la petición.
-
Bridge
Propósito: Desacopla una abstracción de su implementación, de modo que ambas puedan varias de forma independiente.
Al separar la abstracción de la implementación, en un sistema se pueden asociar clases en tiempo de ejecución, sin la necesidad de re-compilar las clases, lo cual mejora la compatibilidad binaria entre bibliotecas de clases distintas.
El patrón Bridge es en esencia una herramienta para organizar código que te permite agregar cualquier cantidad de front-end (servicios) que implementan sus operaciones por medio de la delegación a cualquier cantidad de back-end (opciones).
-
Decorator
Intención: Dinámicamente agrega responsabilidades a los objetos. El decorador provee una alternativa flexible para extender la funcionalidad de las subclases. La estructura de decorador tiene un traspié, el cual es que requiere que en Componente (hiper-abstracta) se deben colocar todos los métodos de los decorators.
-
Versión 1
Menu m;
m=new Menu(new ScrollBar(new TextView));
m.escribir(‘a’);
-
Versión 2
Para esto, existe otra versión, donde el decorador no cambia la interfaz del decorator. Decorador PELEA contra la herencia. En este segundo esquema, el decorador no agrega métodos, sino que agrega código a métodos que se quieren decorar en el componente concreto. Esto seria, extensiones que evitan tener que agregar/modificar la interfaz de la clase componente. Todos tienen la misma interfaz. Los hijos de decorador pueden redefinir métodos vía la composición.
-
Composite
Propósito: Compone objetos en estructuras de árbol para representar jerarquías de parte-todo. Permite que los clientes traten de manera uniforme a los objetos individuales y a los compuestos.
Organiza los objetos en una estructura tipo árbol, de tal manera que los objetos se contengan a si mismos y haya una propagación de mensajes que vaya del todo a sus partes.
La clave del patrón composite es una clase abstracta que representa tanto a primitivas como a sus contenedores.
Se utiliza en situaciones donde existen objetos contenedores y objetos contenidos y se trata a todos como si fueran del mismo tipo, aun cuando sean de diferente naturaleza.
Tengo dos cosas naturalmente distintas y las quiero tratar igual, no los quiero diferenciar (ramas de hojas). Se quiere tener un tipo en común. La estructura natural de los objetos, deben ser de tipo árbol.
-
Facade
Propósito: Proporciona una interfaz unificada para un conjunto de interfaces de un subsistema. Define una interfaz de alto nivel que hace que el subsistema sea más facil de usar.
Dado un conjunto de clases que conforman un sistema y a su vez ellas se dividen en subsistemas. El facade propone crear una clase artificial para acceder a un determinado subsistema (conjunto de clases) para tener un punto de acceso en común y abstracto. Se pueden tener tantas facade como subsistemas se tenga. El controller es un patrón de diseño facade.
Un típico objetivo de diseño es minimizar la comunicación y dependencias entre subsistema. La fachada proporciona una interfaz única y simplificada para los servicios más generales.
-
Flyweight
Propósito: Permite compartir objetos con granularidad muy fina, sin un coste prohibitivo. Un peso ligero es un objeto compartido que puede usarse a la vez en varios contextos. Son independientes en cada contexto, por lo tanto, no pueden hacer suposiciones sobre el contexto en el cual operan.
Los pesos ligeros modelan conceptos o entidades que normalmente son demasiado numerosos como para ser representados como objetos.
Todos los objetos pesos ligeros se pueden compartir siempre que el estado intrínseco del mismo sea independiente del contexto.
-
Proxy
Propósito: Proporciona un representante o sustituto de otro objeto para controlar el acceso a éste.
Una razón para controla el acceso a un objeto es retrasar el coste de su creación e inicialización hasta que sea realmente necesario usarlo.
Algunas de las aplicaciones de los proxys son:
- Proxy Remoto: proporciona un representante local a un objeto remoto (otro espacio de de direcciones, disco, por red, etc.)
- Proxy virtual: crea objetos costosos por “petición”.
- Proxy de Protección: Controla el acceso al objeto original.
- Referencia Inteligente: proporciona un handler con funcionalidades adicionales.
En este diagrama, subject también implementa la misma interfaz ISubject.
-
Patrones de Comportamiento
-
Chain of Responsability
-
Propósito: Evita acoplar el emisor de una petición a su receptor, dando a más de un objeto la posibilidad de responder a la petición. Encadena los objetos receptores y pasa la petición a través de la cadena hasta que es procesa por algún objeto.
El objeto que hace la petición no tiene un conocimiento explícito de quién la tratará.
Para reenviar la petición a lo largo de la cadena, y para garantizar que los receptores permanecen implícitos, cada objeto de la cadena comparte una interfaz común para procesar peticiones y para acceder a su sucesor en la cadena.
Una de las ventajas de éste patrón es que reduce el acoplamiento, debido a que tanto el receptor como el emisor se conocen explícitamente entre ellos.
-
Command
Propósito: Encapsula una petición en un objeto, permitiendo así parametrizar a los clientes con diferentes peticiones, hacer cola o lleva un registro de la peticiones, y poder deshacer las operaciones.
La clave de este patrón es una clase abstracta orden, que declara una interfaz para ejecutar operaciones.
Los objetos tipo command se puede decir que son “mensajeros”, por que su proposito es transportar “comportamiento” en lugar de “datos”.
Algunos de los usos más comunes para el patrón command son:
- operación “deshacer”. Volver atrás con cambios realizados sobre objetos.
- Comportamiento de “transacciones”.
- Barras de progreso. En una aplicaciones que ejecuta varias tareas a la vez, cada tarea podría tener un método tiempoEstimado().
- Wizards de programas de instalación, donde cada pantalla guarda el estado y el boton finalizar dispara el comando final.
- Botones de GUI e items de menu.
-
Thread Pools (almacén de hilos de ejecución, donde cada ítem en el pool es un objeto comando).
-
Interpreter
-
Propósito: Dado un lenguaje, define una representación de su gramática junto con un intérprete que usa dicha representación para interpretar sentencias del lenguaje.
Se debe usar cuando la gramática a tratar es simple, en caso contrario, se debería usar otra herramienta;y cuando la eficiencia no es crítica.
Este patrón hace que sea fácil cambiar y ampliar la gramática, gracias a la herencia, debido a que cada regla se implementa en una clase.
Para tener en cuanta, existen ocasiones en las cuales para resolver un determinado problema, puede que no sea beneficioso utilizar un mismo lenguaje de programación. Quizás resolver ese problema en un determinado lenguaje es muy costoso y difícil, y en otro lenguaje todo lo contrario. La solución seria utilizar el patrón interpreter para crear y embeber un interpreter para un determinado lenguaje dentro de otro programa.
Python es un ejemplo de un lenguaje que se puede embeber en Java sin ningún tipo de restricción.
-
Iterator
Propósito: Proporciona un modo de acceder secuencialmente a los elementos de un objeto agregado sin exponer su representación interna.
La idea clave de este patrón es tomar la responsabilidad de acceder y recorrer un objeto (por ejemplo, lista) y poner dicha responsabilidad en un objeto iterador. La clase iterador define una interfaz para acceder a los elementos de la lista.
Al tener dos interfaces (lista e iterador) separadas, se pueden implementar diferentes recorridos para los objeto agregado.
-
Mediator
Propósito: Define un objeto que encapsula cómo interactúan una seria de objetos. Promueve un bajo acoplamiento al evitar que los objetos se refieran unos a otros explícitamente, y permite varias la interacción entre ellos de forma independiente.
El mediador es un objeto que administra y controla la interacción entre un grupo de objetos. Estos objetos no se conocen entre sí, solo conocer al mediador.
Este patrón se debe usar cuando un grupo de objetos se comunican de forma bien definida y compleja; cuando un comportamiento está dividido en varias clases.
Los colegas envían y reciben peticiones a través de un mediador. EL mediador implementa el comportamiento cooperativo encaminando cada petición a los colegas apropiados.
Las ventajas de usar el mediator son:
- Reduce la herencia.
- Desacopla los colegas, al conocer sólo a la clase mediador.
- Simplifica los protocolos de objetos al transformar las relaciones muchos-a-muchos en uno-a-muchos.
Tanto Colleague como Mediator pueden ser interfaces/abstractas, pero en el caso de mediator, se puede hacer una sola clase concreta si es que la el protocolo de comunicación es simple.
-
Memento
Propósito: Representa y exterioriza el estado interno de un objeto sin violar la encapsulación, de forma que éste puede volver a dicho estado más tarde.
Es útil cuando se requiere la operación deshacer para anular operaciones provisionales o en sistemas de recuperación de errores.
Cada vez que se requiera guardar el estado de un objeto se crea un memento del objeto deseado,y sólo el creador del memento puede almacenar y recuperar información del memento.
El cliente o algún elemento externo solicita al originador que cree un memento, el mismo es almacenado en un lugar seguro (Caretaker). Sólo el originador y el Caretaker pueden acceder nuevamente al memento.
-
Observer
Propósito: Define una dependencia uno-a-muchos entre objetos tal que cuando un objeto cambia de estado, todos sus dependientes son notificados y actualizados automáticamente.
Este patrón resuelve el problema de comunicación (ascendente, flecha hacia arriba) entre la capa de presentación y la capa de negocio. Permitiendo, que los distintos GUI’s envíen señales hacia un Observer y este notifique al negocio.
Este patrón entra en un concepto llamado MVC (Model-View-Controller) que es un Framework de diseño que se utiliza para elaborar API de interfaz gráfica.
Existen dos estrategias para poder determinar si el modelo de datos sufre actualizaciones, la primera se denomina pooling (encuesta), donde en cada momento se le pregunta al modelo de datos si ha sufrido de actualizaciones. La segunda estrategia es la de interrupciones, donde, ante algún cambio de estado, se genera una interrupción y se actualizan las dependencias.
El patrón Observer también es llamado Modelo Suscriptor-Publicista. Y se debe usar cuando se una abstracción tiene dos aspectos y uno depende del otro, o cuando un objeto debe ser capaz de notificar a otros sin hacer suposiciones sobre quienes son dichos objetos.
Una de las ventajas del patrón observer es que promueve el bajo acoplamiento entre el sujeto y el Observador, debido a que el sujeto sólo tiene una lista de observadores que implementan una interfaz común, entonces, no conoce su tipo exacto.
Una desventaja del uso del patrón es que si no se controla bien el sistema de actualizaciones, una actualización inesperada o no requerida puede provocar cambios en cascada que no son necesarios.
En los casos donde las relaciones entre sujetos y observadores son muy complejas, se puede implementar un mediador entre ambos, el cual puede mantener las referencias necesarias, establecer diferentes políticas de actualización, etc. Esta clase es conocida por todos los sujetos y observadores, implementa el patrón mediator y singletón.
-
State
Propósito: Permite que un objeto modifique su comportamiento cada vez que cambie su estado interno. Parecerá que cambia la clase del objeto.
Este patrón se usa cuando el comportamiento de un objeto depende de su estado, y debe cambiar en tiempo de ejecución dependiendo de ese estado.
Contexto guarda una referencia hacia el estado actual, y dependiendo de la implementación del estado, será como se manejará. Incluso, contexto se puede pasar a si mismo en la invocación del manejador de estado para que éste haga algo con él. También el estado concreto puede determinar internamente cuál es el siguiente estado.
-
Strategy
Propósito: define una familia de algoritmos, encapsula cada uno y los hace intercambiables. Strategy permite que un algoritmo variar independientemente de los clientes que lo usan. Permite determinar una estrategia (modo de hacer algo) dinámicamente, o sea, on the fly.
Este patrón se debe usar cuando una clase puede diferir de entre varios comportamientos posibles; cuando se necesitan distintas variantes de un algoritmo.
Un código donde existen muchas sentencias condicionales, indica que posiblemente se deba usar el patrón strategy.
-
Template Method
Propósito: define el esqueleto de un algoritmo en una operación, delegando en las subclases algunos de sus pasos. Permite que las subclases redefinan ciertos pasos de un algoritmo sin cambiar su estructura.
Al definir algunos de los pasos de un algoritmo usando operaciones abstractas, el método plantilla fija su ordenación, pero permite que las subclases modifiquen dichos pasos para adecuarse a sus necesidades.
Es uno de los patrones de diseño más importantes, básicamente describe el diseño arquitectónico que aplican los frameworks. Esto es, dado un segmento de código, permite poder seleccionar determinas partes del mismo (métodos primitivos) e intercambiarlas aplicando para esto subclases.
Los métodos primitivos se los llama hook (ganchos) y permiten “enganchar” el código que uno hace (especializado) con el código original (template).
Se aplica el concepto de hoolywood: “no me llames, yo te voy a llamar”.
Los métodos plantilla son una técnica fundamental de reutilización de código. Son particularmente importantes en las bibliotecas de clases, ya que son el modo de factorizar y extraer el comportamiento común de las clases de la biblioteca.
Es importante que el método plantilla defina correctamente qué operaciones son enganches (hook, que “pueden” ser redefinidas) y qué operaciones son son abstractas (que “deben” ser redefinidas).
-
Visitor
Propósito: Representa una operación sobre los elementos de una estructura de objetos. Permite definir una nueva operación sin cambiar las clases de los elementos sobre los que opera.
Con el patrón visitor, definimos dos jerarquías de clases: una para los elementos sobre los que se opera y otra para los visitantes que definen operaciones sobre los elementos.
Este patrón se debe usar cuando tenemos toda una estructura de objetos, los cuales son de diferentes tipos y queremos realizar operaciones sobre la estructura, independientemente del tipo de cada objeto, o cuando la estructura de objetos es estática pero queremos añadir nuevas operaciones sobre la estructura.
El patrón visitante se debe usar cuando la estructura de objetos a visitar es bastante estática, pero lo que no es estático son las distintas operaciones sobre el conjunto.
-
7Análisis y diseño Orientado a Objeto. {#7análisis-y-diseño-orientado-a-objeto.}
Cuando hablamos de procesos de desarrollo y las etapas que lo componen, se pueden distinguir fácilmente tres etapas:
- Análisis
- Diseño
- Implementación
Cualquier proceso de construcción es continuo, desde el análisis hasta la implementación.
El análisis es el QUÉ, donde tratamos de entender el dominio del problema, lo que representa los elementos que componen el problema que se esta analizando, pero desde el punto de vista de la realidad. Son todos los elementos que van a formar parte del negocio.
El diseño, es el CÓMO. Ya se entendió el problema y sus componentes. Ahora, como se resuelve? Se plantar la solución en términos del dominio del problema, del mundo real del problema. No son conceptos tecnológicos, sino ¿cómo lógicamente se soluciona el problema? Se distinguen dos partes:
- COMO Lógico (solución conceptual) soluciona 100% el dominio del problema, en términos conceptuales (abstractos).
- COMO Tecnológico (que cincel y martillo se decide tomar), las herramientas de desarrollos.
Nota:Estos dos elementos son el DISEÑO.
La implementación, implica HACELO. Prácticamente es una etapa automatizada, por que no requiere de tanta creatividad, desde el diseño viene la solución.
Generalmente, la línea de acción va desde lo menos importante (programación) hasta el análisis.
-
Análisis y Diseño
Naturalmente el QUE y el COMO están ligados, existe un GRIS entre el BLANCO (o sea, el análisis) y en NEGRO (el diseño). Si fuera un cambio brusco, entonces se hablaría de un cambio en el cual se perdería algo, no seria algo natural, el gris es una continuidad del desarrollo. Siempre se debe lograr que la transición sea lo mas continuar posible. Tener un cambio graduado ayuda a no perder detalles que se podrían obviar en una transición brusca.
El A\&D OO es naturalmente iterativo, porque desde el análisis se va al diseño y es un ciclo repetido. En la OO siempre existe un proceso evolutivo del software.
-
UML (Unified-Modeling-Languaje) {#uml-(unified-modeling-languaje)}
Booch Su método
Runbaught OMT
Jacobson OOSE
Los tres autores, se unieron y propusieron la separación de:
- La notación UML
Con la notación lograron unificar la lenguaje para el modelado del proceso del software.
- El Proceso UP (unified process)
Unificaron además las metodologías de desarrollo. Pero el proceso es algo abstracto que siempre requiere algún cambio para poder adaptarse a la empresa y su entorno, eso quiere decir que el proceso de software es un esquema adaptativo.
-
Análisis
- Requerimientos del sistema Requerimientos funcionales.
- Atributos del sistema Requerimientos no funcionales.
Estas dos características deben quedar bien comprendidas antes de comenzar la etapa de análisis.
-
Identificación de Actores (IA) {#identificación-de-actores-(ia)}
Un actor es una entidad externa al sistema que de alguna manera participa en la historia de un caso de uso, un actor típicamente estimula al sistema con eventos de entrada o recibe cosas de el. Un actor queda representado por el rol que cumple en ese caso de uso. Los actores pueden ser: personas, sistemas computacionales o mecanismos electromecánicos.
-
Casos de Uso (CU) {#casos-de-uso-(cu)}
Un Caso de Uso es un documento narrativo que describe la secuencia de eventos que un actor, que usa el sistema, debe llevar a cabo para completar un determinado proceso.
Un proceso describe de inicio a fin una secuencia de eventos acciones y transacciones requeridas para producir o completar algo de valor para la organización o el actor involucrado.
Documentación de un Caso de Uso.
Caso de Uso: | <Nombre> ejemplo: CU1 - Comprar Articulo CU2 – Pagar Compra |
---|---|
Actores: | <lista de actores que participan en el caso de uso, A1, A2,…, An> |
Propósito: | <Descripción del objetivo/proceso del caso de uso> Ejemplo: el cliente compra ítems del supermercado |
Descripción: | <Descripción extensa> Describe de manera narrativa de todo lo que ocurre en la interacción. Generalmente, solo se describe el caso “feliz”, lo común. Todo lo que sale bien. |
Tipo: | <Primario> El caso de uso, si no se termina de resolver en el D\&I entonces el sistema no hace lo que debe hacer => Alta prioridad o <Secundario> Si esta, mejor,, sino no tienen tanta importancia. Se puede sobrevivir sin el. => baja prioridad y <Esencial> Se describe totalmente en el análisis. Algo utópico, o en la reingeniería de un sistema, se describe algo que totalmente se puede describir. o <Real> Cuando se describe un caso de uso indicando lo que exactamente se debe hacer, incluyendo tecnologías. |
Referencias Cruzadas: | < RF1, RF2,…,RFn, CU1, CU2,…,CUn> Lista de requerimientos funcionales que se consideraron para describir el caso de uso. También pueden existir asociaciones con casos de Uso. |
Curso de Eventos:
Actor | Sistema |
---|---|
1. El cliente llega a la caja con los artículos, y los presenta uno a uno. 2. El cajero presenta un artículo por vez, indicando el tipo y cantidad de artículo, hasta que no haya más artículos. En ese Caso ir al paso 4. El cajero solicita el total a pagar. 6. El cajero informa el total al cliente. 7. El cliente paga. (ver CU2). | 3.Registra el articulo y su cantidad 5. Se computa el total haciendo la sumatoria del producto entre precio unitario y cantidad de cada artículo. |
Se describe paso a paso, las charlas que existen entre el actor y el sistema. Siguiendo al diagrama de curso de eventos, se describen las Excepciones, para cada paso del curso de eventos que posee excepciones se debe generar un cuadro de curso de eventos de excepciones.
Excepciones:
Actor | Sistema |
---|---|
7.1.1. El cliente devuelve un articulo 7.1.2. El cajero presenta el articulo a devolver | 7.1.3. Se quita el artículo. (se vuelve al paso 4) |
Después de realizar el curso de eventos, se realiza el diagrama de actividad.
-
Diagrama de Actividad (DA) {#diagrama-de-actividad-(da)}
Por ejemplo: DA1 – Comprar Articulo. Cada CU se corresponde con un diagrama de actividad. Es una relación uno-a-uno.
En los cursos de eventos donde existan múltiples excepciones, se debe realizar 1 (uno) diagrama de actividades, completo, o sea, se deben describir todos los flujos de datos posibles.
Una vez finalizado todos los caso de uso. Viene lo que se denomina Diagrama de Caso de Usos.
-
Diagrama de Caso de Usos (DCU) {#diagrama-de-caso-de-usos-(dcu)}
El diagrama es uno solo. Un ovalo por cada CU. Todo englobado en una caja y los actores fuera de la caja. El diagrama viene a significar el diálogo entre los actores y el sistema. El diagrama esta orienta a charlas, por lo tanto, los casos de uso no son requerimientos funcionales, en su lugar pueden implementar una serie de requerimientos funcióneles. Los casos de uso no son necesariamente procesos, pueden ser varios.
Si un caso de uso puede existir sin que otro, que lo relaciona exista, entonces se dice que la relación es de <<uses>> (el que esta siendo apuntado existe per-se). En cambio el <<extend>> es una relación en donde un caso de uso ocurre si solo si otro caso de uso lo exige, tiene sentido cuando el escenario es tan complejo que no lo puedo describir en un CU. El apuntado no puede existir per se. Mayormente, las relaciones son del tipo <<uses>>.
Una vez finalizados los casos de uso y el diagrama de caso de uso (se dice que el desarrollo es “dirigido por los casos de uso” y el DCU es el punto final), se procede con todos los casos de uso a lo que viene a llamarse ranking de casos de uso.
-
Ranking de casos de uso
Todos los CU son listados en la primera columna de la siguiente tabla:
CU | Urgencia | Dependencia | Sugerido | No importante | Total |
---|---|---|---|---|---|
CU1 | 7 | 10 | |||
CU2 | |||||
. | |||||
CUn |
Luego se procede como sigue:
- Se asigna una puntuación entre 1 y 10 a cada CU, según la característica de ese CU, o sea, si se trata de un escenario critico o urgente, tendrá puntaje alto, si además posee muchas dependencias, también se le deberá asignar un puntaje alto.
- En la última columna se realiza la suma de los puntos de todas las columnas para ese CU.
- Una vez ordenado por el total y agrupado los casos de uso de mayor ranking, es cuando se asignar una serie de CU para cada iteración del desarrollo.
-
Para cada iteración se realiza un análisis y diseño.
-
Modelo Conceptual
-
Es uno solo y en el vamos a documentar diferentes características del sistema.
Un modelo conceptual es una representación de los conceptos presentes en el dominio del problema. Es el esquema o “entregable” más importante que se crea durante el análisis orientado a objetos. EL mismo se ocupa de identificar el conjunto de objetos que forma parte del negocio.
El término modelo conceptual posee la ventaja de que enfatiza fuertemente en los conceptos del dominio del problema y NO en las entidad del software a desarrollar. Básicamente el modelo conceptual es un diagrama estático estructural en donde las operaciones no son definidas. El mismo muestra:
- Conceptos
- Asociaciones entre conceptos
- Y atributos de esos conceptos.
El concepto es la palabra clave de este modelo. El concepto es: ¿Qué cosas forman parte del dominio del problema? Se dice que el diagrama es estático por que no muestra las variaciones de los elementos que conforman el dominio del problema en el paso del tiempo. Al ser estático y no dinámico (no mostrar comportamiento), las operaciones no aparecen.
EL modelo conceptual es igual al diagrama de clases pero sin los métodos. Se muestra la estructura del sistema.
En la primera iteración, cuando se descubren los primeros conceptos, solo se dibuja una caja, en la medida que se evoluciona en el análisis y se descubren atributos, los mismos se van listado en la cajita.
Las asociaciones es igual al que vimos en el diagrama de clases.
La diferencia con el diagrama de clase es que en el diagrama de clases se esta hablando de entidades de “software”, conceptos tecnológicos. En el modelo conceptual solo se reflejan los conceptos. El proceso de desarrollo del diagrama es:
- se identifican los conceptos
- se descubren las asociaciones
- se identifican los atributos.
El diagrama / modelo conceptual puede variar, dependiendo si varias los requerimientos.
Uno de los criterios básicos en el desarrollo del modelo es sub-especificar, o sea, ante la duda, los conceptos se describen.
En el modelo conceptual nos concentramos en las COSAS, no en procesos como en el análisis estructurado, independientemente de cómo las cosas se comportan.
Como se describen los conceptos.
- se descubre los conceptos por sus categorías (ver tabla pagina 92)
- Identificar sustantivos. Todos los sustantivos son candidatos a conceptos.
Asociaciones.
Criterios de identificación de asociaciones
- Necesidad de conocer, si un concepto necesita conocer a otro concepto
- se descubre las asociaciones según sus categorías (ver tabla pagina 108)
Muchas de las asociaciones pueden ser que desaparezcan, por que acá se modela el negocio y luego en la implementación, quizás no sean necesarias todas.
Atributos
Es el valor de un dato que pertenece a un concepto. Los atributos deben ser atómicos.
En caso de duda, modelar los atributos dudosos como conceptos.
-
Glosario
Aquellas cosas que no son muy obvios, se debe colocar el nombre del concepto y se lo define. SE documenta en forma descriptiva, de modo tal que la persona que lo lea pueda identificar correctamente el concepto y si es necesario se definen los atributos con todo el detalle necesario según el tipo de problema.
-
Diagrama de eventos de sistema
(Parte de comportamiento) Estos diagramas ilustran los eventos desde los actores hacia el sistema. Aquí se va desde lo más general a lo más finito. Se ven todos los actores y se analiza ¿Qué eventos disparan esos actores hacia el sistema? Se documentan estos eventos a nivel caja negra, simplemente se describe quien lanza que, no que sucede después.
Por cada caso de uso se genera un diagrama de eventos del sistema, mirando el diagrama de actividades. Pero también se da que en varios casos de usos pueden estar presente los mismo eventos, por lo tanto, se deben revisar todos los CU para evitar esta duplicación.
Solo se documentan los eventos en donde se interactu directamente con el sistema. Los eventos se describen muy parecido a un métodos, por que luego estos eventos / estímulos del sistema se transforman en los métodos.
Puede ser que existan una suerte de switch en los eventos del sistema, entonces siempre se debe poner todos los eventos y se los encierra en una llave para indicar que es uno y otro.
Un evento es un estimulo externo que recibe el sistema desde el actor. Esto desencadena una Operación del sistema (alias método). Hasta el punto en que el sistema, se transforme en un clase (de muy alto nivel). Para cada evento existe una operación/método. En este diagrama es la primera vez en el análisis aparece lo que luego de identificar todos los eventos, se puede llegar a identificar un objeto /clase.
La clase sistema es lo que conocemos como controller y aplica el patrón Facade. Por que es el punto de acceso al negocio del sistema. En sistemas grandes, pueden existir varios subsistemas. Sistema = Controller.
Aquí se ve por primera vez el GRIS.
-
Contratos
Hasta aquí se vio todo desde el punto de vista de una caja negra, ahora ¿Qué pasa cuando el evento llega al sistema? El punto de concesión se llama “contrato”.
Los contratos también pertenecen a la parte dinámica.
Tiene que existir una relación directa entre los argumentos de los métodos del sistema y los atributos de los conceptos del modelo conceptual.
La documentación debe se:
Coherente
Fuerte
Estable
Trazable
Consistente
Los contratos son documentos que describen el comportamiento del sistema (un todo) en términos de los cambio de estados que ocurrieron sobre el sistema una vez que la operación del sistema fue invocada.
QUE cambios ocurrieron <> NO se debe especifica COMO ocurrieron esos cambios.
Entonces los contratos entran en un PRE-diseño.
Nombre: | <Nombre completo de la operación del sistema que se esta describiendo> Ej. Nombre(parámetros):retorno |
---|---|
Responsabilidades: | <descripción muy corta de que responsabilidad tiene la operación del sistema> |
Referencias cruzadas: | <acá esta el tema de la trazabilidad> RF1, .., RFn, CU1,..CUn |
Excepciones: | <lista de todas aquellas situaciones que hacer que esta operación no pueda llevarse a cabo> |
Salida: | <los datos que salen, pero que no son datos de interfaz gráfica> Todas las salidas internas del sistema, comunicación con otros sistemas, almacenamiento en un registro, etc. |
Notas: | <cualquier consideración, que como analista, se quiera decir al diseñador o implementador a futuro> |
Precondiciones: | <todas las condiciones necesarias para que la operación pueda llevarse a cabo correctamente> |
Postcondiciones: | <se reflejan de manera completa y cronológica y en forma prosaica, todos los cambios de estado en sistema que se dieron lugar una vez que la operación fue llevada a cabo> 1) 2) .. Básicamente existen tres tipos de poscondiciones: Creación/eliminación de una instancia. Modificación de atributos de una instancia Creación/eliminación de una asociación entre instancias. Si con un evento apareció un perro es el primero Si el perro cumple años, es el segundo Si el perro cambio de dueño, se modifico la tercera Nota: se debe tratar de pensar en PASADO. |
Existe una fuerte relación entre las postcondiciones y el modelo conceptual. Si algo pasa o se describe en un contrato, entonces lo que se describió acá debió haber estado en el modelo conceptual.
-
Volviendo sobre el modelo conceptual.
Cuando tenemos conceptos de primer nivel (son accesible directamente desde el mundo exterior, existen conceptos ocultos) va a tener colectores.
Los colectores son más de diseño que de análisis.
En las postcondiciones, todos aquellos elementos que sean de instancia, primero hay que buscar esas instancias en los colectores. Entonces, este primer evento se debe indicar en las postcondiciones, aunque no encaje con las categorías. O sea, la búsqueda de instancias en colecciones. EL colector coleccionar instancias de primer nivel. Todas las búsquedas deben pasa por un colector.
Todos los conceptos de primer nivel, seguramente tendrán un colector: reservas, clientes, pagos, etc.
-
Diagrama de colaboración.
Los contratos dice que le va a ocurrir al sistema, pero no diseño como.
Los diagramas de colaboración son diagramas de iteración que dicen COMO los objetos va a interactuar entre si para llevar a cabo una operación que esta documentada en algún contrato Acá se explotan los contratos, es el paso a paso.
Básicamente un diagrama de colaboración se basa en:
El modelo conceptual define los objetos, sus atributos y relaciones.
Los contratos determinan las operaciones (métodos) del sistema y su estado
Existen dos diagramas de interacción:
Diagramas de secuencia:
Diagramas de colaboración
El problema con el diagrama de secuencias es que puede que tenga muchas flechas y uno se puede enredad.
Los diagramas de colaboración son distintos en el sentido de que aparece el concepto de que:
Se esta hablando de un método de instancia o de clase.
Una instancia de persona le esta enviado a una instancia de perro, el mensaje, Juga(). El número es importante por que indica la secuencialidad de los mensajes.
Cada elemento en un diagrama de colaboración puede ser de tres tipos:
Pagina 173
Si a una clase le llega un método, si o si, el método debe ser de clase.
Existe solo un enlace (línea) que comunica dos elementos.
Siempre una de las clases debe tener un mensaje que venga del hiperespacio (operación del sistema). Siempre se comienza el diagrama de colaboración, con la clase sistema (controller) y de ahí se desparrama hacia todos lados.
El método de estos diagramas, se documenta de la misma manera que en los diagrama de clases.
En los métodos que retornen algo: 1.ok:=morder(g)
-
Interfaz Gráfica de Prototipo
Paca daca diagrama de eventos de sistema, se le pone al lado una interfaz gráfica de prototipo. Esta es una foto de la ventana del GUI del sistema.
References
- Sandro Mancuso - SOLID: https://reversecoding.net/solid-principles-explained-sandro-mancuso/
- Clean Code: https://medium.com/mindorks/how-to-write-clean-code-lessons-learnt-from-the-clean-code-robert-c-martin-9ffc7aef870c
- https://medium.com/s/story/reflections-on-clean-code-8c9b683277ca
- https://williamdurand.fr/2013/06/03/object-calisthenics/
- https://sourcemaking.com/refactoring/smells
- IDD - Sandro Mancuso vimeo.com/130256611
- DDD - https://medium.com/the-coding-matrix/ddd-101-the-5-minute-tour-7a3037cf53b8
- El Domain Name System (DNS) es una base de datos distribuida y jerárquica que almacena información asociada a nombres de dominio en redes como Internet. Aunque como base de datos el DNS es capaz de asociar diferentes tipos de información a cada nombre, los usos más comunes son la asignación de nombres de dominio a direcciones IP y la localización de los servidores de correo electrónico de cada dominio.