Для возврата `this` из универсального родительского класса требуется приведение в дочерний класс
Я использую шаблон builder , извлекаю повторяющийся код в класс "helper", но есть один аспект повторяющегося кода, которым я все еще не доволен.
Шаблон builder позволяет построить цепочку реализации кода следующим образом:
Car car = new CarBuilder().Wheels(4).Convertible().Build();
Каждый из методов CarBuilder
, Wheels
и Convertible
возвращает один и тот же экземпляр класса builder (return this
), а метод Build
возвращает только что созданный экземпляр Car
.
Вот моя попытка создать универсальный конструктор класс:
public class Builder<T> where T : class
{
private Func<T, T> func;
protected void SetInstantiator(Func<T, T> f) => this.func = f;
protected void Chain(Action<T> action)
{
this.ChainFunc(action);
}
private ChainFunc(Action<T> action)
{
// SNIPPED
}
protected T Instantiate() => this.func(null);
}
И вот реализация моего универсального конструктора:
public class CarBuilder : Builder<Car>
{
public CarBuilder()
{
this.SetInstantiator(c => new Car());
return this;
}
public CarBuilder Wheels(int wheels)
{
this.Chain(c => c.SetWheelCount(wheels));
return this;
}
public CarBuilder Convertible()
{
this.Chain(c => c.RetractableRoof = true);
return this;
}
public Car Build() => this.Instantiate();
}
Что меня беспокоит, так это повторение return this
после каждого вызова метода Chain
и мысль, что я мог бы протолкнуть это в сам метод Chain
, т. е. я хочу написать код так:
public CarBuilder Wheels(int wheels) =>
this.Chain(c => c.SetWheelCount(wheels));
В классе builder я попытался изменить тип возвращаемого значения с void
на Builder
:
protected Builder Chain(Action<T> action)
{
this.ChainFunc(action);
return this;
}
... но компилятор говорит, что возвращаемый тип должен быть Builder<T>
, т. е.
protected Builder<T> Chain(Action<T> action)
{
this.ChainFunc(action);
return this;
}
Ладно, достаточно честно., но в моем классе реализации я теперь должен сделать приведение:
public CarBuilder Wheels(int wheels) =>
(CarBuilder)this.Chain(c => c.SetWheelCount(wheels));
Итак, я снова повторил код, в котором все методы теперь должны включать приведение. Передача типа класса из подтипа в супертип не кажется правильной.
Мне кажется, я упускаю здесь что-то фундаментальное. Могу ли я избежать как повторения приведения, так и необходимости "возвращать это" из каждого метода реализации builder?2 ответа:
Один из способов сохранить логику в защищенной области - это добавить статический метод, который вызывается вместо метода экземпляра. Статический метод может использовать неявное приведение для возврата типа вызывающего объекта
Внутри
Builder<T>
protected void Chain(Action<T> action) { //local chain logic } protected static BT Chain<BT>(BT builder, Action<T> action) where BT:Builder<T> { builder.Chain(action); return builder; }
Вызовы внутри
CarBuilder
:public CarBuilder Wheels(int wheels) => Chain(this , c => c.SetWheelCount(wheels)); public CarBuilder Convertible() => Chain(this, c => c.RetractableRoof = true);