Универсальный компоновщик 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();
    }
}


Надеюсь это поможет.

Другие вопросы по тегам