MVP patterm

MVP is architectural pattern that separates an application into three main logical components Model, View, and Presenter.

It is a derivation of the MVC pattern, in which Controller is replaced with Presenter.

Model is responsible to manage data, that independent from the user interface.

View is responsible for rendering UI elements, accept user input and pass it to the Presenter. View does not know anything about Model. It interacts only with Presenter. View chooses which Presenter to use.

Presenter is responsible for view and model interaction. It acts as mediator between View and Model. Presenter provides all necessary data to View, handles events from View and Model. When the model data are modified Presenter modify View or ask View to update itself. In last case Presenter provides all necessary data to View. Communication between View-Presenter and Presenter-Model happens via an interface. So Presenter does not know anything about View and Model.

MVP vs MVC

In MVC View may interact with Controller and Model directly.

In MVP View cannot interact with Model directly.

example

1. Define interfaces for View, Model and Presenter. Usually they are nested in one contract interface.

interface LoginContract {

    interface View {
        fun setLogin(s: String)
        fun showError(errMsg: String)
        fun onLogged()
    }

    interface Presenter{
        fun getLastUser(): String
        fun doAuth(login: String, password: String)
        fun onDestroy()
    }

    interface Model{

        fun interface OnAuthCallback{
            fun onFinished(errMsg: String?)
        }

        fun getLastUser(): String
        fun auth(login: CharSequence, psw: CharSequence, cb: OnAuthCallback)
    }

}

2. Define Presenter.

class LoginPresenter(val view: LoginContract.View, val model: LoginContract.Model) :
    LoginContract.Presenter {

    override fun getLastUser(): String = model.getLastUser()

    override fun doAuth(login: String, password: String) {
        model.auth(login, password) {
            if (it != null) {
                view.showError(it)
            }else{
                view.onLogged()
            }
        }
    }

    override fun onDestroy() {
        // .. do something
    }
}

Yeah, sometimes you can see how Presenter interacts with Model directly without interface.

3. Define View. In Android activities and fragments may act as View.

// ...
import kotlinx.android.synthetic.main.activity_login.*

class LoginActivity : AppCompatActivity(), LoginContract.View {

    private lateinit var presenter: LoginContract.Presenter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)
        presenter = LoginPresenter(this, LoginModel())
    }

    override fun onDestroy() {
        super.onDestroy()
        presenter.onDestroy()
    }

    override fun setLogin(s: String) {
        edLogin.setText(s)
    }

    override fun onLogged() {
        finish()
    }

    override fun showError(errMsg: String) {
        Toast.makeText(this, errMsg, Toast.LENGTH_SHORT).show()
    }
}