Генерированная реализация шаблона посетителя в Java

Я провел некоторое исследование, пытаясь разработать среду преобразования типов, которая предоставляет возможность преобразовывать экземпляры исходного класса (например, Foo) в экземпляры классов результатов (например, Bar или Baz). Платформа должна предоставлять возможность использовать разную логику преобразования (то есть разные преобразователи) для одной и той же пары источника и результата. Он также должен быть расширяемым, то есть позволять добавлять новые конвертеры для новых и существующих пар источника и результата. Еще одно требование - безопасность типов, то есть любая попытка преобразовать экземпляр некоторого исходного класса в экземпляр класса результата без преобразователя, реализующего соответствующую логику преобразования, должна привести к ошибке времени компиляции.

Я решил использоватьШаблон посетителя с конвертерами в качестве посетителей и конвертируемых классов в качестве элементов. Для обеспечения расширяемости и безопасности типов я решил использовать дженерики. Итак, первая реализация структуры конверсии, которую я сделал, под влиянием какой-то статьи в Интернете (к сожалению, я потерял ссылку), была ...

Фреймворк для конвертации с конвертерами Statefull

Вот основные интерфейсы платформы, Converter и Convertable:

    public interface Converter<V extends Converter<V,A>, A extends Convertable<V,A>> {

        void convert(A convertable);
    }


    public interface Convertable<V extends Converter<V,A>, A extends Convertable<V,A>> {

        void convertWith(V converter);
    }

Дженерики делают реализациюConvertable принимать только реализацииConverter которые могут преобразовать их и сделать реализациюConverter посещать только реализацииConvertable который он сделан, чтобы преобразовать. Вот пример таких преобразователей:

interface FooConverter extends Converter<FooConverter,Foo> {

    void convert(Foo convertable);

    void convert(FooChild1 convertable);

    void convert(FooChild2 convertable);
}


public class Foo2BarConverter implements FooConverter {

    private Bar result;

    public Bar getResult() {
        return result;
    }

    @Override
    public void convert(Foo convertable) {
        this.result = new Bar("This bar's converted from an instance of Foo");
    }

    @Override
    public void convert(FooChild1 convertable) {
        this.result = new Bar("This bar's converted from an instance of FooChild1");
    }

    @Override
    public void convert(FooChild2 convertable) {
        this.result = new Bar("This bar's converted from an instance of FooChild2");
    }
}


public class Foo2BazConverter implements FooConverter {

    private Baz result;

    public Baz getResult() {
        return result;
    }

    @Override
    public void convert(Foo convertable) {
        this.result = new Baz("This baz's converted from an instance of Foo");
    }

    @Override
    public void convert(FooChild1 convertable) {
        this.result = new Baz("This baz's converted from an instance of FooChild1");
    }

    @Override
    public void convert(FooChild2 convertable) {
        this.result = new Baz("This baz's converted from an instance of FooChild2");
    }
}

И вот некоторые классы, которые могут быть преобразованы с помощью этих преобразователей:

public class Foo implements Convertable<FooConverter, Foo> {

    @Override
    public void convertWith(FooConverter converter) {
        converter.convert(this);
    }
}


public class FooChild1 extends Foo {

    @Override
    public void convertWith(FooConverter converter) {
        converter.convert(this);
    }
}


public class FooChild2 extends Foo {

    @Override
    public void convertWith(FooConverter converter) {
        converter.convert(this);
    }
}

Вот классы результатов, то есть <, code> Bar иBaz:

public class Bar {

    private String message;

    public Bar(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}


public class Baz {

    private String message;

    public Baz(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}

А вот код, который тестирует конвертеры:

Foo fooObj = new Foo();
Foo fooChild1Obj = new FooChild1();
Foo fooChild2Obj = new FooChild2();

// converting to bar
Foo2BarConverter foo2BarConverter = new Foo2BarConverter();

fooObj.convertWith(foo2BarConverter);
System.out.println(foo2BarConverter.getResult().getMessage());

fooChild1Obj.convertWith(foo2BarConverter);
System.out.println(foo2BarConverter.getResult().getMessage());

fooChild2Obj.convertWith(foo2BarConverter);
System.out.println(foo2BarConverter.getResult().getMessage());

// converting to baz
System.out.println();
Foo2BazConverter foo2BazConverter = new Foo2BazConverter();

fooObj.convertWith(foo2BazConverter);
System.out.println(foo2BazConverter.getResult().getMessage());

fooChild1Obj.convertWith(foo2BazConverter);
System.out.println(foo2BazConverter.getResult().getMessage());

fooChild2Obj.convertWith(foo2BazConverter);
System.out.println(foo2BazConverter.getResult().getMessage());

и вывод построен по этому коду

This bar's converted from an instance of Foo
This bar's converted from an instance of FooChild1
This bar's converted from an instance of FooChild2

This baz's converted from an instance of Foo
This baz's converted from an instance of FooChild1
This baz's converted from an instance of FooChild2

Взгляни наresult поле вFoo2BarConverter а такжеFoo2BazConverter, Это главный недостаток реализации. Это делает конвертеры современными, что не всегда удобно. Пытаясь избежать этого недостатка, я разработал ...

Конверсионный каркас без двойной диспетчеризации

Основной целью этой реализации является параметризация преобразователей с помощью классов результатов и возвращение результатов изconvert методConverter а такжеconvertWith методConvertable, Вот как это выглядит в коде:

public interface Converter<A extends Convertable<A>,R> {

    R convert(A convertable);
}

public interface Convertable<A extends Convertable<A>> {

    <R> R convertWith(Converter<A,R> converter);
}

public interface FooConverter<R> extends Converter<Foo,R> {

    @Override
    R convert(Foo convertable);

    R convert(FooChild1 convertable);

    R convert(FooChild2 convertable);
}

public class Foo2BarConverter implements FooConverter<Bar> {

    @Override
    public Bar convert(Foo convertable) {
        return new Bar("This bar's converted from an instance of Foo");
    }

    @Override
    public Bar convert(FooChild1 convertable) {
        return new Bar("This bar's converted from an instance of FooChild1");
    }

    @Override
    public Bar convert(FooChild2 convertable) {
        return new Bar("This bar's converted from an instance of FooChild2");
    }
}

public class Foo2BazConverter implements FooConverter<Baz> {

    @Override
    public Baz convert(Foo convertable) {
        return new Baz("This baz's converted from an instance of Foo");
    }

    @Override
    public Baz convert(FooChild1 convertable) {
        return new Baz("This baz's converted from an instance of FooChild1");
    }

    @Override
    public Baz convert(FooChild2 convertable) {
        return new Baz("This baz's converted from an instance of FooChild2");
    }
}

public class Foo implements Convertable<Foo> {

    @Override
    public <R> R convertWith(Converter<Foo,R> converter) {
        return converter.convert(this);
    }
}

public class FooChild1 extends Foo {

    @Override
    public <R> R convertWith(Converter<Foo,R> converter) {
        return converter.convert(this);
    }
}

public class FooChild2 extends Foo {

    @Override
    public <R> R convertWith(Converter<Foo,R> converter) {
        return converter.convert(this);
    }
}

V удаляется изConvertable декларация, потому что иметь класс результата вConverter Декларация будет иметь нас на самом деле для параметризации реализацииConvertable с результатами классов. Каждая привязка конвертируемой привязывается к единственному классу результатов, в который она может быть преобразована. ТакconvertWith вConvertable относится полученные конвертеры сConverter<A,R> интерфейс. И это проблема. Теперь реализацииConvertable вызов полученного конвертера всегда вызоветconvert который определен вConverter интерфейс, а неconvert методы, которые переопределяют его вConverter Реализации. Другими словамиconvert(FooChild1 convertable) а такжеconvert(FooChild2 convertable) вFoo2BarConverter а такжеFoo2BazConverter никогда не будет называться. По сути, это убивает основное понятие шаблона посетителя - двойная диспетчеризация. Вот тестовый код ...

Foo fooObj = new Foo();
Foo fooChild1Obj = new FooChild1();
Foo fooChild2Obj = new FooChild2();

// converting to bar
Foo2BarConverter foo2BarConverter = new Foo2BarConverter();
System.out.println(fooObj.convertWith(foo2BarConverter).getMessage());
System.out.println(fooChild1Obj.convertWith(foo2BarConverter).getMessage());
System.out.println(fooChild2Obj.convertWith(foo2BarConverter).getMessage());

System.out.println();

// converting to baz
Foo2BazConverter foo2BazConverter = new Foo2BazConverter();
System.out.println(fooObj.convertWith(foo2BazConverter).getMessage());
System.out.println(fooChild1Obj.convertWith(foo2BazConverter).getMessage());
System.out.println(fooChild2Obj.convertWith(foo2BazConverter).getMessage());

и его вывод, который демонстрирует, что переопределяющие методы не вызываются в этой реализации.

This bar's converted from an instance of Foo
This bar's converted from an instance of Foo
This bar's converted from an instance of Foo

This baz's converted from an instance of Foo
This baz's converted from an instance of Foo
This baz's converted from an instance of Foo

Следующая реализация, с которой я пытался сделать преобразователи без сохранения состояния, была ...

Конвертеры с параметризованными методами

Основная идея здесь - параметризовать только те методы, которые я хочу вернуть в результате преобразования без параметризации объявлений интерфейсов.

public interface Converter<V extends Converter<V,A>, A extends Convertable<V,A>> {

    <R> R convert(A convertable);
}

public interface Convertable<V extends Converter<V,A>, A extends Convertable<V,A>> {

    <R> R convertWith(V converter);
}

interface FooConverter extends Converter<FooConverter,Foo> {

    <R> R convert(Foo convertable);

    <R> R convert(FooChild1 convertable);

    <R> R convert(FooChild2 convertable);
}

public class Foo2BarConverter implements FooConverter {

    @Override
    public Bar convert(Foo convertable) {
        return new Bar("This bar's converted from an instance of Foo");
    }

    @Override
    public Bar convert(FooChild1 convertable) {
        return new Bar("This bar's converted from an instance of FooChild1");
    }

    @Override
    public Bar convert(FooChild2 convertable) {
        return new Bar("This bar's converted from an instance of FooChild2");
    }
}

public class Foo2BazConverter implements FooConverter {

    @Override
    public Baz convert(Foo convertable) {
        return new Baz("This baz's converted from an instance of Foo");
    }

    @Override
    public Baz convert(FooChild1 convertable) {
        return new Baz("This baz's converted from an instance of FooChild1");
    }

    @Override
    public Baz convert(FooChild2 convertable) {
        return new Baz("This baz's converted from an instance of FooChild2");
    }
}

public class Foo implements Convertable<FooConverter, Foo> {

    @Override
    public <R> R convertWith(FooConverter converter) {
        return converter.convert(this);
    }
}

public class FooChild1 extends Foo {

    @Override
    public <R> R convertWith(FooConverter converter) {
        return converter.convert(this);
    }
}

public class FooChild2 extends Foo {

    @Override
    public <R> R convertWith(FooConverter converter) {
        return converter.convert(this);
    }
}

Тестовый код

Foo fooObj = new Foo();
Foo fooChild1Obj = new FooChild1();
Foo fooChild2Obj = new FooChild2();

// converting to bar
Foo2BarConverter foo2BarConverter = new Foo2BarConverter();
System.out.println(fooObj.<Bar>convertWith(foo2BarConverter).getMessage());
System.out.println(fooChild1Obj.<Bar>convertWith(foo2BarConverter).getMessage());
System.out.println(fooChild2Obj.<Bar>convertWith(foo2BarConverter).getMessage());

System.out.println();

// converting to baz
Foo2BazConverter foo2BazConverter = new Foo2BazConverter();
System.out.println(fooObj.<Baz>convertWith(foo2BazConverter).getMessage());
System.out.println(fooChild1Obj.<Baz>convertWith(foo2BazConverter).getMessage());
System.out.println(fooChild2Obj.<Baz>convertWith(foo2BazConverter).getMessage());

и это вывод

This bar's converted from an instance of Foo
This bar's converted from an instance of FooChild1
This bar's converted from an instance of FooChild2

This baz's converted from an instance of Foo
This baz's converted from an instance of FooChild1
This baz's converted from an instance of FooChild2

На первый взгляд выглядит великолепно. Но на самом деле это решение небезопасно. Например, следующий вызов

fooObj.<Baz>convertWith(foo2BarConverter).getMessage()

не вызовет ошибку времени компиляции. Но это приведет к ClassCastException во время выполнения.

Итак, общий вопрос заключается в следующем.

Есть ли способ сделать обобщенный типизированный безопасный посетитель с помощью Java?

UPD: Я добавил ссылки на источники всех трех реализаций:первый, второй а такжетретий

Ответы на вопрос(2)

Ваш ответ на вопрос