Ejemplo del patrón de diseño No Operation

Escrito por el , actualizado el .
java planeta-codigo programacion
Enlace permanente Comentarios

Java

Hasta el momento ya he escrito alguna entrada sobre los patrones de diseño en general, sobre algunos casos particulares como el patrón de diseño Command relacionado con la programación concurrente y sobre el patrón de diseño State para hacer máquinas de estados. En esta entrada hablaré sobre otro patrón de diseño, el patrón No Operation y de que forma podemos aprovecharlo para resolver algún problema y hacer nuestro código más simple.

En un programa que emplea un lenguaje de programación orientado a objetos estos están constantemente relacionándose entre si a través de llamadas a métodos y a través de las referencias que un objeto posee de otros. Sin embargo, es habitual que un determinado método devuelva un null en vez de una referencia a un objeto. Esta referencia null puede ser un problema ya que nos obliga en el código hacer una comprobación antes de poder llamarlo. Si un método devuelve un null puede dar como resultado un NullPointerException en otra parte de la aplicación en donde se intente usar esa referencia y no se haga la comprobación.

Para tratar de evitar llenar nuestro código java de sentencias if con la comprobación de null podemos utilizar el patrón de diseño No Operation. La idea de este patrón es que en vez de devolver un null como resultado de la llamada a un método devolvamos un objeto que no haga nada en las llamadas a los métodos en los que se use. Por ejemplo, supongamos que tenemos un método que en base a un enum se encarga de devolver un objeto que sigue el patrón Command. Y ahora supongamos que para cierto valor del enum no hay objeto command que se pueda devolver, podríamos devolver null en cuyo caso nos veríamos obligados a realizar la comprobación por null o empleando la idea del patrón No Operation devolver un objeto que implemente la interfaz command en cuestión pero que no haga nada o haga una operación inocua. Si vemos que en un programa estamos llenándolo de sentencias if (objeto == null) tal vez podamos aplicar este patrón. Lo importante para poder eliminar esos if es determinar que es una operación inocua, si se trata de un objeto puede ser que el método no haga nada, si se trata de un número que se utiliza para sumar o multiplicar se puede devolver 0 o 1 respectivamente en vez de null, depende del caso y la operación a simular.

Esta patrón puede usarse también para evitar la excepción NullPointerException pero no es tanto la misión del patrón la misión como evitar preocuparnos por si las referencias son null o no y eliminar ifs, es cierto que empleándolo no dará la excepción pero si la aplicación continua puede producir otra excepción o un comportamiento no deseado más complicado de resolver y de averiguar su causa en otra parte del código.

Veámoslo con el ejemplo de una factoría que para determinados enumerados se devuelve un objeto que sigue el patrón command pero para ciertos valores del enumerado no hay command válido y en vez de devolver null devolvemos un command no operation, este es el caso de llamar a la factoría con un enumerado null. Para el enumerado Operacion.MENSAJE se devuelve un command que emite un mensaje, para Operacion.NO_MENSAJE y null se devuelve un command que no hace nada.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package io.github.picodotdev.pattern.nooperation;

public class OperacionCommandFactory {

	public enum Operacion {
		MENSAJE, NO_MENSAJE
	}
	
	public OperacionCommand buildCommand(Operacion operacion) {
		if (operacion == null) {
			return new NoOperacionCommand();
		}
		switch (operacion) {
			case MENSAJE:
				return new MensajeCommand();
			case NO_MENSAJE:
				return new NoOperacionCommand();
			default:
				throw new IllegalArgumentException();
		}
	}
}
OperacionCommandFactory.java
1
2
3
4
5
6
package io.github.picodotdev.pattern.nooperation;

public interface OperacionCommand {

	void operacion();
}
OperacionCommand.java
1
2
3
4
5
6
7
8
package io.github.picodotdev.pattern.nooperation;

public class NoOperacionCommand implements OperacionCommand {

	@Override
	public void operacion() {
	}
}
NoOperacionCommand.java
1
2
3
4
5
6
7
8
9
package io.github.picodotdev.pattern.nooperation;

public class MensajeCommand implements OperacionCommand {

	@Override
	public void operacion() {
		System.out.println("Hola mundo!");
	}
}
MensajeCommand.java

Y finalmente el caso de prueba donde puede verse que no hay ningún if ya que no se devuelve en ningún caso un null:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package io.github.picodotdev.pattern.nooperation

import io.github.picodotdev.pattern.nooperation.OperacionCommand
import io.github.picodotdev.pattern.nooperation.OperacionCommandFactory
import io.github.picodotdev.pattern.nooperation.OperacionCommandFactory.Operacion

import spock.lang.Specification

public class OperacionCommandFactorySpec extends Specification {

	private OperacionCommandFactory factory = null
	
	void setup() {
		factory = new OperacionCommandFactory()
	}
	
	void test1() {
		setup:
		OperacionCommand operacion = factory.buildCommand(Operacion.MENSAJE)
	
		when:
		operacion.operacion()
		
		then:
		1 == 1
	}
	
	void test2() {
		setup:
		OperacionCommand operacion = factory.buildCommand(Operacion.NO_MENSAJE)
	
		when:
		operacion.operacion()
		
		then:
		1 == 1
	}
	
	void test3() {
		setup:
		OperacionCommand operacion = factory.buildCommand(null)
	
		when:
		operacion.operacion()
		
		then:
		1 == 1
	}
}
OperacionCommandFactorySpec.groovy

Puedes obtener el código fuente completo del ejemplo de su repositorio de GitHub.


Comparte el artículo: