DDD: как сохранить объект комплексного значения неизменным? - PullRequest
6 голосов
/ 13 сентября 2011

Я хотел бы смоделировать Address как объект значения.Так как это хорошая практика, чтобы сделать его неизменным, я решил не предоставлять никакого установщика, который мог бы позволить изменить его позже.

Обычный подход - это передача данных конструктору;однако, когда объект значения довольно большой, он может стать довольно раздутым:

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

Другой подход заключается в предоставлении аргументов в виде массива, что приводит к чистому конструктору, но это может испортить реализациюконструктора:

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'];
        }
        // ...
    }
}

Это также выглядит немного неестественно для меня.

Любой совет, как правильно реализовать довольно большой объект значения?

Ответы [ 3 ]

12 голосов
/ 13 сентября 2011

Основная проблема с большим списком параметров - удобочитаемость и опасность того, что вы перепутаете параметры. Вы можете решить эти проблемы с помощью Pattern Builder , как описано в Effective Java . Это делает код более читабельным (особенно языки, которые не поддерживают именованные и необязательные параметры):

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();

Преимущества:

  • параметры названы так, чтобы они были более читабельными
  • сложнее перепутать номер дома с регионом
  • можете использовать свой собственный порядок параметров
  • необязательные параметры могут быть опущены

Один недостаток, о котором я могу подумать, состоит в том, что забывание указать один из аргументов (например, не вызывая WithHouseNumber) приведет к ошибке времени выполнения вместо ошибки времени компиляции при использовании конструктора. Вам также следует рассмотреть возможность использования большего количества объектов-значений, таких как, например, PostalCode (в отличие от передачи строки).

В связанной заметке иногда бизнес-требования требуют изменения части объекта значения. Например, когда адрес был введен изначально, номер улицы мог быть написан с ошибкой и должен быть исправлен сейчас. Так как вы смоделировали Address как неизменный объект, здесь нет сеттера. Одним из возможных решений этой проблемы является введение «функции без побочных эффектов» в объекте значения адреса. Функция будет возвращать копию самого объекта, за исключением нового названия улицы:

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 голосов
/ 27 апреля 2017

Это общая проблема с примерами доменного дизайна.Эксперт по доменам отсутствует, и это человек, который скажет вам, что такое адрес и его требования.Я подозреваю, что Эксперт по домену скажет вам, что у Адреса нет Точки.Возможно, вы сможете создать точку из адреса, но для этого не потребуется точка.Также почтовый ящик не будет отдельным значением в адресе.Возможно, вам нужен адресный класс почтового ящика (POBoxAddress). Я говорю об этом, потому что этот класс выглядит так, как будто он был определен разработчиком, а не экспертом по отгрузке или выставлению счетов.Поговорив с экспертом домена, вы можете уменьшить количество параметров конструктора.

2nd
Вы можете начать группировать параметры как объекты-значения.Вы можете создать объект стоимости города.Это может потребовать города, региона / штата и страны.Я думаю, что название города мало что значит, если я не знаю регион и страну.Сказать Париж означает не что иное, как Париж, Иллинойс, США или Париж, Иль-де-Франс, Франция, дает вам полную картину.Таким образом, это также уменьшит количество параметров счетчика для объекта Address.

Если вы пойдете по пути DDD, найдите эксперта домена для домена, для которого вы кодируете, вы не должны быть экспертом.Иногда проблемы не должны быть исправлены с помощью кода или изящного шаблона проектирования.

0 голосов
/ 18 октября 2011

immutable подходит для параллельных вычислений, без блокировок и без блокировок, immutable предназначен для высокой производительности и хорошей масштабируемости.

, поэтому Value Object может работать лучше в параллельной системе, включить в систему распространения, заменить старуюVO с новым VO, нет необходимости обновлять, поэтому нет блокировки.

...