Построение (несколько) сложного объекта - PullRequest
7 голосов
/ 24 августа 2010

Когда я создаю классы, простые конструкторы имеют тенденцию быть нормой.В одном из моих текущих проектов, библиотеке фильмов, у меня есть объект домена Movie.Он имеет ряд свойств, в результате чего конструктор выглядит следующим образом:

public Movie(string title, int year, Genre genre, int length, IEnumerable<string> actors)
{
    _title = title;
    _year = year;
    _genre = genre;
    _length = length;
    _actors = new List<string>(actors);
}

Это не страшно, но и не просто.Стоит ли использовать фабричный метод (static Movie CreateMovie(...)) или, возможно, конструктор объектов?Есть ли типичный шаблон для создания экземпляров классов доменов?

ОБНОВЛЕНИЕ: спасибо за ответы.Я, вероятно, сначала задумался над этим вопросом, хотя я узнал несколько вещей, которые будут полезны в более сложных ситуациях.Мое решение теперь состоит в том, чтобы заголовок был единственным обязательным параметром, а остальные - именованными / необязательными параметрами.Кажется, это идеальный способ построения этого предметного объекта.

Ответы [ 9 ]

4 голосов
/ 24 августа 2010

Если вы используете .NET 4.0, , вы можете использовать необязательные / именованные параметры , чтобы упростить создание объекта, который принимает несколько аргументов, некоторые из которых являются необязательными. Это полезно, когда вы хотите избежать множества различных перегрузок для предоставления необходимой информации об объекте.

Если вы не используете .NET 4, , вы можете использовать шаблон Object Builder для сборки вашего типа. Построитель объектов требует немного усилий для реализации и поддерживает синхронизацию с вашим типом - поэтому, будет ли в этом достаточно ценности, зависит от вашей ситуации.

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

4 голосов
/ 24 августа 2010

Да, использование фабричного метода - типичная модель, но вопрос в том, зачем вам это нужно?Вот что Википедия говорит о фабричных методах:

Как и другие шаблоны создания, она имеет дело с проблемой создания объектов (продуктов) без указания точного класса объекта, который будетсоздано.Шаблон проектирования фабричного метода решает эту проблему путем определения отдельного метода для создания объектов, который затем можно переопределить подклассами, чтобы указать производный тип создаваемого продукта.

Итак, шаблон фабричного методабудет иметь смысл, если вы хотите вернуть подклассы из Movie.Если это не является (и не будет) требованием, замена общедоступного конструктора фабричным методом на самом деле не имеет смысла.

Для требований, указанных в вашем вопросе, ваше решение выглядит действительно хорошоМне: Все обязательные поля передаются в качестве параметров конструктору.Если ни одно из ваших полей не является обязательным, вы можете добавить инициализатор по умолчанию и использовать синтаксис инициализатора объекта C # .

3 голосов
/ 24 августа 2010

Это зависит.

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

Упомянутый вами метод CreateMovie () является еще одним вариантом, если вам необходимо отделить внутренний конструктор от процесса создания экземпляра Movie.

У вас есть много вариантов для организации конструкторов. Используйте те, которые позволяют вам проектировать вашу систему без запахов и множества принципов (DRY, YAGNI, SRP.)

1 голос
/ 16 января 2013

Если вы можете отличить основные элементы данных от параметров конфигурации, создайте конструктор, который принимает все основные элементы данных и ничего больше (даже параметры конфигурации со значениями по умолчанию & mdash; снимать для удобства чтения). Инициализируйте параметры конфигурации для нормальных значений по умолчанию (в теле метода) и предоставьте установщики. В этот момент фабричный метод может вам что-то купить, если вам нужны общие конфигурации вашего объекта.

Еще лучше, если вы обнаружите, что у вас есть объект, который принимает огромный список параметров, объект может быть слишком толстым. Вы почувствовали тот факт, что ваш код может нуждаться в рефакторинге. Рассмотрите возможность разложения вашего объекта. Хорошая литература по ОО настоятельно аргументируется для небольших объектов (например, Мартин Фаулер, Рефакторинг ; Боб Мартин, Чистый код ). Фаулер объясняет, как разложить большие объекты. Например, параметры конфигурации (если таковые имеются) могут указывать на необходимость большего полиморфизма, особенно если они являются логическими значениями или перечислениями (рефакторинг «Преобразовать условно в полиморфизм»).

Мне нужно посмотреть, как используется ваш объект, прежде чем давать более конкретные советы. Фаулер говорит, что переменные, которые используются вместе, должны быть превращены в их собственный объект. Итак, в качестве иллюстрации, если вы рассчитываете определенные вещи на основе жанра, года и продолжительности, но не других атрибутов, то, возможно, потребуется объединить их вместе в свой собственный объект - уменьшив количество параметров, которые должны быть переданным вашему конструктору.

1 голос
/ 24 августа 2010

Я не вижу ничего плохого в том, чтобы оставить публичный конструктор таким, какой он есть. Вот некоторые правила, которым я следую, когда решаю, использовать ли фабричный метод.

  • Используйте фабричный метод, когда для инициализации требуется сложный алгоритм.
  • Используйте фабричный метод, когда для инициализации требуется операция, связанная с вводом-выводом.
  • Используйте фабричный метод, когда инициализация может вызвать исключение, которое невозможно защитить во время разработки.
  • Используйте фабричный метод, когда для повышения читабельности может потребоваться дополнительная информация.

Поэтому, основываясь на моих личных правилах, я бы оставил конструктор таким, какой он есть.

1 голос
/ 24 августа 2010

Я не вижу ничего плохого в интерфейсе вашего конструктора и не вижу, что даст вам статический метод.У меня будут точно такие же параметры, верно?

Параметры не кажутся необязательными, поэтому нет способа обеспечить перегрузку с меньшим количеством или использовать дополнительные параметры.

ИзС точки зрения вызывающего, это выглядит примерно так:

 Movie m = new Movie("Inception", 2010, Genre.Drama, 150, actors);

Цель фабрики - предоставить вам настраиваемый конкретный экземпляр интерфейса, а не просто вызвать конструктор для вас.Идея состоит в том, что точный класс не является жестко закодированным в точке построения.Это действительно лучше?

 Movie m = Movie.Create("Inception", 2010, Genre.Drama, 150, actors);

Мне кажется, почти то же самое.Единственное, что будет лучше, это если Create() вернет другие конкретные классы, чем Movie.

Одна вещь, о которой стоит подумать, - это как улучшить это, чтобы вызывать код, который легко понять.Самая очевидная проблема для меня заключается в том, что не очевидно, что означает 150, не глядя на код для Movie.Есть несколько способов улучшить это, если вы хотите:

  1. Использовать тип для длины фильма и создать этот тип inline new MovieLength (150)
  2. Использовать именованные параметрыесли вы используете .NET 4.0
  3. (см. ответ @ Heinzi), используйте инициализаторы объектов
  4. Используйте свободный интерфейс

При свободном интерфейсе ваш вызов будет выглядеть так:

 Movie m = new Movie("Inception").
   MadeIn(2010).
   InGenre(Genre.Drama).
   WithRuntimeLength(150).
   WithActors(actors);

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

1 голос
/ 24 августа 2010

Это вполне приемлемо, ИМХО.Я знаю, что статические методы иногда осуждаются, но я обычно помещаю этот код в статический метод, который возвращает экземпляр класса.Обычно я делаю это только для объектов, которым разрешено иметь нулевые значения.

Если значения объекта не могут быть нулевыми, добавьте их в качестве параметров в конструктор, чтобы избежать появления недопустимых объектов.

1 голос
/ 24 августа 2010

Вы дали хороший ответ на свой вопрос, это заводская схема.С шаблоном фабрики вам не нужны огромные конструкторы для инкапсуляции, вы можете установить члены объекта в функции фабрики и вернуть этот объект.

0 голосов
/ 25 августа 2010

Как по мне - все зависит от модели вашего домена.Если ваша модель предметной области позволяет вам создавать простые объекты - вы должны это делать.

Но часто у нас много сложных объектов, и создание каждого из них по отдельности слишком сложно.Вот почему мы ищем лучший способ инкапсулировать логику создания составных объектов.На самом деле, у нас есть только две альтернативы, описанные выше - «Factory Method» и «Object Builder».Создание объекта с помощью статического метода выглядит немного странно, потому что мы помещаем логику создания объекта в объект.Object Builder, в свою очередь, выглядит сложным.

Я думаю, что ответ лежит в модульных тестах.Это как раз тот случай, когда TDD был бы весьма полезен - мы шаг за шагом делаем нашу модель предметной области и понимаем необходимость сложности модели предметной области.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...