I’d like to model an Address as a value object. As it is a good practice to make it immutable, I chose not to provide any setter, that might allow to modify it later.
A common approach is to pass the data to the constructor; however, when the value object is pretty big, that may become quite bloated:
class Address {
public function __construct(
Point $location,
$houseNumber,
$streetName,
$postcode,
$poBox,
$city,
$region,
$country) {
// ...
}
}
Another approach whould be to provide the arguments as an array, resulting in a clean constructor, but that might mess up the implementation of the 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'];
}
// ...
}
}
That also looks a bit unnatural to me.
Any advice on how to correctly implement a pretty big value object?
The main issue with large list of parameters is readability and the danger that you will mix up parameters. You can tackle these issues with Builder pattern as described in Effective Java. It makes code more readable (especially languages that don’t support named and optional parameters):
The advantages:
One disadvantage I can think of is that forgetting to specify one of the arguments (not calling
WithHouseNumberfor example) will result in a run time error, instead of compile time error when using constructor. You should also consider using more Value Objects like PostalCode for example (as oppose to passing a string).On a related note, sometimes business requirements call for changing part of the Value Object. For example, when address was originally entered, the street number might have been misspelled and needs to be corrected now. Since you modeled Address as an immutable object there is not setter. One possible solution to this problem is to introduce a ‘Side-Effect-Free function’ on the Address Value Object. The function would return a copy of the object itself with the exception of a new street name: