Я создаю серию сборщиков, чтобы очистить синтаксис, который создает классы домена для моих макетов, как часть улучшения наших общих модульных тестов. Мои компоновщики по существу заполняют класс домена (такой как Schedule
) некоторыми значениями, определенными путем вызова соответствующего WithXXX
и объединения их в цепочку.
Я столкнулся с некоторой общностью среди моих сборщиков, и я хочу абстрагировать это в базовый класс для увеличения повторного использования кода. К сожалению, то, что я получаю в итоге, выглядит так:
public abstract class BaseBuilder<T,BLDR> where BLDR : BaseBuilder<T,BLDR>
where T : new()
{
public abstract T Build();
protected int Id { get; private set; }
protected abstract BLDR This { get; }
public BLDR WithId(int id)
{
Id = id;
return This;
}
}
Обратите особое внимание на protected abstract BLDR This { get; }
.
Пример реализации компоновщика классов домена:
public class ScheduleIntervalBuilder :
BaseBuilder<ScheduleInterval,ScheduleIntervalBuilder>
{
private int _scheduleId;
// ...
// UG! here's the problem:
protected override ScheduleIntervalBuilder This
{
get { return this; }
}
public override ScheduleInterval Build()
{
return new ScheduleInterval
{
Id = base.Id,
ScheduleId = _scheduleId
// ...
};
}
public ScheduleIntervalBuilder WithScheduleId(int scheduleId)
{
_scheduleId = scheduleId;
return this;
}
// ...
}
Поскольку BLDR не относится к типу BaseBuilder, я не могу использовать return this
в методе WithId(int)
для BaseBuilder
.
Предоставляет ли дочерний тип с помощью свойства abstract BLDR This { get; }
мой единственный вариант здесь, или я пропускаю какой-то синтаксический трюк?
Обновление (поскольку я могу показать, почему я делаю это более четко):
Конечным результатом является создание сборщиков, которые создают профилированные доменные классы, которые можно было бы ожидать получить из базы данных в читаемом формате [программист]. В этом нет ничего плохого ...
mock.Expect(m => m.Select(It.IsAny<int>())).Returns(
new Schedule
{
ScheduleId = 1
// ...
}
);
так как это уже довольно читабельно. Альтернативный синтаксис компоновщика:
mock.Expect(m => m.Select(It.IsAny<int>())).Returns(
new ScheduleBuilder()
.WithId(1)
// ...
.Build()
);
Преимущество, которое я ищу от использования компоновщиков (и реализации всех этих WithXXX
методов), состоит в том, чтобы абстрагироваться от создания сложных свойств (автоматически расширяйте наши значения поиска в базе данных с правильными Lookup.KnownValues
, не обращаясь к базе данных, очевидно) и когда конструктор предоставляет часто используемые тестовые профили для классов домена ...
mock.Expect(m => m.Select(It.IsAny<int>())).Returns(
new ScheduleBuilder()
.AsOneDay()
.Build()
);