Я знаю, что здесь есть много вариантов и связанных тем, связанных с переполнением стека, но я не нашел убедительных ответов, поэтому я сам попробую.
Я пытаюсь создать фабрику построителей, которая возвращает разные подклассы общего интерфейса построителя. Я хочу разрешить всем реализациям использовать общий абстрактный класс для повторного использования кода.
Обратите внимание, что меня не интересует тип возвращаемого значения метода build()
, а только то, какие типы построители.
Вот что у меня есть на данный момент:
Интерфейс Builder с общими для подчиненных интерфейсов:
interface FruitBuilder<T extends FruitBuilder<T>> {
T taste(String taste);
T shape(String shape);
T weight(String weight);
Fruit build();
}
У некоторых строителей есть дополнительные методы:
interface GrapesBuilder extends FruitBuilder<GrapeBuilder> {
GrapesBuilder clusterSize(int clusterSize);
}
Затем нужно указать фабрику, которая возвращает конкретных построителей:
interface FruitBuilderFactory {
GrapesBuilder grapes();
AppleBuilder apple();
LemonBuilder lemon();
}
Пользователь этих интерфейсов должен иметь возможность использовать его как:
Fruit grapes = fruitBuilderFactory
.grapes()
.weight(4)
.color("Purple")
.clusterSize(4) // Note that the GrapesBuilder type must be accessible here!
.build();
Большая часть логики перейдет в абстрактный класс, включая расширенную логику сборки:
abstract class BaseFruitBuilder<T extends FruitBuilder<T>> implements FruitBuilder<T> {
String taste;
T taste(String taste) {
this.taste = taste;
return (T)this; // Ugly cast!!!!!
}
...
Fruit build() {
Fruit fruit = createSpecificInstance();
// Do a lot of stuff on the fruit instance.
return fruit;
}
protected abstract Fruit createSpecificInstance();
}
Учитывая базовый класс, реализовать новые конструкторы действительно просто:
class GrapseBuilderImpl extends BaseFruitBuilder<GrapesBuilder> {
int clusterSize;
GrapesBuilder clusterSize(int clusterSize) {
this.clusterSize = clusterSize;
}
protected Fruit createSpecificInstance() {
return new Grape(clusterSize);
}
}
Это все компилируется и отлично (по крайней мере, мой настоящий код). Вопрос в том, могу ли я удалить уродливое приведение к T в абстрактном классе.
T
намеревается быть типомthis
, так что(T)this
прекрасно. Я бы предпочел использоватьThis
в качестве имени переменной типа, поэтому(This)this
выглядит еще разумнее. Однако есть выход, если вы действительно ненавидите актерский состав; но я не думаю, что это стоит усилий. - person ZhongYu   schedule 29.09.2015BuilderParameters
(конкретный неуниверсальный неокончательный класс), который вы затем вводите в свой конструктор? Код клиента будет примерно таким:Fruit grapes = fruitBuilderFactory.grapes().build(GrapesBuilderParams.newInstance().weight(...).color(...).clusterSize(...))
. Сделав это, вы устранили бы всю универсальность из параметров (чтобы больше не было приведений) и очень мало потеряли бы со стороны настраиваемости (т.е. построители не могут зависеть от порядка инициализации параметра, потому что они этого не знают) - person Giulio Franco   schedule 29.09.2015