Абстрактный строитель строит неизменный объект - PullRequest
0 голосов
/ 17 декабря 2018

У меня есть следующий код, реализующий абстрактный конструктор (как в эффективной книге Java):

interface I {
    I ret();
}

abstract class A implements I {

    private String s = "";

    A(Builder b) {
        s = b.s;
    }

    @Override
    public I ret() {
        String s = "some new string from calc.";
        //HERE I NEED TO CONSTRUCT class B
        //THIS IS PROBLEMATIC LINE BELOW <<<<--------------------------------------
        return new Builder<>().withString(s).build();
    }

    static abstract class Builder<T extends Builder<T>> {

        String s = "";

        T withString(String s) {
            this.s = s;
            return self();
        }

        protected abstract A build();

        //simulated self-type idiom
        protected abstract T self();
    }
}

class B extends A {

    private B(A.Builder b) {
        super(b);
    }

    static class Builder extends A.Builder<Builder> {

        @Override
        protected B build() {
            return new B(this);
        }

        @Override
        protected Builder self() {
            return this;
        }
    }
}

, и это небольшой вариант использования

public static void main(String[] args) {
    I b = new B.Builder().withString("bclass").build();
    I b2 = b.ret(); //<---- this one should construct new immutable B
}

Что я хочу сделать, это -из ret() метода в классе A я хотел бы построить неизменный объект, который не знает родительский тип - поэтому в моем примере это будет новый класс B, который имеет новую внутреннюю строку.

Проблема в том, что класс A не знает о классе B, поскольку B является его родителем.

Возможно ли что-то подобное, не прибегая к дженерикам?

Если я использую изменяемыйкласс, это будет так просто, как this.s = newS;.

ОБНОВЛЕНИЕ

abstract class A implements I {

    private String s = "";

    A(Builder b) {
        s = b.s;
    }

    @Override
    public I ret() {
        String s = "SOME NEW STING FROM CALCULATION";
        return builder().withString(s).build();
    }

    abstract Builder<?> builder();

    static abstract class Builder<T extends Builder<T>> {

        String s = "";

        T withString(String s) {
            this.s = s;
            return self();
        }

        protected abstract A build();

        //simulated self-type idiom
        protected abstract T self();
    }
}

class B extends A {

    B(A.Builder b) {
        super(b);
    }

    @Override
    Builder builder() {
        return new Builder();
    }

    static class Builder extends A.Builder<Builder> {

        @Override
        protected B build() {
            return new B(this);
        }

        @Override
        protected Builder self() {
            return this;
        }
    }
}

1 Ответ

0 голосов
/ 17 декабря 2018

Вам необходимо предоставить абстрактный метод в A для создания подходящего экземпляра Builder:

abstract Builder<A> toBuilder();

Затем реализовать это в конкретных подклассах и вызвать:

return toBuilder().withString(s).build();
...