example java oop nullpointerexception null null-object-pattern

java - example - Patrón de objeto nulo



nodelist java (6)

Parece que hay una creciente comunidad de personas que dicen que nunca debes devolver el valor nulo y que siempre debes usar el Patrón de objeto nulo. Puedo ver la utilidad del NOP al usar una colección / mapa / matriz o llamar a funciones booleanas como isAuthenticated (), que se muestra aquí .

No he encontrado nada en esto que sea completamente convincente. Ten paciencia conmigo aquí mientras trato de organizar mis pensamientos.

Mi entendimiento es que en lugar de devolver un objeto nulo, devuelve un objeto válido que se ha "puesto a cero".

Entonces, por ejemplo, el cliente haría una llamada para obtener un objeto:

Car car = getCar();

Si no usa el NOP, deberá verificar si el objeto devuelto por getCar () es nulo antes de llamar a algún método:

if (car != null){ color = car.getColor(); doScreenStuff(color); }

Usando el NOP, en lugar de que getCar() devuelva un valor nulo, ahora devuelve un Objeto que efectivamente ha sido "puesto a cero". Así que ahora ya no tenemos que hacer if (car != null) y solo podemos solicitar el color. Entonces, supongo que nuestro objeto "a cero" devolvería "ninguno" cuando llamamos color.

¿Cómo ayuda esto? Parece que avanzar y llamar a métodos en un objeto vacío causa tanto dolor como solo marcar nulo. Ahora, cuando llega el momento de mostrar la información, debemos verificar que el color no sea "ninguno", que la altura no sea 0 o cualquier otro valor que tenga. Básicamente, en lugar de verificar al principio del procesamiento si el automóvil es nulo, luego verifica si el objeto del automóvil que tenemos es un automóvil real o un sustituto. Es decir, no queremos mostrar un montón de objetos vacíos, por lo que necesitamos alguna forma de filtrar todos nuestros objetos vacíos.

Este filtrado es un paso adicional como llamar a if (car! = Null). La única diferencia es que al marcar nulo, podemos detener el procesamiento en cuanto descubrimos que el objeto del automóvil es nulo al lanzar una excepción, mientras que con NOP llamamos a los métodos en el objeto vacío y seguimos avanzando hasta que llega el momento de Muestra el objeto y en este punto filtramos los vacíos. Además, necesita conocer los valores devueltos por su objeto vacío. IE devuelve getColor () "none" o "empty".

Obviamente debe haber algo que estoy pasando por alto. Gracias por adelantado.


El objeto puntero nulo, al menos en mi experiencia, es por lo que limita sus comprobaciones nulas a una ubicación central para evitar excepciones de puntero nulo.

Si muchos servicios utilizan CarFactory, es mucho más fácil que Carfactory maneje el resultado nulo, y luego que cada servicio individual lo maneje. Además, garantiza que cada resultado nulo se maneje de la misma manera, ya sea que no se haga nada o se realice una lógica específica. La desventaja es que si no se maneja correctamente, podría dar lugar a algunos errores temporalmente confusos (especialmente porque las excepciones de puntero nulo gritan fuerte y orgullosa).

Realmente ya no lo uso mucho. Existen alternativas al uso de controles nulos, como el uso de Java 8 Opcional. Hay personas que están a favor y en contra de esto también, y esto de ninguna manera es un reemplazo para el patrón de objeto nulo.

String result = Optional.ofNullable(someInteger) .map(Integer::valueOf) .map(i -> i + 1) .map(Object::toString) .orElse("number not present"); System.out.println(result);


El patrón de diseño NULL llena una AUSENCIA de un objeto con un comportamiento POR DEFECTO y se debe usar solo cuando un objeto colabora con otro.

El patrón de diseño NULL no pretende reemplazar el manejo de excepciones NULL. Es uno de los beneficios secundarios del patrón de diseño NULL, pero la intención es proporcionar un comportamiento predeterminado.

Por ejemplo, considere el siguiente ejemplo de código de pseduo. Es una clase de cliente simple que está delegando el cálculo de descuento a un objeto de descuento.

class Customer { IDiscount dis = new NormalDiscount(); public double CalculateDiscount() { return dis.Calculate(); } // Code removed for simplicity }

Ahora digamos que queremos crear clientes que hayan incumplido con los pagos. Por lo tanto, heredamos de la clase anterior ya que muchas propiedades y comportamientos son iguales, pero no queremos el cálculo de descuento porque un cliente predeterminado no es elegible para descuentos. Así que hacemos que el objeto de descuento sea nulo, pero eso es un problema porque ahora el cálculo del descuento se bloqueará debido al objeto de descuento NULL.

class DefaultedCustomer : Customer { IDiscount dis = null; public double CalculateDiscount() { return dis.Calculate(); <---- This will crash in parent } // Code removed for simplicity }

Así que una de las maneras en que los desarrolladores solucionan esto es mi comprobación de NULL y el retorno de cero. Si cierra los ojos y piensa lógicamente, este es realmente un escenario de negocios donde no hay descuentos para el cliente predeterminado.

Entonces, en lugar de arreglar esto técnicamente al marcar NULL, podemos crear una clase de COMPORTAMIENTO POR DESCUENTO POR DEFECTO, como se muestra a continuación, que devuelve un descuento de cero y usa el mismo para el cliente predeterminado. Esto es más limpio que comprobar NULLs.

public class DefaultDiscount : IDiscount { public void Calculate() { return 0; } }

Los NULL son necesarios, pero para el manejo de excepciones no es para arreglar la ausencia de un objeto en colaboración como se muestra arriba. El patrón de diseño NULL no tiene sentido si no hay colaboración.

Debido al patrón de diseño NULL, se evitan las comprobaciones NULL, pero eso es más de un efecto secundario y un beneficio secundario, pero no la intención principal.

Todos los pensamientos anteriores he tomado de este patrón de diseño NULL en C #, que explica Do''s and Donts of NULL.


El patrón de objeto nulo solo tiene sentido cuando hay un valor funcional razonable para el objeto nulo. El propósito no es diferir nulo, como ha descrito, sino eliminar completamente la idea de nulo representando la nada o el vacío con un dato real que aún es funcional. Por ejemplo, el caso natural de agujeros en una estructura de árbol, como se describe en el artículo de Wikipedia .

Un coche nulo no tiene sentido. En este caso, parece que lo más apropiado sería que getCar() devuelva Optional<Car> .


La respuesta de MattPutnam es acertada, y la secundé. Yo agregaría esto: el concepto de "objeto nulo", cuando lo analizas, parece reducirse al concepto matemático de un monoid . Puede pensarlo de esta manera: un monoide es un tipo que tiene ambas cosas:

  1. Una "adición", "suma" o operación similar, que debe ser asociativa : a.op(b).op(c) es lo mismo que a.op(b.op(c)) .
  2. Un valor "vacío", "cero" o "nulo", que actúa como elemento neutral o elemento de identidad de la operación.

El ejemplo clásico del patrón de objeto nulo es devolver una lista o matriz vacía en lugar de nulo. Bueno, las listas son un monoide, con la adición como la operación y la lista vacía como el elemento neutral.

Ahora, el problema al que te enfrentas en tu ejemplo de Car es que el Car no es realmente un monoide; no hay una noción de "carro vacío" o "carro neutral", y realmente no hay una operación sensata que puedas usar para combinar dos Car en uno.

Entonces, la recomendación que está recibiendo correctamente es usar algo como el Java 8 Optional . Y el truco es que no importa qué tipo T sea, Optional<T> es un monoide:

  1. La operación "combinar" del monoide es "elige el primer valor si no está empty , de lo contrario, elige el segundo valor":
    • x || empty = x
    • empty || x = x
  2. El elemento neutral es Optional.empty() , porque Optional.empty().orElse(anything) es lo mismo que anything .

Básicamente, Optional<T> es un contenedor que agrega un objeto nulo a tipos como Car que no tienen uno. El método Optional<T>.orElse(T value) que es una versión ligeramente refactorizada del monoide "elija primero empty valor no empty ".


Si no ve el punto, entonces probablemente no sea un buen paradigma para usted. La idea general de la programación OO es simplificar las cosas para USTED. No se deje atrapar por el pensamiento de que necesita adoptar un sistema basado en patrones elaborado por otra persona. A menudo se necesita una cantidad significativa de trabajo para aprender varios patrones y usarlos de manera efectiva, por lo que es mejor crecer en ellos, en lugar de intentar forzarte a usarlos.

En lo que respecta a este patrón en particular, asume un cierto estilo de programación que puede ser inadecuado para usted. Nunca lo usaría porque devuelvo valores nulos como valores legítimos (datos faltantes) que se manejan de manera diferente en cada caso, por lo que el "manejo centralizado" no tiene sentido para mí. Cuando devuelvo booleanos, uso primitivos.

La conclusión aquí es que si un patrón no te parece natural, no lo uses.


Si se devuelve Opcional a través del automóvil, no se eliminará el paso de verificar si el objeto del automóvil está realmente presente o no.

Al devolver un auto, usted verificará si (car! = Null). De manera similar, al acceder a la Opcional, DEBE verificar si (car.isPresent ()) y luego car.get ().

Hacer un get () sin verificar la presencia no es aceptable y se puede identificar fácilmente y evitar usar la configuración de estilo de verificación para generar errores.

Podría hacer la pregunta ... ¿Qué ventaja obtenemos de esto si comprobamos su presencia en ambos patrones?

La respuesta está en saber que tienes que hacer una verificación antes de usarla. Si sigue estrictamente la práctica de devolver Opcional donde pueda ser anulable, entonces no necesita verificar los nulos donde no sea opcional.

Actúa como una forma programática de documentar métodos que internamente ayudan a aplicar su cheque a través de los estilos de verificación.