Kürzlich wollte ich den Code refaktorieren, weil das Projekt von mehreren Schulen genutzt werden soll und die UI jeder Schule anders ist, aber die Funktionalität fast identisch ist. Obwohl die Verwendung von Gradle-Branches die Codeunterschiede bewältigen kann, wächst die Codelogik in den gemeinsamen Teilen ständig, also wollte ich refaktorieren. Ich habe mir das kürzlich populäre MVP-Muster angesehen und fand es sehr passend. Allerdings wird viel Material online nur herumkopiert, und einige enthalten sogar Fehler, deshalb wollte ich das MVP-Muster basierend auf meinem eigenen Verständnis beschreiben, damit es für andere leichter zu erfassen ist. Sprechen wir zuerst über das MVC-Entwurfsmuster, mit dem alle am besten vertraut sind.

Das MVC-Muster entkoppelt die M-Schicht und die V-Schicht; die M-Schicht und die V-Schicht interagieren über die C-Schicht.

Schauen wir uns nun MVP an. In meinem folgenden Text bedeuten Namen, die mit I beginnen, Interfaces (das Diagramm per Hand zu zeichnen ist mühsam ...)

Der größte Unterschied zwischen MVP und MVC besteht darin, dass die P-Schicht die frühere C-Schicht ersetzt und nicht mehr konkrete Implementierungen, sondern Interfaces steuert. Auf diese Weise wirken sich weder Entwicklung durch mehrere Entwickler noch häufige UI-Änderungen auf die P-Schicht aus. Solange die Interfaces der C- und V-Schicht unverändert bleiben, erfordern UI-Änderungen nur die Anpassung der V-Schicht-Implementierung; die C-Schicht-Implementierung muss nicht einmal geändert werden. Dadurch bleibt der Code sehr übersichtlich und das Testen wird einfach, weil die Logikschicht und die View-Schicht vollständig getrennt sind. Lassen Sie mich anhand von Code sprechen – beginnend mit dem MVC-Muster und als Beispiel die Anmeldung. Android trennt die C-Schicht und die V-Schicht bereits gut: Das XML, das wir schreiben, entspricht der V-Schicht, und die Activity entspricht der C-Schicht.

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

Schauen wir uns an, wie man es mit dem MVP-Muster schreibt. Ein Kommentar unter dem Artikel erwähnte das Schreiben einer Contract-Klasse. Ich habe es mir angesehen, und das Schreiben einer Contract-Klasse ist tatsächlich bequemer, daher ist der folgende Code meine überarbeitete Version.

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

Das MVP-Muster erfordert zwar mehr Klassen und Code, aber es lohnt sich definitiv. Man sieht, dass die Codelogik klarer zu schreiben ist als im MVC-Muster, und auch das Testen des Codes ist einfacher. Man kann sogar Funktionen fertigstellen, wenn man nur die Funktionalität und keine UI-Mockups hat, und UI-Änderungen wirken sich nicht auf den Business-Code aus.

Das obige MVP-Entwurfsmuster ist mein persönliches Verständnis. Falls es Fehler gibt, korrigiert mich bitte. Danke. Lasst uns gemeinsam diskutieren und verbessern.

Eingereicht von: oDynamicSupermano