commerceguys / addressing

A PHP addressing library, powered by CLDR and Google's address data.
MIT License
922 stars 135 forks source link

Why use clone in Address object? #122

Closed devinfd closed 5 years ago

devinfd commented 5 years ago

Why do all the with* methods in the CommerceGuys\Addressing\Address object clone the address before setting their value? It would be much more simple to maintain one object.

bojanz commented 5 years ago

An address is a textbook example of a value object, defined by its properties. if you change one property, you no longer have the same address. The with* methods are taken from PSR-7 which uses the same design for its own interfaces.

Note that the AddressInterface only has getters, the ImmutableAddressInterface which contains the with* methods is optional. So you are free to implement your own Address object which implements just AddressInterface, and then define setters, if that's what you prefer.

devinfd commented 5 years ago

That makes sense. Ultimately what I was trying to achieve was a way to create an Address object and set its properties through an array of values. ie:

new Address([
    'ADMIN_AREA' => 'California',
    'ADDRESS_LINE_1' => '123 Main st',
    'LOCALITY' => 'any town',
    'POSTAL_CODE' => '91234',
]);

I ended up extending CommerceGuys\Addressing\Address and overwriting some of the methods. This made it easier to keep track of the "state" of the object. (but now I'm second guessing this approach given the value object explanation).

use CommerceGuys\Addressing\Address as BaseAddress;

class Address extends BaseAddress
{
    /**
     * The input map will hold all the information we put in to the class
     *
     * @var mixed
     * @access private
     */
    private $inputMap = array(
        'LOCALE' => 'withLocale',
        'ADMIN_AREA' => 'withAdministrativeArea',
        'LOCALITY' => 'withLocality',
        'RECIPIENT' => 'withGivenName',
        'ORGANIZATION' => 'withOrganization',
        'ADDRESS_LINE_1' => 'withAddressLine1',
        'ADDRESS_LINE_2' => 'withAddressLine2',
        'DEPENDENT_LOCALITY' => 'withDependentLocality',
        'POSTAL_CODE' => 'withPostalCode',
        'SORTING_CODE' => 'withSortingCode',
        'COUNTRY' => 'withCountryCode'
    );

    public function __construct($attributes)
    {
        foreach ($attributes as $attribute => $value) {
            $this->{$this->inputMap[$attribute]} ($value);
        }
    }

    public function withCountryCode($countryCode) {
        $this->countryCode = $countryCode;
        return $this;
    }
    ...
bojanz commented 5 years ago

Yeah, that's not what I would do. But in the end, it's your decision. This library is just a set of helpers.