jjzjj

java - 避免在基于通用工厂的 MVP 框架中进行强制转换

coder 2024-03-21 原文

我试图在 MVP 模式中实现一种将 View 和 Presenter 解耦的方法,以提供一个框架,它正是这样做的,但后来我感到困惑。

背景

我有一个连接演示者的通用类型的 View 界面,​​反之亦然。这些接口(interface)将由实现开发人员扩展。这个问题对具体接口(interface)不感兴趣,但它们的类定义如下所示:

public interface Presenter<T extends View>


public interface View<T extends Presenter>

这个想法是 View 和 Presenter 都知道相反的接口(interface)。

为了使用这种结构,开发人员应该提供一个工厂,实例化他想要显示的 View 和处理这个 View 的演示者。他将它们都交给了一个名为 SuperController 的类。它们由 View 的类关联。

创建 Presenter 的 PresenterFactory 接口(interface)没有参数并返回一个 Presenter 实现,如下所示:
public interface PresenterFactory<T extends View> {

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

}

创建 View 的 ViewFactory 接口(interface)基于 Presenter 创建了一个 View 实现,如下所示:
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 工作。它可以编译并成功运行,但这不是我希望实现开发人员做的事情。

为什么会存在这个问题?为什么转换到具体接口(interface)不起作用?在我看来,这应该可行,因为 TestPresenterImpl 实现了 TestPresenter,它扩展了泛型类型 TestView 的 Presenter 但它是不可编译的。

以下 工作。如果您像这样更改 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 演示者,只创建演示者看起来很尴尬。如果可以在方法中推断出类型,那就太好了。

另外,我不想强​​迫实现开发人员强制转换为 S。

有没有更优雅的方法呢?

编辑

一个问题被提出作为重复,我想远离 this question .

由此产生的问题与Producer Extends Consumer Super无关。我有两个生产者(工厂)在使用。两者都在产生一个类。一个依赖于另一个类(View 依赖于Presenter),而另一个没有依赖(Presenter 可以用默认构造函数实例化)。所有受尊重的接口(interface)都具有 T 扩展 XYZ 定义,以允许产生继承该接口(interface)(Presenter 或 View)的接口(interface)。

这里真正的问题是 ViewFactory 中的泛型可以被 Java 推断出来。这两种泛型类型都在类定义中声明。在 PresenterFactory 中,Java 无法推断方法级泛型。尽管如此,泛型类型与 View 工厂中的类型相同 <S extends Presenter<T>> ;在方法中,无法推断出这种类型。

解决方案是强制转换(我不希望使用开发人员这样做)或在类定义中声明 PresenterType(这似乎是多余的。唯一感兴趣的是, View 已定义并且任何 Presenter返回该 View )。

我的问题是我可以做些什么来解决由 extends 子句导致的上述问题?

最佳答案

对于初学者来说,这将解决问题:

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<?>在它们各自的接口(interface)声明中,然后它也不会编译(TestPresenterFactory 会失败)。

  • --- 编辑 ---

    那么如何解决最后两点以及您的原始问题:
    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();
        }
    }
    

    关于java - 避免在基于通用工厂的 MVP 框架中进行强制转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52082791/

    有关java - 避免在基于通用工厂的 MVP 框架中进行强制转换的更多相关文章

    1. ruby-on-rails - Railstutorial : db:populate vs. 工厂女孩 - 2

      在railstutorial中,作者为什么选择使用这个(代码list10.25):http://ruby.railstutorial.org/chapters/updating-showing-and-deleting-usersnamespace:dbdodesc"Filldatabasewithsampledata"task:populate=>:environmentdoRake::Task['db:reset'].invokeUser.create!(:name=>"ExampleUser",:email=>"example@railstutorial.org",:passwo

    2. java - 等价于 Java 中的 Ruby Hash - 2

      我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

    3. ruby-on-rails - RSpec:避免使用允许接收的任何实例 - 2

      我正在处理旧代码的一部分。beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)endRubocop错误如下:Avoidstubbingusing'allow_any_instance_of'我读到了RuboCop::RSpec:AnyInstance我试着像下面那样改变它。由此beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)end对此:let(:sport_

    4. java - 从 JRuby 调用 Java 类的问题 - 2

      我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

    5. java - 我的模型类或其他类中应该有逻辑吗 - 2

      我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

    6. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

      什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

    7. 叮咚买菜基于 Apache Doris 统一 OLAP 引擎的应用实践 - 2

      导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵

    8. Observability:从零开始创建 Java 微服务并监控它 (二) - 2

      这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/

    9. 【Java 面试合集】HashMap中为什么引入红黑树,而不是AVL树呢 - 2

      HashMap中为什么引入红黑树,而不是AVL树呢1.概述开始学习这个知识点之前我们需要知道,在JDK1.8以及之前,针对HashMap有什么不同。JDK1.7的时候,HashMap的底层实现是数组+链表JDK1.8的时候,HashMap的底层实现是数组+链表+红黑树我们要思考一个问题,为什么要从链表转为红黑树呢。首先先让我们了解下链表有什么不好???2.链表上述的截图其实就是链表的结构,我们来看下链表的增删改查的时间复杂度增:因为链表不是线性结构,所以每次添加的时候,只需要移动一个节点,所以可以理解为复杂度是N(1)删:算法时间复杂度跟增保持一致查:既然是非线性结构,所以查询某一个节点的时候

    10. 基于C#实现简易绘图工具【100010177】 - 2

      C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

    随机推荐