在 MVP 模式中将 View 的引用传递给 Presenter 是一种不好的做法吗?
Is a bad practice passing references of a View to a Presenter in MVP Pattern?
我有一个使用 MVP 模式的 Kotlin 的 Android 大项目,我开始努力进行单元测试(测试演示者模拟视图界面)。原因是我在演示者中传递了对我的函数的视图引用,而不得不模拟它们真的很糟糕,例如:
我的代码如下所示:
class MainActivity : Activity(), MainActivityView {
@BindView(R.id.numberTV)
lateinit var numberTV : AppCompatTextView
private val mainActivityPresenter = MainActivityPresenter(this)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mainActivityPresenter.onCreate()
}
override fun showNumber() {
mainActivityPresenter.showNumber(numberTV, 22)
}
}
interface MainActivityView {
fun showNumber()
}
class MainActivityPresenter(private val mainActivityView: MainActivityView) {
fun showNumber(numberTV: AppCompatTextView, number: Int) {
numberTV.text = if (number < 0) {
"Not compatible"
} else if (number < 10) {
number.toString()
} else {
"9+"
}
}
fun onCreate() {
mainActivityView.showNumber()
}
}
我目前的问题是,当我在 单元测试 中使用 Mockito 测试函数 showNumber(AppCompatTextView, Int)
时,我应该模拟视图以通过测试(因为它不能为空)。
在此处进行单元测试时,哪种方法更好?
我的想法是:
- 从
MainActivityPresenter
中抓取numberTV: AppCompatTextView
,例如mainActivityPresenter.getBindViews().mainIV
- 仅从演示者返回文本逻辑(
"Not compatible"
、number.toString()
或 "9+"
),尽管有时需要在多个视图之间执行逻辑 (2+)
你会怎么做?
编辑
我想指出,不向演示者传递任何 View
,可能是异步调用的问题。
示例:使用 Glide 设置图像:
internal fun showImageAccordingToCache(cachedSplashScreenUri: String?, mainImageView: ImageView) {
Glide.with(context)
/**
* Save in cache, but we say to load it from cache. Otherwise it will throw an error
*/
.setDefaultRequestOptions(defaultDiskStrategy()
.onlyRetrieveFromCache(true))
.load(cachedSplashScreenUri)
.listener(object : RequestListener<Drawable> {
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
/**
* And when that error is thrown, we preload the image for the next time.
*/
activityViewPresenter.showLogo()
loadImageInCache(cachedSplashScreenUri)
return true
}
override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
return false
}
})
.into(mainImageView)
}
我认为你不应该将观点传递给你的演示者。您的演示者应该调用 mainActivityView 方法来显示所需的数据。这应该是您的 mainAcitivityView
中的方法
override fun showNumber(number: String) {
numberTV.text = number
}
你应该像这样从你的演示者那里调用它
fun onCreate() {
showNumber(22)
}
fun showNumber(number: Int) {
numberString:String = if (number < 0) {
"Not compatible"
} else if (number < 10) {
number.toString()
} else {
"9+"
}
mainActivityView.showNumber(numberString:String)
}
Presenter中有界面View是正常的。在您的情况下,MainActivityView 是表示您的 Activity 必须遵守的合同的界面。您通常会在演示者的构造函数中传递该视图(您已经在做的事情),方法是使用匕首将其注入演示者。
现在这个,不是很平常:
fun showNumber(numberTV: AppCompatTextView, number: Int) {
numberTV.text = if (number < 0) {
"Not compatible"
} else if (number < 10) {
number.toString()
} else {
"9+"
}
}
主持人现在知道了"Android SDK components",这可不是什么好事。在这种情况下你应该做的是:
fun showNumber(number: Int) {
if (number < 0) {
mainActivityView.setNumberText("Not compatible");
} else if (number < 10) {
mainActivityView.setNumberText(number.toString());
} else {
mainActivityView.setNumberText("9+");
}
}
要对此进行测试,您将模拟视图并查看是否根据数量实际调用了这些方法中的每一个。(在 java 中)。
@Mock
MainActivityView view;
@Test
public fun shouldShowCorrectNumber() {
int number = 10;
presenter.showNumber(number);
verify(view).showNumber("9+");
}
至于异步调用,我在使用Glide库时通常看到的是在activity中使用,而不是在presenter中使用。例如,其他类型的异步调用可能会进入其他层。我通常在 Interactor 层中看到网络调用,并回调到演示者:
我有一个使用 MVP 模式的 Kotlin 的 Android 大项目,我开始努力进行单元测试(测试演示者模拟视图界面)。原因是我在演示者中传递了对我的函数的视图引用,而不得不模拟它们真的很糟糕,例如:
我的代码如下所示:
class MainActivity : Activity(), MainActivityView {
@BindView(R.id.numberTV)
lateinit var numberTV : AppCompatTextView
private val mainActivityPresenter = MainActivityPresenter(this)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mainActivityPresenter.onCreate()
}
override fun showNumber() {
mainActivityPresenter.showNumber(numberTV, 22)
}
}
interface MainActivityView {
fun showNumber()
}
class MainActivityPresenter(private val mainActivityView: MainActivityView) {
fun showNumber(numberTV: AppCompatTextView, number: Int) {
numberTV.text = if (number < 0) {
"Not compatible"
} else if (number < 10) {
number.toString()
} else {
"9+"
}
}
fun onCreate() {
mainActivityView.showNumber()
}
}
我目前的问题是,当我在 单元测试 中使用 Mockito 测试函数 showNumber(AppCompatTextView, Int)
时,我应该模拟视图以通过测试(因为它不能为空)。
在此处进行单元测试时,哪种方法更好?
我的想法是:
- 从
MainActivityPresenter
中抓取numberTV: AppCompatTextView
,例如mainActivityPresenter.getBindViews().mainIV
- 仅从演示者返回文本逻辑(
"Not compatible"
、number.toString()
或"9+"
),尽管有时需要在多个视图之间执行逻辑 (2+)
你会怎么做?
编辑
我想指出,不向演示者传递任何 View
,可能是异步调用的问题。
示例:使用 Glide 设置图像:
internal fun showImageAccordingToCache(cachedSplashScreenUri: String?, mainImageView: ImageView) {
Glide.with(context)
/**
* Save in cache, but we say to load it from cache. Otherwise it will throw an error
*/
.setDefaultRequestOptions(defaultDiskStrategy()
.onlyRetrieveFromCache(true))
.load(cachedSplashScreenUri)
.listener(object : RequestListener<Drawable> {
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
/**
* And when that error is thrown, we preload the image for the next time.
*/
activityViewPresenter.showLogo()
loadImageInCache(cachedSplashScreenUri)
return true
}
override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
return false
}
})
.into(mainImageView)
}
我认为你不应该将观点传递给你的演示者。您的演示者应该调用 mainActivityView 方法来显示所需的数据。这应该是您的 mainAcitivityView
中的方法override fun showNumber(number: String) {
numberTV.text = number
}
你应该像这样从你的演示者那里调用它
fun onCreate() {
showNumber(22)
}
fun showNumber(number: Int) {
numberString:String = if (number < 0) {
"Not compatible"
} else if (number < 10) {
number.toString()
} else {
"9+"
}
mainActivityView.showNumber(numberString:String)
}
Presenter中有界面View是正常的。在您的情况下,MainActivityView 是表示您的 Activity 必须遵守的合同的界面。您通常会在演示者的构造函数中传递该视图(您已经在做的事情),方法是使用匕首将其注入演示者。
现在这个,不是很平常:
fun showNumber(numberTV: AppCompatTextView, number: Int) {
numberTV.text = if (number < 0) {
"Not compatible"
} else if (number < 10) {
number.toString()
} else {
"9+"
}
}
主持人现在知道了"Android SDK components",这可不是什么好事。在这种情况下你应该做的是:
fun showNumber(number: Int) {
if (number < 0) {
mainActivityView.setNumberText("Not compatible");
} else if (number < 10) {
mainActivityView.setNumberText(number.toString());
} else {
mainActivityView.setNumberText("9+");
}
}
要对此进行测试,您将模拟视图并查看是否根据数量实际调用了这些方法中的每一个。(在 java 中)。
@Mock
MainActivityView view;
@Test
public fun shouldShowCorrectNumber() {
int number = 10;
presenter.showNumber(number);
verify(view).showNumber("9+");
}
至于异步调用,我在使用Glide库时通常看到的是在activity中使用,而不是在presenter中使用。例如,其他类型的异步调用可能会进入其他层。我通常在 Interactor 层中看到网络调用,并回调到演示者: