Недавно я захотел отрефакторить код, потому что проект должен использоваться несколькими школами, и UI каждой школы отличается, но функциональность почти одинаковая. Хотя использование веток Gradle может справиться с различиями в коде, кодовая логика в общих частях постоянно растёт, поэтому я захотел провести рефакторинг. Я взглянул на недавно популярный шаблон MVP и нашёл его очень подходящим. Однако многие материалы в интернете просто скопированы, а некоторые даже содержат ошибки, поэтому я захотел описать шаблон MVP на основе собственного понимания, чтобы другим было легче его усвоить. Сначала поговорим о шаблоне проектирования MVC, который всем наиболее знаком.

Шаблон MVC разделяет M-слой и V-слой; M-слой и V-слой взаимодействуют через C-слой.
Теперь давайте рассмотрим MVP. В моём тексте ниже имена, начинающиеся с I, означают интерфейсы (рисовать диаграмму вручную утомительно...)
Самое большое отличие MVP от MVC в том, что P-слой заменяет прежний C-слой и больше не управляет конкретными реализациями, а работает с интерфейсами. Таким образом, ни разработка несколькими разработчиками, ни частые изменения UI не повлияют на P-слой. Пока интерфейсы C- и V-слоёв остаются неизменными, при изменениях UI нужно только изменить реализацию V-слоя; реализацию C-слоя даже не нужно менять. Это делает код очень понятным и упрощает тестирование, потому что логический слой и слой представления полностью разделены. Позвольте объяснить кодом — начиная с шаблона MVC, на примере входа в систему. В Android C-слой и V-слой уже хорошо разделены: XML, который мы пишем, эквивалентен V-слою, а Activity — C-слою.
public interface ICallback{
void receive(boolean success);
}
public class LoginModel{
public void login(String name,String password,ICallback callback){
WebApi.login(name,password,callback);
}
}
public class LoginActivity extends Activity{
private LoginModel mLoginModel;
private EditText mUserNameEt;
private EditText mPasswordEt;
private Button mSubmitBtn;
public void onCreate(......){
mLoginModel = new LoginModel(...);
mSubmitBtn.setOnClickListener(new OnClickListener(View view){
mLoginModel.login(mUserNameEt.get...,mPasswordEt.get...,new ICallback(){
public void receive(boolean success){
if(success){
startActivity(new Intent(this,MainActivity.class));
finish();
} else {
Toast.makeText(this,"登录失败",Toast.LENGTH_SHORT).show();
}
}
});
});
}
}
Давайте посмотрим, как написать это с помощью шаблона MVP. В комментарии под статьёй упоминалось написание класса контракта. Я разобрался с этим, и писать класс контракта действительно удобнее, поэтому приведённый ниже код — это то, что я переработал.
public interface ILoginContract {
public interface ILoginModel{
public void login(String name,String password,ICallback callback);
}
public interface ILoginPresenter{
public void login(String name,String password);
}
public interface ILoginView{
public void showDialog();
public void dismissDialog();
public void showToast(String message);
public void navigateToMain();
}
}
public class PresenterImpl implements ILoginPresenter,ICallback{
private ILoginView mLoginView;
private ILoginModel mLoginModel;
public PresenterImpl(ILoginView loginView){
this.mLoginView = loginView;
this.mLoginModel = new LoginModelImpl();
}
public void login(String name,String password){
if(isEmpty(name)||isEmpty(password)){
this.mLoginView.showToast("用户名或密码不能为空");
return;
}
this.mLoginModel.login(name,password,this);
}
public void receive(boolean success){
if(success){
this.mLoginView.navigateToMain();
}else{
this.mLoginView.showToast("登录失败");
}
}
private boolean isEmpty(String text){
return text==null||"".equals(text)?true:false;
}
}
public class LoginActivity extends Activity implements ILoginView{
private IPresenter mPresenter;
private EditText mUserNameEt;
private EditText mPasswordEt;
private Button mSubmitBtn;
public void onCreate(......){
mPresenter = new PresenterImpl(this);
mSubmitBtn.setOnClickListener(new OnClickListener(View view){
mPresenter.login(mUserNameEt.getText().toString(),
mPasswordEt.getText().toString());
});
}
public void showDialog(){
//显示一个转圈的dialog;
}
public void dismissDialog(){
//隐藏转圈的dialog;
}
public void showToast(String message){
Toast.makeText(this,message,Toast.LENGTH_SHORT).show();
}
public void navigateToMain(){
startActivity(new Intent(this,MainActivity.class));
finish();
}
}
Шаблон MVP действительно требует больше классов и кода, но это определённо того стоит. Вы можете видеть, что логика кода пишется яснее, чем в шаблоне MVC, и тестирование кода также проще. Вы даже можете реализовать функции, когда у вас есть только функциональность и нет макетов UI, а изменения UI не повлияют на какой-либо бизнес-код.
Описанный выше шаблон проектирования MVP — это моё личное понимание. Если есть ошибки, пожалуйста, исправьте меня. Спасибо. Давайте обсуждать и улучшать вместе.
Отправил: oDynamicSupermano