Избежание приведения в общей фабричной структуре MVP - PullRequest
0 голосов
/ 29 августа 2018

Я пытался реализовать способ разъединения View и Presenter в шаблоне MVP, чтобы обеспечить среду, которая делает именно это, но после некоторого момента я запутался.

Фон

У меня есть интерфейс View с универсальным типом для подключенного докладчика и наоборот. Эти интерфейсы должны быть расширены разработчиком. Конкретный интерфейс не представляет интереса для этого вопроса, но их определение класса выглядит следующим образом:

public interface Presenter<T extends View>

и

public interface View<T extends Presenter>

Идея состоит в том, что и View, и Presenter знают противоположный интерфейс.

Для использования этой структуры разработчик должен предоставить фабрику, которая создает экземпляр представления, которое он хочет показать, и Presenter, который обрабатывает это представление. Он отдает их обоих классу, называемому SuperController. Они связаны классом View.

Интерфейс PresenterFactory, который создает Presenter, не имеет параметров и возвращает реализацию Presenter и выглядит следующим образом:

public interface PresenterFactory<T extends View> {

    <S extends Presenter<T>> S create();

}

Интерфейс ViewFactory, который создает View, создает реализацию View на основе Presenter и выглядит следующим образом:

public interface ViewFactory<T extends View, S extends Presenter<T>> {

    T create(S presenter);

}

Проблема

Проблема, с которой я сталкиваюсь, заключается в следующем:

Я хотел предоставить пример с TestView и TestPresenter. Те выглядят так:

public interface TestPresenter extends Presenter<TestView> {...}

public interface TestView extends View<TestPresenter> {...}

Также предоставляется ViewFactory, который выглядит следующим образом:

class TestViewFactory implements ViewFactory<TestView, TestPresenter> {

    @Override
    public TestView create(TestPresenter presenter) {
        return new TestViewImpl(presenter);
    }
}

А это TestPresenterFactory:

private class TestPresenterFactory implements PresenterFactory<TestView> {

    @Override
    public <S extends Presenter<TestView>> S create() {
        return new TestPresenterImpl();
    }
}

Этот код не может быть скомпилирован. Проблема заключается в возвращаемом значении TestPresenterFactory. Java говорит, что ожидает S, а не TestPresenterImpl. Кроме того, приведение к TestPresenter не будет работать. Однако приведение к S будет работать . Это может быть скомпилировано и также успешно выполнено, но я не хотел, чтобы разработчик-исполнитель делал это.

Почему существует эта проблема? И почему не работает приведение к конкретному интерфейсу? По моему мнению, это должно работать, потому что TestPresenterImpl реализует TestPresenter, который расширяет Presenter универсального типа TestView, но не компилируется.


Следующая работает работает. Если вы измените определение PresenterFactory следующим образом:

public interface PresenterFactory<T extends View, S extends Presenter<T>> {

    S create();

}

с примером реализации теперь выглядит так:

private class TestPresenterFactory implements PresenterFactory<TestView, TestPresenter> {

    @Override
    public TestPresenter create() {
        return new TestPresenterImpl();
    }
}

Его можно скомпилировать и запустить, как если бы я приводил к S в приведенном выше примере.

Однако, это не , что я хочу. Объявление универсального типа является избыточным. Разрешение разработчику-исполнителю объявлять View и Presenter, чтобы только создать Presenter, выглядит неловко. Было бы замечательно, если бы тип мог выводиться по методу.

Кроме того, я не хочу заставлять разработчика-разработчика приводить к S.

Есть ли более элегантный способ для этого?


редактировать

Вопрос был поднят как дубликат, и я хочу дистанцироваться от этого вопроса .

Проблема, которая возникает из-за этого, не имеет никакого отношения к Producer Extends Consumer Super. У меня есть два производителя (фабрики) в использовании. Оба из которых производят класс. Один зависит от другого класса (представление зависит от Presenter), а другой - без зависимости (Presenter может быть создан с помощью конструктора по умолчанию). Все уважаемые интерфейсы имеют расширение XYZ для расширения T, что позволяет создавать интерфейсы, которые наследуют этот интерфейс (либо Presenter, либо View).

Настоящая проблема здесь заключается в том, что общий вид в ViewFactory может быть выведен из Java. Оба общих типа объявлены в определении класса. В PresenterFactory универсальный уровень метода не может быть выведен с помощью Java. Несмотря на то, что универсальный тип совпадает с типом в фабрике представлений <S extends Presenter<T>>; в методе этот тип не может быть выведен.

Решение состоит в том, чтобы либо привести (что я не хочу делать с помощью разработчика), либо объявить PresenterType в определении класса (что представляется избыточным. Единственное, что интересует, - это то, что представление определено и любой докладчик для этого представления возвращается).

Мой вопрос: что я могу сделать, чтобы обойти указанные проблемы, которые являются результатом предложения extends?

1 Ответ

0 голосов
/ 05 сентября 2018

Для начала, это решит проблему:

interface PresenterFactory<T extends View, S extends Presenter<T>> {
    S create();
}

class TestPresenterFactory implements PresenterFactory<TestView, TestPresenterImpl> {

    @Override
    public TestPresenterImpl create() {
        return new TestPresenterImpl();
    }
}

Но мне все еще не нравится:

  • круговая зависимость между View и Presenter;
  • использование необработанных типов: на самом деле, если вы слепо используете View<?> и Presenter<?> в их соответствующих объявлениях интерфейса, то опять не скомпилируется (TestPresenterFactory не удастся).

--- edit ---

Так как насчет этого, чтобы исправить последние 2 пункта вместе с вашей первоначальной проблемой:

interface Presenter<P extends Presenter<P, V>, V extends View<P, V>> {

}

interface View<P extends Presenter<P, V>, V extends View<P, V>> {

}

interface PresenterFactory<P extends Presenter<P, V>, V extends View<P, V>> {

    P create();

}

interface ViewFactory<P extends Presenter<P, V>, V extends View<P, V>> {

    V create(P presenter);

}

interface TestPresenter extends Presenter<TestPresenter, TestView> {}

class TestPresenterImpl implements TestPresenter {

}

interface TestView extends View<TestPresenter, TestView> {}

class TestViewImpl implements TestView {

    public TestViewImpl(Presenter<?, ?> presenter) {
    }
}

class TestViewFactory implements ViewFactory<TestPresenter, TestView> {

    @Override
    public TestView create(TestPresenter presenter) {
        return new TestViewImpl(presenter);
    }
}

class TestPresenterFactory implements PresenterFactory<TestPresenter, TestView> {

    @Override
    public TestPresenter create() {
        return new TestPresenterImpl();
    }
}
...