Principios SOLID en programación orientada a objetos


En este post hablaremos sobre los primeros 5 principios de diseño de la programación orientada a objetos S.O.L.I.D que significa:

  • S : Single-responsibility principle
  • O : Open-closed principle
  • L : Liskov substitution principle
  • I : Interface segregation principle
  • D : Dependency inversion principle

Single-responsibility principle

Este principio se puede resumir en que Una clase debe tener una sola responsabilidad al hacer esto tendremos que:

  • Crear siempre clases pequeñas
  • Si una clase es muy grande dividirla en clases más pequeñas
  • No debe existir más de una razón para modificar una clase

Como vemos este principio es violado constantemente por código legado ya que muchas veces tienen clases con un sólo método y cientos de líneas de código, lo cual las hace difíciles de mantener, escalar y probar.

Open-closed principle

Este principio se puede resumir en que “Una clase debe estar abierta a extensiones, pero cerrada a modificaciones” al hacer esto tendremos que:

  • Estar abiertos a agregar funcionalidad a nuestras clases
  • Evitar modificaciones en ellas
  • Utilizar atributos privados y crear getters y setters pero solo cuando se necesiten
  • Utilizar clases abstractas con la funcionalidad común que no va a cambiar y crear múltiples implementaciones con la funcionalidad específica

Con esto nos aseguramos de tener métodos estables, confiables, con posibilidad de crecer y que al no ser modificados constantemente no van a impactar a otros componentes.

Liskov substitution principle

Este principio dice que “Las funciones que usan referencias a las clases base (padres) deben ser capaces de utilizar los objetos de sus clases hijas sin necesidad de saber que existen” al hacer esto tendremos que:

  • Al utilizar herencia, asegurarnos que se cubre la condición IS A (Es un)
  • Perro es un Animal, entonces perro puede heredar de animal
  • Libro no es un Cuaderno entonces aunque sean similares Libro no hereda de Cuaderno
  • En caso de que no se cumpla la condición IS A validar si se puede usar HAS A para evitar usar herencia en lugar de composición

Al respetar el principio de IS A podremos utilizar referencias de clases base apuntando a objetos de sus clases hijas sin necesidad de conocer quienes son.

Interface segregation principle

Este principio dice que “Un cliente nunca debe ser forzado a implementar una interfaz que no utiliza o no debe ser forzado a depender de métodos que el no utiliza” al hacer esto tendremos que:

  • Crear interfaces específicas
  • Preferir muchas interfaces específicas pequeñas a una interfaz general
  • Minimizar la dependencia entre los componentes

Al crear interfaces específicas evitamos interfaces generales que obligan a los clientes a implementar métodos que ellos no necesitan.

Dependency inversion principle

Este principio dice que “Las entidades deben depender de abstracciones y no de implementaciones, un módulo de alto nivel no debe depender de uno de bajo nivel” al hacer esto tenemos que:

  • Las abstracciones no deben depender de detalles específicos
  • Detalles específicos no deben depender en abstracciones
  • Objetos de alto nivel y de bajo nivel deben depender de la misma abstracción

Siguiendo este principio nos aseguraremos que nuestros componentes son fácilmente escalables sin necesidad de impactar a componentes que no deberían.

Conclusión

Al seguir los principios anteriores nos aseguraremos que nuestro código sigue estándares de calidad que permitirán que sea fácil de probar, mantener, escalable y re utilizable.

Para estar al pendiente sobre nuestro contenido nuevo síguenos en nuestras redes sociales  https://www.facebook.com/devs4j/ y https://twitter.com/devs4j

Autor: Alejandro Agapito Bautista
Twitter: @raidentrance
Contacto:raidentrance@gmail.com

Algoritmo de ordenamiento por selección en Java (Selection sort)


En el post anterior hablamos sobre el https://devs4j.com/2018/12/19/algoritmo-de-la-burbuja-en-java-bubble-sort/ en este post veremos otro algoritmo de ordenamiento llamado por selección o (Selection sort).

De igual modo que con el método de la burbuja, el algoritmo de selección dividirá el arreglo en 2 particiones, una ordenada y una desordenada, durante la ejecución la partición ordenada irá creciendo y la desordenada irá disminuyendo.

Algoritmo

El algoritmo seguirá los siguientes pasos:

  • Se definirá un indice de partición (partitionIndex) en la última posición del arreglo
  • Se definirá un indice (i) para recorrer el arreglo
  • Se definirá una variable (maxValue) como el elemento mayor apuntando inicialmente al primer elemento
  • Se buscará el elemento mayor entre los elementos que se encuentren antes del partitionIndex se utilizará la variable maxValue para determinarlo.
  • El elemento mayor se intercambiará con el elemento en la posición partitionIndex.
  • Se reducirá en uno el partitionIndex.
  • Se reiniciará el valor del indice i a 0
  • El proceso anterior se repetirá hasta que el arreglo completo se ordene

Veamos el código

Una vez que entendimos el algoritmo veamos el código:

public class SelectionSort {

	public void sortArray(int[] array) {
		for (int i = array.length - 1; i > 0; i--) {
			int maxValue = 0;
			for (int j = 0; j < i; j++) {
				if (array[j + 1] > array[maxValue]) {
					maxValue = j + 1;
				}
			}
			swap(array, i, maxValue);
			printArray(array);
		}
	}

	public void printArray(int[] array) {
		for (int i = 0; i < array.length; i++) {
			System.out.printf("%d \t", array[i]);
		}
		System.out.println();
	}

	public void swap(int[] array, int a, int b) {
		int value = array[b];
		array[b] = array[a];
		array[a] = value;
	}

	public static void main(String[] args) {
		SelectionSort selectionSort = new SelectionSort();
		int[] array = { 10, 1, 5, 40, 12, 34, 44, 12, 11, 9 };
		selectionSort.printArray(array);
		selectionSort.sortArray(array);
		selectionSort.printArray(array);
	}
}

Ejecutando el algoritmo y analizando los resultados

10     1   5   40  12  34  44  12  11  9   
10     1   5   40  12  34  9   12  11  44  
10     1   5   11  12  34  9   12  40  44  
10     1   5   11  12  12  9   34  40  44  
10     1   5   11  9   12  12  34  40  44  
10     1   5   11  9   12  12  34  40  44  
10     1   5   9   11  12  12  34  40  44  
 9     1   5   10  11  12  12  34  40  44  
 5     1   9   10  11  12  12  34  40  44  
 1     5   9   10  11  12  12  34  40  44  
 1     5   9   10  11  12  12  34  40  44  

En la salida anterior podemos analizar el siguiente comportamiento:

  • Los elementos mayores se van colocando del lado derecho
  • El arreglo se va ordenando de derecha a izquierda
  • El partitionIndex se va moviendo hacia la izquierda
  • Se requiere un número menor de intercambios que en el algoritmo de la burbuja

Características del algoritmo

  • No requiere arreglos adicionales para el ordenamiento
  • La complejidad es de O(n²)
  • Requiere menos cambios que el algoritmo de la burbuja (Bubble sort) para ordenar el arreglo
  • Es un algoritmo inestable (Si existe un elemento duplicado cambiarán de posición)
  • Es más eficiente que el algoritmo de la burbuja (Bubble sort)

Para estar al pendiente sobre nuestro contenido nuevo síguenos en nuestras redes sociales  https://www.facebook.com/devs4j/ y https://twitter.com/devs4j.

Si hay algún algoritmo que te gustaría que desarrollemos coméntenlo en nuestras redes sociales o en la sección de comentarios del post.

Autor: Alejandro Agapito Bautista
Twitter: @raidentrance
Contacto:raidentrance@gmail.com

Algoritmo de la burbuja en Java (Bubble sort)


El algoritmo de la burbuja es un algoritmo de ordenamiento de arreglos popular, este dividirá el arreglo en 2 secciones o particiones, una ordenada y la otra desordenada, durante la ejecución la partición ordenada irá creciendo y la des ordenada irá disminuyendo.

A continuación se muestra un arreglo desordenado:

El algoritmo seguirá los siguientes pasos:

  • Se tomará el primer elemento y se comparará con el siguiente
  • En caso de que sea mayor se intercambiarán los elementos de posición
  • En caso de que sea menor se hará nada
  • del lado derecho se irá creando la partición ordenada

Veamos el código

Una vez que entendimos el algoritmo veamos como se ve el código:

public class BubbleSort {
	public void sortArray(int[] array) {
		for (int i = array.length - 1; i > 0; i--) {
			for (int j = 0; j < i; j++) {
				if (array[j] > array[j + 1]) {
					swap(array, j, j+1);
				}
				printArray(array);
			}
			System.out.println("Change of partition index");
		}
	}

	public void printArray(int[] array) {
		for (int i = 0; i < array.length; i++) {
			System.out.printf("%d \t", array[i]);
		}
		System.out.println();
	}

	public void swap(int[] array, int a, int b) {
		int value = array[b];
		array[b] = array[a];
		array[a] = value;
	}

	public static void main(String[] args) {
		BubbleSort bubbleSort = new BubbleSort();
		int[] array = { 10, 1, 5, 40, 12, 34, 44, 12, 11, 9 };
		bubbleSort.printArray(array);
		bubbleSort.sortArray(array);
		bubbleSort.printArray(array);
	}
}

Ejecutando el algoritmo y analizando los resultados:

Una vez que ejecutamos la aplicación veremos la siguiente salida:

10 	1 	5 	40 	12 	34 	44 	12 	11 	9 	
1 	10 	5 	40 	12 	34 	44 	12 	11 	9 	
1 	5 	10 	40 	12 	34 	44 	12 	11 	9 	
1 	5 	10 	40 	12 	34 	44 	12 	11 	9 	
1 	5 	10 	12 	40 	34 	44 	12 	11 	9 	
1 	5 	10 	12 	34 	40 	44 	12 	11 	9 	
1 	5 	10 	12 	34 	40 	44 	12 	11 	9 	
1 	5 	10 	12 	34 	40 	12 	44 	11 	9 	
1 	5 	10 	12 	34 	40 	12 	11 	44 	9 	
1 	5 	10 	12 	34 	40 	12 	11 	9 	44 	
Change of partition index
1 	5 	10 	12 	34 	40 	12 	11 	9 	44 	
1 	5 	10 	12 	34 	40 	12 	11 	9 	44 	
1 	5 	10 	12 	34 	40 	12 	11 	9 	44 	
1 	5 	10 	12 	34 	40 	12 	11 	9 	44 	
1 	5 	10 	12 	34 	40 	12 	11 	9 	44 	
1 	5 	10 	12 	34 	12 	40 	11 	9 	44 	
1 	5 	10 	12 	34 	12 	11 	40 	9 	44 	
1 	5 	10 	12 	34 	12 	11 	9 	40 	44 	
Change of partition index
1 	5 	10 	12 	34 	12 	11 	9 	40 	44 	
1 	5 	10 	12 	34 	12 	11 	9 	40 	44 	
1 	5 	10 	12 	34 	12 	11 	9 	40 	44 	
1 	5 	10 	12 	34 	12 	11 	9 	40 	44 	
1 	5 	10 	12 	12 	34 	11 	9 	40 	44 	
1 	5 	10 	12 	12 	11 	34 	9 	40 	44 	
1 	5 	10 	12 	12 	11 	9 	34 	40 	44 	
Change of partition index
1 	5 	10 	12 	12 	11 	9 	34 	40 	44 	
1 	5 	10 	12 	12 	11 	9 	34 	40 	44 	
1 	5 	10 	12 	12 	11 	9 	34 	40 	44 	
1 	5 	10 	12 	12 	11 	9 	34 	40 	44 	
1 	5 	10 	12 	11 	12 	9 	34 	40 	44 	
1 	5 	10 	12 	11 	9 	12 	34 	40 	44 	
Change of partition index
1 	5 	10 	12 	11 	9 	12 	34 	40 	44 	
1 	5 	10 	12 	11 	9 	12 	34 	40 	44 	
1 	5 	10 	12 	11 	9 	12 	34 	40 	44 	
1 	5 	10 	11 	12 	9 	12 	34 	40 	44 	
1 	5 	10 	11 	9 	12 	12 	34 	40 	44 	
Change of partition index
1 	5 	10 	11 	9 	12 	12 	34 	40 	44 	
1 	5 	10 	11 	9 	12 	12 	34 	40 	44 	
1 	5 	10 	11 	9 	12 	12 	34 	40 	44 	
1 	5 	10 	9 	11 	12 	12 	34 	40 	44 	
Change of partition index
1 	5 	10 	9 	11 	12 	12 	34 	40 	44 	
1 	5 	10 	9 	11 	12 	12 	34 	40 	44 	
1 	5 	9 	10 	11 	12 	12 	34 	40 	44 	
Change of partition index
1 	5 	9 	10 	11 	12 	12 	34 	40 	44 	
1 	5 	9 	10 	11 	12 	12 	34 	40 	44 	
Change of partition index
1 	5 	9 	10 	11 	12 	12 	34 	40 	44 	
Change of partition index
1 	5 	9 	10 	11 	12 	12 	34 	40 	44 	

En la salida anterior podemos analizar el siguiente comportamiento:

  • Los elementos se van cambiando de izquierda a derecha
  • La partición ordenada se encontrará del lado derecho e irá creciendo cada que veamos el log “Change of partition index”
  • La partición desordenada se encontrará del lado izquierdo e irá decreciendo cada que veamos el log “Change of partition index”
  • Es posible que antes de que termine el algoritmo el arreglo ya se encuentre ordenado, pero tiene que terminar la ejecución para asegurarlo

Características del algoritmo

  • No requiere de arreglos adicionales para realizar el ordenamiento
  • La complejidad es de O(n²)
  • El performance se reduce cuando crece el número de elementos en el arreglo

Para estar al pendiente sobre nuestro contenido nuevo síguenos en nuestras redes sociales  https://www.facebook.com/devs4j/ y https://twitter.com/devs4j.

Si hay algún algoritmo que te gustaría que desarrollemos coméntenlo en nuestras redes sociales o en la sección de comentarios del post.

Autor: Alejandro Agapito Bautista

Twitter: @raidentrance

Contacto:raidentrance@gmail.com


Tutorial sobre la clase Optional a profundidad


La clase Optional se introdujo en Java 8 y se utiliza para representar si un valor esta presente o no, esto ayuda a crear apis limpias y a evitar posibles NullPointerExceptions.

A continuación algunos ejemplos sobre como utilizar Optional en una aplicación:

Ejemplo Básico

Supongamos que tenemos el siguiente código:

public Person findPersonByName(String name) {
	return dao.findPersonByName(name);
}

El código a simple vista parece no tener ningún problema, ahora veamos el código que lo utiliza:

public void validate(String name) {
	Person result = findPersonByName(name);
	boolean adult = result.getAge() > 18;
	/**
	 * Other calculations 
	 */
}

Como vemos el método validate utiliza a findPersonByName para con base en el nombre determinar si es o no un adulto, el código no tendría ninguna falla siempre y cuando el nombre que se pasa como parámetro existe en la base de datos, en caso contrario se produciría una NullPointerException.

Veamos como solucionarlo sin utilizar la clase Optional:

public void validate(String name) {
	Person result = findPersonByName(name);
	if(result!=null){
		boolean adult = result.getAge() > 18;
	}else{
		/**
		 * take different actions
		 */
	}
}

La solución es funcional pero no es muy práctica, ya que depende mucho de quien consume el método realizar la validación o no.

Veamos la solución utilizando la clase Optional:

public Optional<Person> findPersonByName(String name) {
	return Optional.ofNullable(dao.findPersonByName(name));
}

public void validate(String name) {
	Optional<Person> result = findPersonByName(name);
	if (result.isPresent()) {
		boolean adult = result.get().getAge() > 18;
	} else {
		/**
		 * take different actions
		 */
	}
}

Como vemos el método findPersonByName() busca a la persona por nombre y utiliza Optional.ofNullable() para indicar que es posible que se tenga un valor nulo. Al devolver un objeto de tipo Optional en lugar de uno de tipo Person se da a conocer a los consumidores del api que el valor de retorno del método puede ser nulo así que deben utilizar el método isPresent para validar si existe el valor o no. Esto lo hace una solución muy elegante a demás que se evita generar NullPointerExceptions para casos cuando es posible tener un elemento nulo.

Utilizando valores por defecto 

Existen casos cuando en caso de no existir un valor tal vez quieres utilizar un valor por defecto en lugar de devolver la referencia null, veamos el siguiente ejemplo:

public Properties getApplicationProperties() {
	Properties props = new Properties();
	props.setProperty("applicationName", "Great application");
	return props;
}

public void configureCluster(){
	Properties properties = getApplicationProperties();
	String context= Optional.ofNullable(properties.getProperty("contextPath")).orElse("/api");
	System.out.println(context);
}

public static void main(String[] args) {
	new Configuration().configureCluster();
}

Si ejecutamos el código anterior la salida será /api porque obtenemos la propiedad contextPath, al ser nula devolvemos el valor /api utilizando el método orElse().

En el ejemplo anterior vimos como utilizar el método orElse(), la limitante que tiene es que recibe un objeto del mismo tipo que el objeto opcional así que solo podremos poner un valor en el sin lógica, existe una version similar llamada orElseGet() la cual en lugar de tomar el valor a devolver en caso de que el valor no se encuentre presente tomará una interfaz funcional la cual solo se invocará en caso de que el elemento no se encuentre presente.

Veamos un ejemplo

public void configureCluster() {
	Properties properties = getApplicationProperties();
	String context = Optional.ofNullable(properties.getProperty("contextPath")).orElseGet(() -> 
	getContextPathFromRemoteService()
	);
	System.out.println(context);
}

public String getContextPathFromRemoteService() {
	return Math.random() + "";
}

Como vemos, en caso de que la propiedad ‘contextPath’ no se encuentre en nuestro objeto properties se mandará a llamar el método getContextPathFromRemoteService(). 

Lanzando excepciones

En el ejemplo anterior vimos como devolver un valor por defecto en caso de que algo no esté presente, ahora veremos como mandar una excepción, veamos el siguiente ejemplo:

public Properties getApplicationProperties() {
	Properties props = new Properties();
	props.setProperty("applicationName", "Great application");
	return props;
}

public void configureCluster() {
	Properties properties = getApplicationProperties();
	String context = Optional.ofNullable(properties.getProperty("contextPath")).orElseThrow(IllegalStateException::new);
	/**
	 * configuring the application
	 */
}

Como se puede ver modificamos el ejemplo anterior para que en caso de que no se encuentre la propiedad contextPath se genere una excepción IllegalStateException y detenga el inicio de la aplicación.

En el siguiente post veremos como utilizar la clase Optional en conjunto con Streams.

Para estar al pendiente sobre nuestro contenido nuevo síguenos en nuestras redes sociales  https://www.facebook.com/devs4j/ y https://twitter.com/devs4j.

Autor: Alejandro Agapito Bautista

Twitter: @raidentrance

Contacto:raidentrance@gmail.com

Pruebas Unitarias Parte 4: Code coverage con JaCoCo


En posts anteriores hemos hablado sobre como escribir nuestros test unitarios, el uso de mocks, spying y como escribir código “Testeable”, el día de hoy hablaremos sobre un concepto llamado Code coverage.

Code Coverage

El code coverage es una métrica utilizada en el desarrollo de software que determina el número de líneas de código que fueron ejecutadas durante nuestros test unitarios. Utilizando code coverage podemos determinar si nuestras aplicaciones se prueban de forma correcta y que porcentaje de escenarios no cuenta con pruebas automatizadas.

JaCoCo

JaCoCo es un plugin de maven que se utiliza para realizar reportes con base en la métrica de code coverage.

Configuración

Para configurar JaCoCo solo debemos incluir el siguiente plugin en nuestro código

<plugin>
	<groupId>org.jacoco</groupId>
	<artifactId>jacoco-maven-plugin</artifactId>
	<version>0.8.2</version>
	<executions>
		<execution>
			<goals>
				<goal>prepare-agent</goal>
			</goals>
		</execution>
		<execution>
			<id>report</id>
			<phase>prepare-package</phase>
			<goals>
				<goal>report</goal>
			</goals>
		</execution>
	</executions>
</plugin>

Utilizaremos JUnit para hacer nuestros tests de ejemplo así que incluiremos su dependencia a nuestro proyecto:

<dependency>
	<groupId>junit</groupId>
	<artifactId>junit</artifactId>
	<version>4.10</version>
	<scope>test</scope>
</dependency>

Con lo anterior tenemos listo nuestro ambiente para empezar a trabajar con JaCoCo y JUnit.

Paso 2 : Creando una clase de ejemplo 

Una vez que tenemos nuestro ambiente listo, crearemos una clase sobre la que ejecutaremos nuestras pruebas:

/**
 * @author raidentrance
 *
 */
public class Calculator {
	public int sumPositiveValues(int... numbers) {
		int sum = 0;
		for (int number : numbers) {
			if(number>0){
				sum += number;
			}
		}
		return sum;
	}
}

La clase Calculator tiene un método llamado sumPositiveValues(…) el cual dado un conjunto de números enteros sumará todos aquellos que sean positivos.

Paso 3 : Creando test unitarios

Una vez que tenemos nuestro método a probar el siguiente paso será crear su test unitario, veamos el ejemplo:


import static org.junit.Assert.assertTrue;

import org.junit.Test;

/**
 * @author raidentrance
 *
 */
public class CalculatorTest {
	private Calculator calculator = new Calculator();

	@Test
	public void testSumPositiveValues_withNegativeValues() {
		int result = calculator.sumPositiveValues(-10, -20, -30);
		assertTrue(result == 0);
	}
}

Como podemos ver el test unitario prueba el método sumPositiveValues() solo con valores negativos y al final valida que el resultado sea cero, si ejecutamos lo anterior veremos que el test unitario es exitoso.

Paso 4 : Ejecutando el goal de JaCoCo

Una vez que tenemos nuestro test listo queremos ver el code coverage de nuestro programa, para esto ejecutaremos el siguiente goal de maven:

mvn install
mvn jacoco:report

El goal anterior creará un directorio dentro de el directorio target llamado site dentro de este veremos otro folder llamado jacoco y dentro de el un archivo llamado index.html que al abrirlo se verá como se muestra a continuación:

El reporte anterior indica que el code coverage de nuestra aplicación es de 86%, todo nuestro código se encuentra dentro del paquete com.devs4j.jacoco , al darle click veremos la clase Calculator y su porcentaje de code coverage como se muestra en la siguiente imagen:

Al dar click en Calculator podremos ver el code coverage a nivel de método:

En este caso podemos ver que el método sumPositiveValues tiene un 84% de code coverage, al darle click podemos ver exactamente el código que no se ejecutó durante los tests:

En este caso podemos ver que como el test unitario solo probó el resultado utilizando números negativos la línea 15 nunca se ejecutó. Por esta razón no alcanzamos un code coverage de 100%.

Paso 5 : Agregando los tests faltantes

Como vimos una parte de nuestro código no se ejecutó durante los tests, eso significa que debemos agregar un test que utilice números positivos para asegurarnos que todo nuestro código se ejecutó durante la fase de tests unitarios, para esto modificaremos la clase CalculatorTest.java y agregaremos el siguiente test:

@Test
public void testSumPositiveValues_withPositiveValues() {
	int result = calculator.sumPositiveValues(10, 20, 30);
	assertTrue(result == 60);
}

Una vez hecho esto aseguraremos que con los dos tests unitarios que tenemos se ejecutará todo nuestro código.

Paso 6: Ejecutando de nuevo JaCoCo

Una vez incluido el nuevo test ejecutaremos nuevamente el plugin de JaCoCo para ver el nuevo code coverage:

Como podemos ver ahora el code coverage de nuestra aplicación es del 100%, esto no nos asegura que nuestro código es perfecto, pero si que todas las partes de el fueron probadas por tests unitarios.

Puedes encontrar el código completo en el siguiente enlace : https://github.com/raidentrance/jacoco-example.

Para estar al pendiente sobre nuestro contenido nuevo síguenos en nuestras redes sociales  https://www.facebook.com/devs4j/ y https://twitter.com/devs4j.

Autor: Alejandro Agapito Bautista

Twitter: @raidentrance

Contacto:raidentrance@gmail.com

Java 8 manejo de fechas y tiempo : ZonedDateTime, Period y Duration


En el post anterior Java 8 manejo de fechas y tiempo : LocalDate, LocalTime y LocalDateTime aprendimos como utilizar algunas clases para trabajar con fechas y tiempo, pero estas clases no consideran las zonas horarias, en este post analizaremos las clases ZonedDateTime, Period y Duration.

ZonedDateTime

ZonedDateTime se utiliza cuando queremos trabajar con fechas y tiempo pero agrega el factor de las zonas horarias, para esto utiliza un ZoneId el cual es un identificador para diferentes zonas, el siguiente código obtiene todos los ZoneId disponibles:

import java.time.ZoneId;
import java.util.Set;

/**
 * @author raidentrance
 *
 */
public class LocalDateExample {
	public static void main(String[] args) {
		Set availableZoneIds = ZoneId.getAvailableZoneIds();
		for (String zoneId : availableZoneIds) {
			System.out.println(zoneId);
		}
	}
}

Salida:

Asia/Aden
America/Cuiaba
Etc/GMT+9
Etc/GMT+8
Africa/Nairobi
America/Marigot
Asia/Aqtau
Pacific/Kwajalein
America/El_Salvador
Asia/Pontianak
Africa/Cairo
Pacific/Pago_Pago
Africa/Mbabane
Asia/Kuching
Pacific/Honolulu
Pacific/Rarotonga
America/Guatemala
Australia/Hobart
Europe/London
America/Belize
America/Panama
Asia/Chungking
America/Managua
America/Indiana/Petersburg
Asia/Yerevan
Europe/Brussels
GMT
Europe/Warsaw
America/Chicago
Asia/Kashgar
Chile/Continental
Pacific/Yap
CET
Etc/GMT-1
Etc/GMT-0
Europe/Jersey
America/Tegucigalpa
Etc/GMT-5
Europe/Istanbul
America/Eirunepe
Etc/GMT-4
America/Miquelon
Etc/GMT-3
Europe/Luxembourg
Etc/GMT-2
Etc/GMT-9
America/Argentina/Catamarca
Etc/GMT-8
Etc/GMT-7
Etc/GMT-6
Europe/Zaporozhye
Canada/Yukon
Canada/Atlantic
Atlantic/St_Helena
Australia/Tasmania
Libya
Europe/Guernsey
America/Grand_Turk
US/Pacific-New
Asia/Samarkand
America/Argentina/Cordoba
Asia/Phnom_Penh
Africa/Kigali
Asia/Almaty
US/Alaska
Asia/Dubai
Europe/Isle_of_Man
America/Araguaina
Cuba
Asia/Novosibirsk
America/Argentina/Salta
Etc/GMT+3
Africa/Tunis
Etc/GMT+2
Etc/GMT+1
Pacific/Fakaofo
Africa/Tripoli
Etc/GMT+0
Israel
Africa/Banjul
Etc/GMT+7
Indian/Comoro
Etc/GMT+6
Etc/GMT+5
Etc/GMT+4
Pacific/Port_Moresby
US/Arizona
Antarctica/Syowa
Indian/Reunion
Pacific/Palau
Europe/Kaliningrad
America/Montevideo
Africa/Windhoek
Asia/Karachi
Africa/Mogadishu
Australia/Perth
Brazil/East
Etc/GMT
Asia/Chita
Pacific/Easter
Antarctica/Davis
Antarctica/McMurdo
Asia/Macao
America/Manaus
Africa/Freetown
Europe/Bucharest
Asia/Tomsk
America/Argentina/Mendoza
Asia/Macau
Europe/Malta
Mexico/BajaSur
Pacific/Tahiti
Africa/Asmera
Europe/Busingen
America/Argentina/Rio_Gallegos
Africa/Malabo
Europe/Skopje
America/Catamarca
America/Godthab
Europe/Sarajevo
Australia/ACT
GB-Eire
Africa/Lagos
America/Cordoba
Europe/Rome
Asia/Dacca
Indian/Mauritius
Pacific/Samoa
America/Regina
America/Fort_Wayne
America/Dawson_Creek
Africa/Algiers
Europe/Mariehamn
America/St_Johns
America/St_Thomas
Europe/Zurich
America/Anguilla
Asia/Dili
America/Denver
Africa/Bamako
GB
Mexico/General
Pacific/Wallis
Europe/Gibraltar
Africa/Conakry
Africa/Lubumbashi
Asia/Istanbul
America/Havana
NZ-CHAT
Asia/Choibalsan
America/Porto_Acre
Asia/Omsk
Europe/Vaduz
US/Michigan
Asia/Dhaka
America/Barbados
Europe/Tiraspol
Atlantic/Cape_Verde
Asia/Yekaterinburg
America/Louisville
Pacific/Johnston
Pacific/Chatham
Europe/Ljubljana
America/Sao_Paulo
Asia/Jayapura
America/Curacao
Asia/Dushanbe
America/Guyana
America/Guayaquil
America/Martinique
Portugal
Europe/Berlin
Europe/Moscow
Europe/Chisinau
America/Puerto_Rico
America/Rankin_Inlet
Pacific/Ponape
Europe/Stockholm
Europe/Budapest
America/Argentina/Jujuy
Australia/Eucla
Asia/Shanghai
Universal
Europe/Zagreb
America/Port_of_Spain
Europe/Helsinki
Asia/Beirut
Asia/Tel_Aviv
Pacific/Bougainville
US/Central
Africa/Sao_Tome
Indian/Chagos
America/Cayenne
Asia/Yakutsk
Pacific/Galapagos
Australia/North
Europe/Paris
Africa/Ndjamena
Pacific/Fiji
America/Rainy_River
Indian/Maldives
Australia/Yancowinna
SystemV/AST4
Asia/Oral
America/Yellowknife
Pacific/Enderbury
America/Juneau
Australia/Victoria
America/Indiana/Vevay
Asia/Tashkent
Asia/Jakarta
Africa/Ceuta
Asia/Barnaul
America/Recife
America/Buenos_Aires
America/Noronha
America/Swift_Current
Australia/Adelaide
America/Metlakatla
Africa/Djibouti
America/Paramaribo
Europe/Simferopol
Europe/Sofia
Africa/Nouakchott
Europe/Prague
America/Indiana/Vincennes
Antarctica/Mawson
America/Kralendijk
Antarctica/Troll
Europe/Samara
Indian/Christmas
America/Antigua
Pacific/Gambier
America/Indianapolis
America/Inuvik
America/Iqaluit
Pacific/Funafuti
UTC
Antarctica/Macquarie
Canada/Pacific
America/Moncton
Africa/Gaborone
Pacific/Chuuk
Asia/Pyongyang
America/St_Vincent
Asia/Gaza
Etc/Universal
PST8PDT
Atlantic/Faeroe
Asia/Qyzylorda
Canada/Newfoundland
America/Kentucky/Louisville
America/Yakutat
Asia/Ho_Chi_Minh
Antarctica/Casey
Europe/Copenhagen
Africa/Asmara
Atlantic/Azores
Europe/Vienna
ROK
Pacific/Pitcairn
America/Mazatlan
Australia/Queensland
Pacific/Nauru
Europe/Tirane
Asia/Kolkata
SystemV/MST7
Australia/Canberra
MET
Australia/Broken_Hill
Europe/Riga
America/Dominica
Africa/Abidjan
America/Mendoza
America/Santarem
Kwajalein
America/Asuncion
Asia/Ulan_Bator
NZ
America/Boise
Australia/Currie
EST5EDT
Pacific/Guam
Pacific/Wake
Atlantic/Bermuda
America/Costa_Rica
America/Dawson
Asia/Chongqing
Eire
Europe/Amsterdam
America/Indiana/Knox
America/North_Dakota/Beulah
Africa/Accra
Atlantic/Faroe
Mexico/BajaNorte
America/Maceio
Etc/UCT
Pacific/Apia
GMT0
America/Atka
Pacific/Niue
Canada/East-Saskatchewan
Australia/Lord_Howe
Europe/Dublin
Pacific/Truk
MST7MDT
America/Monterrey
America/Nassau
America/Jamaica
Asia/Bishkek
America/Atikokan
Atlantic/Stanley
Australia/NSW
US/Hawaii
SystemV/CST6
Indian/Mahe
Asia/Aqtobe
America/Sitka
Asia/Vladivostok
Africa/Libreville
Africa/Maputo
Zulu
America/Kentucky/Monticello
Africa/El_Aaiun
Africa/Ouagadougou
America/Coral_Harbour
Pacific/Marquesas
Brazil/West
America/Aruba
America/North_Dakota/Center
America/Cayman
Asia/Ulaanbaatar
Asia/Baghdad
Europe/San_Marino
America/Indiana/Tell_City
America/Tijuana
Pacific/Saipan
SystemV/YST9
Africa/Douala
America/Chihuahua
America/Ojinaga
Asia/Hovd
America/Anchorage
Chile/EasterIsland
America/Halifax
Antarctica/Rothera
America/Indiana/Indianapolis
US/Mountain
Asia/Damascus
America/Argentina/San_Luis
America/Santiago
Asia/Baku
America/Argentina/Ushuaia
Atlantic/Reykjavik
Africa/Brazzaville
Africa/Porto-Novo
America/La_Paz
Antarctica/DumontDUrville
Asia/Taipei
Antarctica/South_Pole
Asia/Manila
Asia/Bangkok
Africa/Dar_es_Salaam
Poland
Atlantic/Madeira
Antarctica/Palmer
America/Thunder_Bay
Africa/Addis_Ababa
Europe/Uzhgorod
Brazil/DeNoronha
Asia/Ashkhabad
Etc/Zulu
America/Indiana/Marengo
America/Creston
America/Mexico_City
Antarctica/Vostok
Asia/Jerusalem
Europe/Andorra
US/Samoa
PRC
Asia/Vientiane
Pacific/Kiritimati
America/Matamoros
America/Blanc-Sablon
Asia/Riyadh
Iceland
Pacific/Pohnpei
Asia/Ujung_Pandang
Atlantic/South_Georgia
Europe/Lisbon
Asia/Harbin
Europe/Oslo
Asia/Novokuznetsk
CST6CDT
Atlantic/Canary
America/Knox_IN
Asia/Kuwait
SystemV/HST10
Pacific/Efate
Africa/Lome
America/Bogota
America/Menominee
America/Adak
Pacific/Norfolk
Europe/Kirov
America/Resolute
Pacific/Tarawa
Africa/Kampala
Asia/Krasnoyarsk
Greenwich
SystemV/EST5
America/Edmonton
Europe/Podgorica
Australia/South
Canada/Central
Africa/Bujumbura
America/Santo_Domingo
US/Eastern
Europe/Minsk
Pacific/Auckland
Africa/Casablanca
America/Glace_Bay
Canada/Eastern
Asia/Qatar
Europe/Kiev
Singapore
Asia/Magadan
SystemV/PST8
America/Port-au-Prince
Europe/Belfast
America/St_Barthelemy
Asia/Ashgabat
Africa/Luanda
America/Nipigon
Atlantic/Jan_Mayen
Brazil/Acre
Asia/Muscat
Asia/Bahrain
Europe/Vilnius
America/Fortaleza
Etc/GMT0
US/East-Indiana
America/Hermosillo
America/Cancun
Africa/Maseru
Pacific/Kosrae
Africa/Kinshasa
Asia/Kathmandu
Asia/Seoul
Australia/Sydney
America/Lima
Australia/LHI
America/St_Lucia
Europe/Madrid
America/Bahia_Banderas
America/Montserrat
Asia/Brunei
America/Santa_Isabel
Canada/Mountain
America/Cambridge_Bay
Asia/Colombo
Australia/West
Indian/Antananarivo
Australia/Brisbane
Indian/Mayotte
US/Indiana-Starke
Asia/Urumqi
US/Aleutian
Europe/Volgograd
America/Lower_Princes
America/Vancouver
Africa/Blantyre
America/Rio_Branco
America/Danmarkshavn
America/Detroit
America/Thule
Africa/Lusaka
Asia/Hong_Kong
Iran
America/Argentina/La_Rioja
Africa/Dakar
SystemV/CST6CDT
America/Tortola
America/Porto_Velho
Asia/Sakhalin
Etc/GMT+10
America/Scoresbysund
Asia/Kamchatka
Asia/Thimbu
Africa/Harare
Etc/GMT+12
Etc/GMT+11
Navajo
America/Nome
Europe/Tallinn
Turkey
Africa/Khartoum
Africa/Johannesburg
Africa/Bangui
Europe/Belgrade
Jamaica
Africa/Bissau
Asia/Tehran
WET
Europe/Astrakhan
Africa/Juba
America/Campo_Grande
America/Belem
Etc/Greenwich
Asia/Saigon
America/Ensenada
Pacific/Midway
America/Jujuy
Africa/Timbuktu
America/Bahia
America/Goose_Bay
America/Virgin
America/Pangnirtung
Asia/Katmandu
America/Phoenix
Africa/Niamey
America/Whitehorse
Pacific/Noumea
Asia/Tbilisi
America/Montreal
Asia/Makassar
America/Argentina/San_Juan
Hongkong
UCT
Asia/Nicosia
America/Indiana/Winamac
SystemV/MST7MDT
America/Argentina/ComodRivadavia
America/Boa_Vista
America/Grenada
Australia/Darwin
Asia/Khandyga
Asia/Kuala_Lumpur
Asia/Thimphu
Asia/Rangoon
Europe/Bratislava
Asia/Calcutta
America/Argentina/Tucuman
Asia/Kabul
Indian/Cocos
Japan
Pacific/Tongatapu
America/New_York
Etc/GMT-12
Etc/GMT-11
Etc/GMT-10
SystemV/YST9YDT
Europe/Ulyanovsk
Etc/GMT-14
Etc/GMT-13
W-SU
America/Merida
EET
America/Rosario
Canada/Saskatchewan
America/St_Kitts
Arctic/Longyearbyen
America/Fort_Nelson
America/Caracas
America/Guadeloupe
Asia/Hebron
Indian/Kerguelen
SystemV/PST8PDT
Africa/Monrovia
Asia/Ust-Nera
Egypt
Asia/Srednekolymsk
America/North_Dakota/New_Salem
Asia/Anadyr
Australia/Melbourne
Asia/Irkutsk
America/Shiprock
America/Winnipeg
Europe/Vatican
Asia/Amman
Etc/UTC
SystemV/AST4ADT
Asia/Tokyo
America/Toronto
Asia/Singapore
Australia/Lindeman
America/Los_Angeles
SystemV/EST5EDT
Pacific/Majuro
America/Argentina/Buenos_Aires
Europe/Nicosia
Pacific/Guadalcanal
Europe/Athens
US/Pacific
Europe/Monaco

La salida anterior representa todos los ZoneId disponibles, veamos como crear un objeto ZoneId utilizándolos:

ZoneId zoneIdMx = ZoneId.of("America/Mexico_City");
ZoneId zoneIdAm = ZoneId.of("Europe/Amsterdam");

Lo anterior crea dos objetos de la clase ZoneId uno con la zona de la Ciudad de México y otro con la de Amsterdam, ahora simularemos un vuelo que sale de la Ciudad de México rumbo a Amsterdam y veremos la hora de salida y la de aterrizaje en las dos zonas horarias.

ZoneId zoneIdMx = ZoneId.of("America/Mexico_City");
ZoneId zoneIdAm = ZoneId.of("Europe/Amsterdam");
ZonedDateTime departureDate = ZonedDateTime.of(2018, 10, 31, 2, 39, 0, 0, zoneIdMx);

System.out.println("Departure date: " + departureDate);
System.out.println("Arrival in México time: " + departureDate.plusHours(12));
System.out.println("Arrival in Amsterdam time: " + ZonedDateTime.ofInstant(departureDate.plusHours(12).toInstant(), zoneIdAm));

Salida:

Departure date: 2018-10-31T02:39-06:00[America/Mexico_City]
Arrival in México time: 2018-10-31T14:39-06:00[America/Mexico_City]
Arrival in Amsterdam time: 2018-10-31T21:39+01:00[Europe/Amsterdam]

El código anterior nos permite agregar el tiempo del vuelo y ver la hora en el destino y en el origen.

Period

La clase Period se utiliza para modificar valores de una fecha u obtener la diferencia entre dos fechas.

En el siguiente ejemplo tomaré mi cumpleaños 19 de agosto y calcularé periodo de tiempo que falta desde la fecha actual que es 31 de octubre del 2018:

LocalDate currentTime = LocalDate.now();

LocalDate myBirthDate = LocalDate.parse("2019-08-19");

Period period = Period.between(currentTime, myBirthDate);
System.out.println(
		String.format("Years %d Months %d Days %d", period.getYears(), period.getMonths(), period.getDays()));

Salida:

Years 0 Months 9 Days 19

Podemos ver que el periodo que falta son 0 años 9 meses y 19 días.

Duration

La clase Duration funciona de forma similar que Period la única diferencia es que en lugar de trabajar con fechas trabaja con tiempo, veamos el siguiente ejemplo:

Es la 1:35 pm y mi hora de salida del trabajo es a las 5:30 pm calculemos cuantos minutos faltan para salir:

LocalTime currentTime = LocalTime.of(1, 35);
LocalTime timeToLeave=LocalTime.of(5, 30);
Duration duration = Duration.between(currentTime, timeToLeave);
System.out.println(String.format("Minutes  %s", duration.toMinutes()));

Salida:

Minutes  235

Estos posts explican como facilitar el uso de fechas y horas lo cual se vuelve un dolor de cabeza para muchos desarrolladores. Para estar al pendiente sobre nuestro contenido nuevo síguenos en nuestras redes sociales  https://www.facebook.com/devs4j/ y https://twitter.com/devs4j.

Autor: Alejandro Agapito Bautista

Twitter: @raidentrance

Contacto:raidentrance@gmail.com

Java 8 manejo de fechas y tiempo : LocalDate, LocalTime y LocalDateTime


localdatesUno de los problemas más comunes a los que se enfrentan los desarrolladores es el manejo de fechas, tiempo y zonas horarias, para resolver este problema se solían utilizar api’s como Joda Time, Java 8 incluyó un conjunto de apis que nos ayudan a resolverlo sin incluir dependencias adicionales.

LocalDate, LocalTime y LocalDateTime

Las clases más comunes para el manejo de fechas con java 8 son LocalDate, LocalTime y LocalDateTime, se utilizan cuando la zona horaria no es requerida.

LocalDate

Un LocalDate representa una fecha en formato ISO (yyyy-MM-dd) sin tiempo. Veamos algunos ejemplos:

LocalDate date = LocalDate.now();
System.out.println(date);

El código anterior tendrá la siguiente salida(Entendiendo que el post se escribió el 30-10-2018):

2018-10-30

Como vemos es una fecha sin tiempo y sin zona horaria. Veamos algunas otras formas de crear un LocalDate:

LocalDate date2 = LocalDate.of(2018, 10, 30);
LocalDate date3 = LocalDate.parse("2018-10-30");

Las 2 expresiones crearán objetos de tipo LocalDate con la fecha del 30-10-2018.

Operaciones que se pueden realizar con LocalDate

Una vez que sabemos como construir un LocalDate el siguiente paso será conocer el tipo de operaciones que se pueden realizar con el.

  • Manipulación de fechas (Sumar o restar días, meses, años, etc ):
LocalDate date = LocalDate.parse("2018-10-30");
LocalDate newDate = date.plusDays(10);
System.out.println(date);
System.out.println(newDate);

Salida:

2018-10-30
2018-11-09

Como vemos a la fecha inicial se le sumaron 10 días y el mes se actualizó de forma automática, esto nos permite evitar considerar el número de días en un mes, el horario de verano, etc.

LocalDate date = LocalDate.parse("2018-10-30");
LocalDate newDate = date.plusMonths(3);
System.out.println(date);
System.out.println(newDate);

Salida:

2018-10-30
2019-01-30

De igual forma podemos hacerlo con los meses y LocalDate resolverá si es necesario cambiar de año.

Recordemos que cuando hacemos operaciones sobre las fechas debemos asignar la respuesta a una nueva referencia ya que el objeto original no se modificará puesto que los objetos LocalDate son immutables.

  • Comparación entre fechas

Así como podemos realizar operaciones entre las fechas podemos hacer comparaciones entre ellas, veamos algunos ejemplos:

Valida si una fecha es antes que otra:

System.out.println(LocalDate.parse("2018-10-30").isBefore(LocalDate.parse("2018-10-31")));

Salida

true

Valida si un año es bisiesto:

System.out.println(LocalDate.parse("2018-10-30").isLeapYear());

Salida:

false

Podemos realizar validaciones muy simples sin necesidad de escribir código complejo.

  • Obtener información sobre alguna fecha

El siguiente paso será obtener información sobre alguna fecha en específico veamos algunos ejemplos:

Obtener el día de la semana de mi cumpleaños:

System.out.println(LocalDate.parse("2019-08-19").getDayOfWeek());

Salida:

MONDAY

Extraer información de una fecha:

System.out.println(LocalDate.parse("2019-08-19").getDayOfWeek());

Salida:

19

Si lo único que necesitamos de una fecha es el mes podemos fácilmente extraerlo .

Podemos ver el detalle de todos los métodos disponibles de la clase LocalDate en el siguiente link.

LocalTime

LocalTime representa una hora sin la fecha, del mismo modo que con LocalDate podemos crearlo haciendo uso de los métodos now(), parse(..) y of(..), veamos algunos ejemplos:

LocalTime time = LocalTime.now();
LocalTime time2 = LocalTime.parse("11:00:59.759");
LocalTime time3 = LocalTime.of(11, 00, 59);
System.out.println(time);
System.out.println(time2);
System.out.println(time3);

Salida:

11:02:06.198
11:00:59.759
11:00:59

Las 3 anteriores son formas válidas de crear un objeto LocalTime. Veamos algunas de las operaciones que podemos realizar.

  • Modificar un LocalTime

La primera operación que veremos es como modificar un LocalTime:

LocalTime time = LocalTime.parse("11:00:59.759");
LocalTime time2 = time.plusHours(1);
System.out.println(time2);

Salida:

12:00:59.759

Con el método plusHours crearemos un nuevo LocalTime con la nueva hora calculada.

  • Validar un LocalTime

El siguiente punto será hacer validaciones sobre un LocalTime:

LocalTime time = LocalTime.parse("11:00:59.759");
LocalTime time2 = LocalTime.parse("12:00:59.759");
System.out.println(time.isBefore(time2));

Salida:

true

Usando métodos como isBefore podremos saber si una hora es mayor a otra.

  • Extraer información de una hora:

El siguiente paso será extraer solo una parte del objeto LocalTime:

LocalTime time = LocalTime.parse("11:00:59.759");
System.out.println(time.getHour());

Salida:

 11 

Esto lo utilizaremos en caso de que nuestra aplicación utilice solo la hora para realizar algún proceso.

LocalDateTime

La siguiente clase a analizar será LocalDateTime la cual representa una combinación entre LocalDate y LocalTime, veamos como crearlo:

LocalDateTime dateTime = LocalDateTime.now();
LocalDateTime dateTime1=LocalDateTime.of(2018, 10, 10, 11, 25);
LocalDateTime dateTime2=LocalDateTime.parse("2018-10-10T11:25");

System.out.println(dateTime);
System.out.println(dateTime1);
System.out.println(dateTime2);

Salida:

2018-10-30T11:46:58.274
2018-10-10T11:25
2018-10-10T11:25

En la salida anterior podemos ver como la salida incluye fecha y hora por cada objeto. Veamos algunas de las operaciones que podemos realizar con estos objetos.

  • Manipular un LocalDateTime:

De igual forma que con los anteriores podemos realzar manipulaciones sobre el LocalDateTime:

LocalDateTime dateTime=LocalDateTime.parse("2018-10-10T11:25");
LocalDateTime newDateTime = dateTime.plusDays(1).plusHours(2);

System.out.println(newDateTime);

Lo anterior creará un objeto LocalDateTime le agregará 1 día y después 2 horas, recordemos que debemos asignar el resultado a una nueva referencia ya que el objeto original no se modificará sino que se devolverá uno nuevo.

  • Realizar validaciones sobre un LocalDateTime

Como lo vimos en los ejemplos anteriores podemos realizar validaciones sobre el LocalDateTime:

LocalDateTime dateTime=LocalDateTime.parse("2018-10-10T11:25");
LocalDateTime dateTime2=LocalDateTime.parse("2019-10-10T11:25");

System.out.println(dateTime.isBefore(dateTime2));

Salida:

true

Se realizará del mismo modo que en los casos anteriores solo que ahora considerará tanto la fecha como la hora.

Esta es una guía rápida sobre como utilizar algunas clases del api de fechas y tiempo de java, en futuros posts hablaremos sobre como trabajar con zonas horarias, periodos e instantes. Para estar al pendiente sobre nuestro contenido nuevo síguenos en nuestras redes sociales  https://www.facebook.com/devs4j/ y https://twitter.com/devs4j.

Autor: Alejandro Agapito Bautista

Twitter: @raidentrance

Contacto:raidentrance@gmail.com