Как создать построитель в C# для объекта, свойства которого являются ссылочными типами?
Я начал создавать конструктор, чтобы легко создавать тестовые данные для написанных модульных тестов.
Основная структура застройщика:
public class MyClassBuilder
{
public int id = 0; //setting the default value here
//allows MyClass to be built with a specific id
public MyClassBuilder WithId(int id)
{
this.id = id;
return this;
}
public MyClass Build()
{
return new MyClass(id);
}
}
Использование этого шаблона затем становится:
MyClass mc = new MyClassBuilder().WithId(5).Build();
Мне это удобно... но у меня есть вопросы, когда MyClass
имеет свойство, которое не является тривиальным.... Я немного не уверен в том, как создать его со значением по умолчанию.
public class MyClassBuilder
{
public int id = 0; //setting the default value here
//do I construct the default value using a MySecondClassBuilder?
public MySecondClass mySecondClass;
//allows MyClass to be built with a specific id
public MyClassBuilder WithId(int id)
{
this.id = id;
return this;
}
public MyClassBuilder WithMySecondClass(MySecondClass mySecondClass)
{
this.mySecondClass = mySecondClass;
}
public MyClass Build()
{
return new MyClass(id);
}
}
Я предполагаю, что я бы создал строителя для MySecondClass
и использовать это для создания реализации по умолчанию.
Кто-нибудь может подтвердить, что мое предположение правильно и является лучшей практикой?
В настоящее время я проверяю свое предположение, но я решил документировать эту идею в Stackru, поскольку единственные примеры паттерна для разработчиков, которые я смог найти, - это когда-либо создаваемые свойства Google, которые являются типами значений, а не ссылочными типами.
2 ответа
Как с большинством вещей - это зависит.
Если вы хотите ссылочную семантику в поле mySecondClass (то есть: вы хотите сохранить ссылку на другой класс), то это точно как ваше целое число. Значение по умолчанию, равное нулю, может быть вполне приемлемым.
Однако, если вы хотите всегда гарантировать значение, вам нужно либо создать, либо просто создать новый объект для вашего второго класса.
Кроме того, если вы хотите клонировать вместо копий по ссылкам, вам нужно создать новый объект и назначить его для ссылки.
Это, как говорится, во многих случаях традиционные конструкторы более уместны, чем пытаться сделать свободный интерфейс стиля сборки. Действительно легко "испортить" свободный интерфейс для создания подобного построения, поскольку вы полагаетесь на пользователя больше, чем на традиционный конструктор.
Например, что происходит в вашем коде, если пользователь никогда не вызывает Build? Он по-прежнему что-то возвращает (так как каждый из методов возвращает объект), но действительно ли это? Я не был бы уверен как потребитель вашего класса. Традиционные конструкторы с перегрузками, с другой стороны, показывают мне, что является допустимым, а что нет, и применяются во время компиляции.
Как только у меня появится сложный объект, в котором я хочу использовать пустышку в модульных тестах, я обычно обращаюсь к фальсификации / подделке / изоляции. В C# я предпочитаю Moq, но есть несколько таких, как Rhino Mocks, Typemock и так далее.
Преимущество заключается в том, что они, как правило, имеют возможность заглушить все свойства, существующие в исходном типе, со значениями по умолчанию и пустыми объектами, без необходимости делать что-либо, кроме оператора одной строки, для создания поддельного объекта. В то же время вы можете легко настроить объект так, чтобы он возвращал значения, которые вы хотите для тех свойств, которые важны для вашего теста. Кроме того, большинство таких фреймворков поддерживают рекурсивное подделывание, так что сложные объекты внутри вашего типа также будут поддельными, и так далее.
Есть причины, по которым кто-то может не захотеть использовать отдельную структуру, и в этом случае я думаю, что предложение Рида Копси было бы ручным способом сделать это.
Если нет никаких причин избегать добавления фреймворка (который будет зависеть только от ваших тестовых проектов, если вы разделите тестирование), я бы искренне рекомендовал его для сложных типов.