简体   繁体   English

将视图的引用传递给MVP模式中的Presenter是一种不好的做法吗?

[英]Is a bad practice passing references of a View to a Presenter in MVP Pattern?

I have a big project for Android with Kotlin using the MVP pattern, and I'm starting to struggle to do Unit Tests (testing the presenters mocking the view interfaces). 我有一个关于Android的大项目,Kotlin使用MVP模式,我开始努力进行单元测试(测试演示者模拟视图接口)。 The reason is I'm passing view references to my functions in the presenters and it's really bad having to mock them, example: 原因是我在演示者中传递对我的函数的视图引用,并且必须模拟它们真的很糟糕,例如:

My code would look like: 我的代码看起来像:

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()
    }
}

My current problem is, when I'm testing the function showNumber(AppCompatTextView, Int) with Mockito in Unit Tests , I should mock the view just to pass the test (as it can't be null). 我当前的问题是,当我在单元测试中使用Mockito测试函数showNumber(AppCompatTextView, Int)时,我应该模拟视图以传递测试(因为它不能为null)。

Which one would be a better approach to do Unit Tests here? 哪一个是更好的方法来进行单元测试?

My thoughts are: 我的想法是:

  1. Grabbing the numberTV: AppCompatTextView from the MainActivityPresenter , such as mainActivityPresenter.getBindViews().mainIV MainActivityPresenter numberTV: AppCompatTextView ,例如mainActivityPresenter.getBindViews().mainIV
  2. Returning just the text logic ( "Not compatible" , number.toString() or "9+" ) from the presenter, although sometimes the requirements require to do logic between more than view (2+) 从演示者返回文本逻辑( "Not compatible"number.toString()"9+" ),尽管有时需要在视图之外执行逻辑(2+)

What would you do? 你会怎么做?


EDIT 编辑

I would like to point out that not passing any View to the presenter, might be an issue with asynchronous calls. 我想指出,不将任何View传递给演示者,可能是异步调用的问题。

Example: setting an image with Glide: 示例:使用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)
}

I think you should not pass views to your presenter. 我认为你不应该向你的演示者传递意见。 Your presenter should call mainActivityView methods to show the required Data. 您的演示者应该调用mainActivityView方法来显示所需的数据。 This should be the method in your mainAcitivityView 这应该是mainAcitivityView中的方法

override fun showNumber(number: String) {
    numberTV.text = number
}

And you should call this from your presenter like this 你应该像这样从你的演示者那里打电话

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)
}

It's normal to have an interface View in your presenter. 在演示者中有一个界面View是很正常的。 In your case MainActivityView is the interface that represents the contract your Activity must comply to. 在您的情况下,MainActivityView是表示您的Activity必须遵守的合同的接口。 You'd normally pass that view in the constructor of the presenter (what you're already doing) on by using dagger to inject it into the presenter. 您通常会使用dagger将其注入演示者,从而在演示者的构造函数(您已经在做的事情)中传递该视图。

Now this, is not very usual: 现在这个,不常见:

 fun showNumber(numberTV: AppCompatTextView, number: Int) {
        numberTV.text = if (number < 0) {
            "Not compatible"
        } else if (number < 10) {
            number.toString()
        } else {
            "9+"
        }
    }

Now the presenter knows about "Android SDK components" which is not a good thing. 现在主持人知道“Android SDK组件”这不是一件好事。 In this case what you should be doing is this: 在这种情况下,你应该做的是:

 fun showNumber(number: Int) {
        if (number < 0) {
            mainActivityView.setNumberText("Not compatible");
        } else if (number < 10) {
            mainActivityView.setNumberText(number.toString());
        } else {
            mainActivityView.setNumberText("9+");
        }
    }

To test this you would mock the view and see if depending on number each of those methods were actually called.(In java). 为了测试这个,你会模拟视图,看看是否依赖于数字实际调用了这些方法。(在java中)。

@Mock
MainActivityView view;

@Test
public fun shouldShowCorrectNumber() {

    int number = 10;
    presenter.showNumber(number);
    verify(view).showNumber("9+");
}

As for async calls, what I usually see when using the Glide library is that it is used in the activity, not in the presenter. 对于异步调用,我在使用Glide库时通常会看到它在活动中使用,而不是在演示者中使用。 Other types of async calls might go in other layers, for example. 例如,其他类型的异步调用可能会出现在其他层中。 I usually see network calls in the Interactor layer with callbacks to the presenter: 我通常会在Interactor层看到网络调用,并对演示者进行回调:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在视图或演示者中初始化模型-MVP模式 - Initialize Model in View or in Presenter - MVP Pattern 主持人是否知道MVP模式中的活动/上下文是个坏主意? - Does the presenter having knowledge of the Activity / Context a bad idea in the MVP pattern? 我们应该检查Presenter或MVP模式的Activity中的视图可见性吗? - Should we check for view visibility in Presenter or Activity in MVP pattern? 如何使用Dagger2将Presenter注入视图(MVP模式) - How to inject a Presenter into a View (MVP pattern) using Dagger2 在MVP模式中如何访问Presenter中的指定视图组件? - In MVP pattern how to access specified view component in Presenter? 为什么我们对MVP模式使用Base View和Base Presenter? - Why do we use Base View and Base Presenter for MVP pattern? 每个视图都必须有 MVP 模式的演示者吗? - Does every view have to have presenter in MVP pattern? Android MVP-将字符串资源从演示者传递到视图 - Android MVP - passing string resources from the presenter to the view 通过Android MVP中的构造函数将片段(View)Presenter传递给适配器 - Passing Fragment's (View) Presenter to Adapter Through Constructor in Android MVP 在MVP中是View或Presenter的onClick责任吗? - In MVP is onClick responsibility of View or Presenter?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM