已经两个月了,我一直在使用 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/
类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
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%
我主要使用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
为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返
它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput
我可以得到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类的两个特殊实例的字符串
如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象
关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?