Tabla de contenido
2.1 División de la implementación de MVP
2.2 Interfaz de comunicación implementada por MVP
2.3 Almacén de funciones realizado por MVP
3. Ventajas y desventajas del modelo MVP
1. Por qué usar el patrón MVP
En el desarrollo de Android, la responsabilidad principal de Actividad es cargar el diseño de la aplicación e inicializar la interfaz de usuario, aceptar y procesar las solicitudes de operación de los usuarios. Sin embargo, a medida que aumenta la complejidad de la interfaz y la lógica, las responsabilidades de la clase Activity continúan aumentando y se vuelven grandes e infladas. Entonces necesitamos usar el modelo MVP para resolver los problemas de confusión, redundancia y fuerte acoplamiento.
1.1 Ejemplo de descripción
La siguiente es una demostración del inicio de sesión del usuario
public class LoginActivity extends AppCompatActivity {
EditText inputUserName;
EditText inputPassword;
Button btnLogin;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
//点击登录
btnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//获取账户和密码
final String userName = inputUserName.getText().toString();
final String password = inputPassword.getText().toString();
//判空
boolean isEmptyPassword = userName == null || userName.length() == 0;
//是否符合规范
boolean isUserNameValid = Pattern.compile("^[A-Za-z0-9]{3,20}+$").matcher(userName).matches();
boolean isPasswordValid = Pattern.compile("^[A-Za-z0-9]{3,20}+$").matcher(password).matches();
if (isEmptyPassword) {
Toast.makeText(LoginActivity.this, "请输入帐号密码", Toast.LENGTH_SHORT).show();
} else {
if (isUserNameValid && isPasswordValid) {
new Thread(new Runnable() {
@Override
public void run() {
//..登录请求
boolean loginResult = false;
//登录结果
if (loginResult) {
Toast.makeText(LoginActivity.this, "登录成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(LoginActivity.this, "登录失败", Toast.LENGTH_SHORT).show();
}
}
}).start();
} else {
Toast.makeText(LoginActivity.this, "帐号密码格式错误", Toast.LENGTH_SHORT).show();
}
}
}
});
}
}
La implementación del servicio de inicio de sesión incluye: escuchar eventos de clic, obtener datos de entrada, verificar datos, procesar juicio vacío, solicitud de inicio de sesión y mostrar resultados de inicio de sesión. Puede ver claramente los problemas de confusión de código y acoplamiento pesado. Entonces, si lo cambiamos al modo MVP, ¿cómo dividirlo? En primer lugar, debemos entender lo que hace el Modelo/Vista/Presentador en el modo MVP.
2. Cómo usar el patrón MVP
MVP significa Modelo, Vista y Presentador.
- El modelo es muy simple, para la carga de datos. Tales como operaciones de base de datos, consultas de archivos, solicitudes de red.
- La capa Vista es responsable de mostrar los datos y la interacción del usuario. Las actividades y los fragmentos de MVP se reflejan en esta capa, como la carga de las vistas de la interfaz de usuario y la configuración de los monitores.
- La capa Presentador maneja la distribución de varias lógicas.Después de recibir las instrucciones de retroalimentación de la interfaz de usuario de la capa Ver, la lógica de procesamiento de distribución se entrega a la capa Modelo para operaciones comerciales específicas.
2.1 División de la implementación de MVP
Después de comprender los principios básicos de implementación del patrón MVP, la función de inicio de sesión se puede dividir en:
1. Valor, número de cuenta y contraseña de EditText (capa de vista clara, no implica operaciones lógicas)
2. Juicio y verificación nulos (Presentador pero implica Vista, porque se utilizan el número de cuenta y la contraseña, a través de la forma de paso de parámetros)
3. Solicitud de inicio de sesión (un verdadero modelo, el procesamiento obviamente está en la capa de presentador)
4. Actualice la interfaz de usuario (capa de vista)
Reescribimos el código de solicitud de inicio de sesión de esta manera.
Capa de modelo
public class LoginModel {
public LoginModel() {
}
//回调的接口
public interface OnLoginCallback{
void onResponse(boolean success);
}
public void login(String username,String password,final OnLoginCallback onLoginCallback){
//..登录请求
boolean loginResult = false;
//登录结果
onLoginCallback.onResponse(loginResult);
}
}
Ver capa
public class LoginActivity extends AppCompatActivity {
EditText inputUserName;
EditText inputPassword;
Button btnLogin;
LoginPresenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
inputUserName = this.findViewById(R.id.et_username);
inputPassword = this.findViewById(R.id.et_password);
btnLogin = this.findViewById(R.id.bt_login);
//创建Presenter
presenter = new LoginPresenter(this);
btnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//调用Presenter层,登录逻辑execureLogin()
presenter.execureLogin(getEditorText(inputUserName),getEditorText(inputPassword));
}
});
}
//在View层获取输入数据
private String getEditorText(EditText et) {
return et.getText().toString();
}
//在View层显示结果
public void notifyLoginResult(boolean loginResult) {
if (loginResult) {
showTips("登录成功");
} else {
showTips("登录失败");
}
}
public void showTips(String verifyMsg) {
Toast.makeText(LoginActivity.this, verifyMsg, Toast.LENGTH_SHORT).show();
}
}
capa de presentador
public class LoginPresenter {
private LoginActivity activity;
private LoginModel model;
private String verifyMsg;
public LoginPresenter(LoginActivity activity) {
//获取View层
this.activity = activity;
//获取model对象
this.model = new LoginModel();
}
public void execureLogin(String username,String password){
//逻辑类的方法
boolean verifyBefore = verifyBeforeLogin(username,password);
if (verifyBefore) {
//调用login()实现网络请求
model.login(username, password, new LoginModel.OnLoginCallback() {
//实现回调方法
@Override
public void onResponse(boolean success) {
//将结果给到View层
activity.notifyLoginResult(success);
}
});
}else {
activity.showTips(verifyMsg);
}
}
private boolean verifyBeforeLogin(String username, String password) {
boolean isEmpty = isEmpty(username) || isEmpty(password);
boolean isValid = isValid(username) && isValid(password);
if (isEmpty) {
verifyMsg = "请输入帐号或密码";
return false;
}
if (isValid) {
return true;
}
verifyMsg = "帐号或密码错误";
return false;
}
private boolean isValid(String s) {
return Pattern.compile("^[A-Za-z0-9]{3,20}+$").matcher(s).matches();
}
}
Aunque la página se divide en tres capas según MVP, el acoplamiento en el código aún existe:
- El presentador sostiene los objetos Vista (Actividad) y Modelo
- Ver retiene objeto de presentador
2.2 Interfaz de comunicación implementada por MVP
El uso de la interfaz para lograr el propósito de desacoplamiento requiere que View y Presenter implementen interfaces para llamadas externas, respectivamente.
- Los métodos que llama View para Presenter son
notifyLoginResult
yshowTips
; - Hay métodos que Presenter puede llamar para View
executeLogin
.
Proporcione métodos para llamadas externas en la interfaz, luego impleméntelos en View y Presenter respectivamente, y finalmente cambie el objeto contenedor a una interfaz. La implementación específica es la siguiente:
Definir la clase de interfaz de IPresenter
public interface IPresenter {
//执行登录的接口
void execureLogin(String username,String password);
}
Definir la clase de interfaz IView
public interface IView {
//显示结果的接口
void notifyLoginResult(boolean loginResult);
void showTips(String verifyMsg);
}
//LoginPresenter实现IPresenter接口
public class LoginPresenter implements IPresenter{
//原来的LoginActivity改为了IView
private IView activity;
...
}
----------------------------------------------------------------------------------------
//LoginActivity 实现IView接口
public class LoginActivity extends AppCompatActivity implements IView{
//将原来的LoginPresenter改为IPresenter
IPresenter presenter;
...
}
2.3 Almacén de funciones realizado por MVP
MVP introduce los conceptos de BaseInterface y Contract. La interfaz definida debe heredar de IView e IPresenter, y ser administrada de manera uniforme por contrato.
paso 1. Primero defina la interfaz pública para la especificación unificada
public interface IPresenter {
...
//一些公共的接口
}
public interface IView {
...
//一些公共的接口
}
paso 2. La interfaz definida debe heredar IView e IPresenter, y ser administrada por Contract
public interface LoginContract {
interface View extends IView{
//显示结果的接口
void notifyLoginResult(boolean loginResult);
void showTips(String verifyMsg);
}
interface Presenter extends IPresenter{
void login(String name,String password);
}
}
paso 3. Implementar las interfaces IView e IPresenter
public class LoginActivity extends AppCompatActivity implements IView {
EditText inputUserName;
EditText inputPassword;
Button btnLogin;
LoginPresenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
inputUserName = this.findViewById(R.id.et_username);
inputPassword = this.findViewById(R.id.et_password);
btnLogin = this.findViewById(R.id.bt_login);
presenter = new LoginPresenter(this);
btnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
presenter.execureLogin(getEditorText(inputUserName),getEditorText(inputPassword));
}
});
}
private String getEditorText(EditText et) {
return et.getText().toString();
}
public void notifyLoginResult(boolean loginResult) {
if (loginResult) {
showTips("登录成功");
} else {
showTips("登录失败");
}
}
public void showTips(String verifyMsg) {
Toast.makeText(LoginActivity.this, verifyMsg, Toast.LENGTH_SHORT).show();
}
}
public class LoginPresenter implements IPresenter {
//原来的LoginActivity改为了IView
private IView activity;
private LoginModel model;
private String verifyMsg;
public LoginPresenter(LoginActivity activity) {
this.activity = activity;
this.model = new LoginModel();
}
public void execureLogin(String username,String password){
boolean verifyBefore = verifyBeforeLogin(username,password);
if (verifyBefore) {
model.login(username, password, new LoginModel.OnLoginCallback() {
@Override
public void onResponse(boolean success) {
activity.notifyLoginResult(success);
}
});
}else {
activity.showTips(verifyMsg);
}
}
private boolean verifyBeforeLogin(String username, String password) {
boolean isEmpty = isEmpty(username) || isEmpty(password);
boolean isValid = isValid(username) && isValid(password);
if (isEmpty) {
verifyMsg = "请输入帐号或密码";
return false;
}
if (isValid) {
return true;
}
verifyMsg = "帐号或密码错误";
return false;
}
private boolean isValid(String s) {
return Pattern.compile("^[A-Za-z0-9]{3,20}+$").matcher(s).matches();
}
}
3. Ventajas y desventajas del modelo MVP
Ventajas de MVP:
El modelo está completamente separado de la vista para reducir el grado de acoplamiento. Podemos modificar la vista sin afectar el modelo; podemos
usar un presentador para varias vistas sin cambiar la lógica del presentador para mejorar la reutilización del código.
La lógica se coloca en el Presentador, lo que favorece el desarrollo basado en pruebas.
Desventajas de MVP:
La mayor complejidad del código, especialmente para el desarrollo de pequeñas aplicaciones de Android, hará que el programa sea redundante.
La interacción entre la vista y el presentador será demasiado frecuente. Una vez que cambie la vista, el presentador también cambiará.
referencia