草庐IT

android - 如果我们可以返回一个值并在 View 中设置(MVP 结构),为什么要在 Presenter 中创建一个界面

coder 2023-12-13 原文

已经两个月了,我一直在使用 MVP Structure 来创建 Android 应用。

正如我发现的关于 MVP 的每个链接中所解释的那样,Presenter 类负责处理所有业务登录和数据(来自 Model 类),我理解并开始工作。

教程中显示的优点之一,我想在这里强调,MVP 使单元测试更容易,因为没有 View 的依赖性(好吧,我也明白这一点)。

我不明白的是为什么要创建接口(interface)来更新演示者的 View ,而我只能调用一个将返回值的演示者方法,然后我可以在那里设置它?

让我们来看看我上面谈到的优势(单元测试)。使用这些接口(interface)单元测试会出现更多问题,因为方法需要接口(interface)实现来完成我们在单元测试中没有的操作(我知道仪器测试也出现在单元测试中,但我只谈论非 ui 测试)。

我只是更喜欢调用 presenter 方法并获取值并在 fragment 或 Activity 本身的 View 中设置,因为接口(interface)的创建会产生另一个级别的复杂性和不必要的接口(interface)声明,所有 View 操作都将被实现。这有点令人沮丧。

与我一起工作的一位 friend 在我的代码中指出了这个问题,并告诉我查看所有在线引用资料以澄清我的错误。但我想知道这些接口(interface)在编程实践中是如何提供帮助的。因为我不能只是消化它。它成为我的屁股的痛苦。看了网上所有的资料都没有解决办法。

例子

带界面的Presenter

class Presenter
{

    private ViewInterface viewInterface;

    public void setViewInterface(ViewInterface viewInterface)
    {
        this.viewInterface = viewInterface;
    }

    // Here value is being passed to interface method that is implemented in fragment.
    // No problem with this implementation but why to do it. 
    // It will make unit test problematic as this method needs ViewInterface.
    public void calculate()
    {
        // Some calculation
        viewInterface.updateView(/*pass some paramerer*/);
    }
}

没有界面的Presenter

class Presenter
{


    // Here just take the value and set the view in fragment
    // Unit test easier just check the returned value.
    public int calculate()
    {
        int result = -1;
        // Some calculation
        return result;
    }
}

正在使用演示者的 fragment

class MyFragment extends Fragment{

    ....

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.layout1, container, false);
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState)
    {
        super.onViewCreated(view, savedInstanceState);
        // just call method and get value to set in a view.
        int result = new Presenter().calculate();
        // set result in a view
    }

}

请检查上面代码中的注释。

任何帮助将不胜感激。 提前致谢。

最佳答案

自从我从自己的新手代码过渡到 MVP 才过去几个月,我不得不说,学习这种架构使我成为了更好的程序员,并让我对接口(interface)有了新的认识。

本人没有学历或经验,不谈SOLID ,或计算机科学中的任何其他原则,但我可以根据我所学和经历的内容向您提供我的意见。

每个组件都有接口(interface),可以很容易地查看您的应用程序的功能。

查看以下合约,发现here :

我确切地知道这个应用程序的整个模块中发生了什么。 如果我关心事情是如何完成的,我可以看看实现。 在编写单元测试时,我不关心事情是如何工作的,只关心它们确实能工作。 我可以更改每个组件,M、V 或 P,而不会破坏我的应用程序中的任何内容。

当我们对同一个模型有多个 View 时,可能会有一个用例。 我们可以有一个 ProductListCustomView、一个 ProductListFragment 和一个 ProductListActivity。我们可以根据运行应用程序的设备选择其中之一。它们都实现相同的接口(interface),因此即使在运行时也很容易进行更改。

public class ProductListContract {

public interface View {
    void showProducts(List<Product> products);

    void showAddProductForm();

    void showEditProductForm(Product product);

    void showDeleteProductPrompt(Product product);

    void showGoogleSearch(Product product);

    void showEmptyText();

    void hideEmptyText();

    void showMessage(String message);

}

public interface Actions {
    void loadProducts();

    void onAddProductButtonClicked();

    void onAddToCartButtonClicked(Product product);

    Product getProduct(long id);

    void addProduct(Product product);

    void onDeleteProductButtonClicked(Product product);

    void deleteProduct(Product product);

    void onEditProductButtonClicked(Product product);

    void updateProduct(Product product);

}

public interface Repository {
    List<Product> getAllProducts();

    Product getProductById(long id);

    void deleteProduct(Product product);

    void addProduct(Product product);

    void updateProduct(Product product);

}

看上面的例子,我不认为 Presenter 的接口(interface)是必要的,除非你有不止一个 Presenter 的实现。 我想不出一个 View 可以与多个 Presenter 类型相关联的场景(例如 LongProductListPresenter、ShortProductListPresenter 等)

在使用 MVP 完成应用程序几个月后,您可能会决定更改您的某些 View ,将它们转换为自定义 View ,或将您的布局转换为 Android 发布的最新大事。

您还可以决定使用不同的库来存储数据或联网。

您可能想要更改 Presenter 中的逻辑,但您不太可能有一天早上醒来并决定您的应用程序需要一个全新的 Presenter。

google 的 MVP todo 示例使用演示者界面。因此,绝大多数开发者,都会这样做。如果有一天他们更改了这个示例应用程序,那么其他所有人都会迅速效仿。

归根结底,您是架构师,如何构建应用程序是您(或您的老板)的决定。

我希望您在做出自己的决定时更有信心,无论您做出哪种选择。

这是关于该主题的另一篇文章。

http://blog.karumi.com/interfaces-for-presenters-in-mvp-are-a-waste-of-time/

关于android - 如果我们可以返回一个值并在 View 中设置(MVP 结构),为什么要在 Presenter 中创建一个界面,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38651767/

有关android - 如果我们可以返回一个值并在 View 中设置(MVP 结构),为什么要在 Presenter 中创建一个界面的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  3. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  4. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  5. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  6. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

    为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

  7. ruby - ruby 中的 TOPLEVEL_BINDING 是什么? - 2

    它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput

  8. ruby - Infinity 和 NaN 的类型是什么? - 2

    我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串

  9. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  10. ruby - 为什么 SecureRandom.uuid 创建一个唯一的字符串? - 2

    关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?

随机推荐