2011-09-13 14 views
6

Me gustaría modelar un Address como un objeto de valor. Como es una buena práctica hacerlo inmutable, decidí no proporcionar ningún setter, que podría permitir modificarlo más tarde.DDD: ¿cómo mantener inmutable un objeto de valor complejo?

Un enfoque común es pasar los datos al constructor; Sin embargo, cuando el objeto de valor es bastante grande, que puede llegar a ser bastante hinchada:

class Address { 
    public function __construct(
     Point $location, 
     $houseNumber, 
     $streetName, 
     $postcode, 
     $poBox, 
     $city, 
     $region, 
     $country) { 
     // ... 
    } 
} 

Otro enfoque whould sea para proporcionar los argumentos como una matriz, lo que resulta en un constructor limpio, pero que podría estropear la aplicación de la constructor:

class Address { 
    public function __construct(array $parts) { 
     if (! isset($parts['location']) || ! $location instanceof Point) { 
      throw new Exception('The location is required'); 
     } 
     $this->location = $location; 
     // ... 
     if (isset($parts['poBox'])) { 
      $this->poBox = $parts['poBox']; 
     } 
     // ... 
    } 
} 

Eso también me parece un poco antinatural.

¿Algún consejo sobre cómo implementar correctamente un objeto de valor bastante grande?

+1

Personalmente, creo que si su objeto de valor es lo suficientemente grande como para causar problemas, debe dividirse en varios objetos de valor. El ejemplo de dirección parece estar bien para mi sensibilidad personal, pero si lo encuentras demasiado grande, tal vez se convierta en la ubicación + dirección + ciudad (donde la ciudad incluye la región y el país). – Domenic

+0

@Domenic: ¡ese es un enfoque interesante también! – Benjamin

Respuesta

12

El problema principal con large list of parameters es la legibilidad y el peligro de que mezcle los parámetros. Puede resolver estos problemas con Builder pattern como se describe en Effective Java. Esto hace que el código sea más legible (especialmente los idiomas que no apoyan con nombre y parámetros opcionales):

public class AddressBuilder { 
    private Point _point; 
    private String _houseNumber; 

    // other parameters 

    public AddressBuilder() { 
    } 

    public AddressBuilder WithPoint(Point point) { 
     _point = point; 
     return this; 
    } 

    public AddressBuilder WithHouseNumber(String houseNumber) { 
     _houseNumber = houseNumber; 
     return this; 
    } 

    public Address Build() { 
     return new Address(_point, _houseNumber, ...); 
    } 
} 

Address address = new AddressBuilder() 
    .WithHouseNumber("123") 
    .WithPoint(point) 
    .Build(); 

Las ventajas:

  • parámetros se nombran por lo que es más fácil de leer
  • más difícil de mezclar número de casa con la región
  • puede utilizar su propio orden de los parámetros
  • parámetros opcionales se puede omitir

Una desventaja que se me ocurre es que olvidar especificar uno de los argumentos (sin llamar a WithHouseNumber por ejemplo) dará como resultado un error de tiempo de ejecución, en lugar de error de tiempo de compilación cuando se utiliza el constructor. También debería considerar el uso de más objetos de valor como PostalCode por ejemplo (como se opone a pasar una cadena).

En una nota relacionada, a veces los requisitos comerciales requieren el cambio de parte del Objeto de valor. Por ejemplo, cuando la dirección se ingresó originalmente, es posible que el número de la calle esté mal escrito y deba corregirse ahora. Como modelaste la dirección como un objeto inmutable, no hay un setter. Una posible solución a este problema es introducir una 'función libre de efectos laterales' en el objeto de valor de dirección. La función devolvería una copia del objeto en sí mismo, con la excepción de un nuevo nombre de la calle:

public class Address { 
    private readonly String _streetName; 
    private readonly String _houseNumber; 

    ... 

    public Address WithNewStreetName(String newStreetName) { 
     // enforce street name rules (not null, format etc) 

     return new Address(
      newStreetName 
      // copy other members from this instance 
      _houseNumber); 
    } 

    ... 
} 
+0

Muy interesante respuesta, gracias. ¿Es eso lo que llamamos una Fábrica también, o es el patrón de Fábrica reservado a las Entidades? – Benjamin

+0

DDD Factory se puede usar para construir objetos de valor complejos. Este constructor puede considerarse una fábrica de DDD. – Dmitry

+0

¡Esta respuesta es correcta! – jpierson

-1

inmutable es apto para el cálculo concurrente, sin bloqueo y sin bloqueo, inmutable es de alto rendimiento y buena escalabilidad.

valor Objeto se puede ejecutar mejor en un sistema simultáneo, incluir en el sistema de distribución, reemplazar el antiguo VO con el nuevo VO, no es necesario actualizar, por lo que no hay bloqueo.

0

Este es un problema común con los ejemplos de diseño impulsado por el dominio. El experto en el dominio falta y esa es la persona que le diría qué es una dirección y sus requisitos. Sospecho que el experto en dominios le dirá que una dirección no tiene un punto. Puede ser capaz de producir un Punto desde una Dirección pero no requeriría un Punto. También un P.O. El cuadro no sería un valor separado en una Dirección.Es posible que necesite una clase de dirección de casilla postal (POBoxAddress) Estoy estableciendo esto porque esta clase parece que fue definida por un desarrollador que no es Experto de dominio de envío o facturación. Al hablar con el experto en dominios, puede reducir el recuento de parámetros de constructor.

2nd
Puede comenzar a agrupar los parámetros como objetos de valor. Puede crear un objeto de valor de Ciudad. Eso podría requerir la Ciudad, Región/Estado y País. Creo que el nombre de una ciudad no significa mucho a menos que conozca la región y el país. Diciendo Paris significa nada más que París, Illinois, EE. UU. O París, Île-de-France, FR le da una imagen completa. Por lo tanto, esto también reduciría el recuento de parámetros de conteo al objeto Dirección.

Si va por la carretera DDD encuentra un experto en dominio para el dominio que está codificando, no debe ser el experto. A veces, los problemas no deben solucionarse mediante un código o un ingenioso patrón de diseño.

Cuestiones relacionadas