Универсальный компоновщик Java
Предположим, мне нужно немного DerivedBuilder
продлить некоторые BaseBuilder
, Базовый строитель имеет такой метод, как foo
(который возвращает BaseBuilder
). Производный строитель имеет метод bar
, метод bar
должен вызываться после метода foo
, Для этого я могу переопределить foo
метод в DerivedBuilder
как это:
@Override
public DerivedBuilder foo() {
super.foo();
return this;
}
Проблема в том, что BaseBuilder
имеет много методов, таких как foo
и я должен переопределить каждый из них. Я не хочу этого делать, поэтому я попытался использовать дженерики:
public class BaseBuilder<T extends BaseBuilder> {
...
public T foo() {
...
return (T)this;
}
}
public class DerivedBuilder<T extends DerivedBuilder> extends BaseBuilder<T> {
public T bar() {
...
return (T)this;
}
}
Но проблема в том, что я до сих пор не могу написать
new DerivedBuilder<DerivedBuilder>()
.foo()
.bar()
Даже если T
здесь DerivedBuilder
, Что я могу сделать, чтобы не перекрывать множество функций?
4 ответа
Ваша проблема в определении DerivedBuilder
:
class DerivedBuilder<T extends DerivedBuilder>;
И затем создание его экземпляра с помощью аргумента стертого типа new DerivedBuilder<DerivedBuilder<...what?...>>()
,
Вам понадобится полностью определенный производный тип, например так:
public class BaseBuilder<T extends BaseBuilder<T>> {
@SuppressWarnings("unchecked")
public T foo() {
return (T)this;
}
}
public class DerivedBuilder extends BaseBuilder<DerivedBuilder> {
public DerivedBuilder bar() {
return this;
}
}
Проверьте http://ideone.com/o8W5gg.
В дополнение к ответу BeyelerStudios, если вы хотите вложить дальше, вы можете просто использовать это:
class Base<T extends Base<?>> {
public T alpha() { return (T) this; }
public T bravo() { return (T) this; }
public T foxtrot() { return (T) this; }
}
class Derived<T extends Derived<?>> extends Base<T> {
public T charlie() { return (T) this; }
public T golf() { return (T) this; }
}
class FurtherDerived<T extends FurtherDerived<?>> extends Derived<T> {
public T delta() { return (T) this; }
public T hotel() { return (T) this; }
}
class MuchFurtherDerived<T extends MuchFurtherDerived<?>> extends FurtherDerived<T> {
public T echo() { return (T) this; }
}
public static void main(String[] args) {
new MuchFurtherDerived<MuchFurtherDerived<?>>()
.alpha().bravo().charlie().delta().echo().foxtrot().golf().hotel()
.bravo().golf().delta().delta().delta().hotel().alpha().echo()
.echo().alpha().hotel().foxtrot();
}
Вместо кастинга return (T) this;
Я здесь сделал Class.cast(this)
,
Для реализации:
BaseBuilder.build(ExtendedBuilder.class).foo().bar().foo().bar();
Каждый класс в иерархии должен знать фактический конечный дочерний класс, поэтому я решил сделать фабричный метод build
в базовом классе.
Актерский состав this
фактический ребенок делается в окончательном методе базового класса, обеспечивая return me();
,
class BaseBuilder<B extends BaseBuilder<B>> {
protected Class<B> type;
public static <T extends BaseBuilder<T>> T build(Class<T> type) {
try {
T b = type.newInstance();
b.type = type;
return b;
} catch (InstantiationException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
protected final B me() {
return type.cast(this);
}
B foo() {
System.out.println("foo");
return me();
}
}
class ExtendedBuilder extends BaseBuilder<ExtendedBuilder> {
ExtendedBuilder bar() {
System.out.println("bar");
return me();
}
}
Из вашего вопроса я понимаю, что метод foo() должен выполняться перед методом bar().
Если это правильно, вы можете применить шаблон дизайна шаблона.
Создайте абстрактную панель методов в BaseBuilder.
И новый метод сказать "шаблон". Шаблон метода определит последовательность: сначала выполняется foo(), а затем bar().
DerivedBuilder предоставит реализацию для панели методов.
public abstract class BaseBuilder {
public void foo(){
System.out.println("foo");
}
public abstract void bar();
public void template(){
foo();
bar();
}
}
public class DerivedBuilder extends BaseBuilder{
@Override
public void bar() {
System.out.println("bar");
}
public static void main(String[] args) {
BaseBuilder builder = new DerivedBuilder();
builder.template();
}
}
Надеюсь это поможет.