O padrão de arquitetura Model-View-Presenter (MVP) é amplamente utilizado no desenvolvimento Android para promover a separação de responsabilidades e facilitar a testabilidade do código. Ao dividir a aplicação nessas três camadas, reduzimos o acoplamento e tornamos a base de código mais modular e fácil de manter.
Definição do Contrato (Contract)
Uma prática recomendada no MVP é utilizar uma interface de contrato que agrupe as definições de interação entre o Model, a View e o Presenter. Isso centraliza a compreensão do fluxo de dados e das ações disponíveis para um determinado módulo da aplicação.
public interface AuthenticationContract {
interface Model {
void authenticateUser(String username, String credential, Presenter presenter);
}
interface View {
void displayErrorMessage(String error);
void navigateToHomeScreen();
String retrieveUsername();
String retrieveCredential();
}
interface Presenter {
void handleAuthenticationSuccess();
void handleAuthenticationFailure(String errorMessage);
}
}
Implementação da Camada Model
O Model é responsável por lidar com a lógica de negócios, regras de validação e a recuperação de dados. Neste exemplo, simulamos uma validação de credenciais de forma assíncrona ou síncrona, dependendo da implementação real.
public class AuthenticationModel implements AuthenticationContract.Model {
@Override
public void authenticateUser(String username, String credential, AuthenticationContract.Presenter presenter) {
// Simulação de validação de credenciais contra um repositório local ou remoto
boolean isValid = "admin".equals(username) && "secret".equals(credential);
if (isValid) {
presenter.handleAuthenticationSuccess();
} else {
presenter.handleAuthenticationFailure("Credenciais inválidas. Tente novamente.");
}
}
}
Implementação da Camada View
A View (geralmente uma Activity ou Fragment no Android) é responsável exclusivamente por exibir os dados e capturar as interações do usuário. Ela delega toda a lógica de negócios ao Presenter, mantendo-se "boba" e focada apenas na interface.
public class AuthenticationActivity extends AppCompatActivity implements AuthenticationContract.View {
private EditText inputUsername;
private EditText inputCredential;
private Button buttonAuthenticate;
private AuthenticationContract.Presenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_authentication);
setupUI();
}
private void setupUI() {
inputUsername = findViewById(R.id.edit_text_username);
inputCredential = findViewById(R.id.edit_text_credential);
buttonAuthenticate = findViewById(R.id.button_authenticate);
presenter = new AuthenticationPresenter();
presenter.attachView(this);
buttonAuthenticate.setOnClickListener(v -> {
AuthenticationContract.Model model = new AuthenticationModel();
model.authenticateUser(retrieveUsername(), retrieveCredential(), presenter);
});
}
@Override
public void displayErrorMessage(String error) {
Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
}
@Override
public void navigateToHomeScreen() {
Toast.makeText(this, "Autenticação bem-sucedida!", Toast.LENGTH_SHORT).show();
// A navegação real para a tela principal seria iniciada aqui via Intent
}
@Override
public String retrieveUsername() {
return inputUsername.getText().toString().trim();
}
@Override
public String retrieveCredential() {
return inputCredential.getText().toString().trim();
}
}
Implementação da Camada Presenter
O Presenter atua como um intermediário. Ele recebe os eventos da View, solicita a execução de tarefas ao Model e, por fim, formata os resultados para que a View possa exibi-los adequadamente ao usuário.
public class AuthenticationPresenter implements AuthenticationContract.Presenter {
private AuthenticationContract.View view;
public void attachView(AuthenticationContract.View view) {
this.view = view;
}
@Override
public void handleAuthenticationSuccess() {
if (view != null) {
view.navigateToHomeScreen();
}
}
@Override
public void handleAuthenticationFailure(String errorMessage) {
if (view != null) {
view.displayErrorMessage(errorMessage);
}
}
}