如何避免MVP中的乒乓方法调用?

时间:2019-04-30 12:13:47

标签: design-patterns mvp android-mvp

在我的Android应用中,我有一个MVP模式的片段。假设我们有:

  • CalculationFragment(视图)
  • CalculationPresenter(演示者)
  • CalculationNetwork(模型)

我需要进行多步计算或REST调用:

  1. 在CalculationFragment的onViewCreated()中,我调用val sessionId = presenter.firstCall() as String来检索sessionToken以作进一步处理。
  2. 然后,演示者通过REST检索sessionToken。到目前为止很酷。然后,我需要链中的下一个调用,该调用消耗检索到的sessionToken和一个NfcAdapter.getDefaultAdapter(activity)

val jsonObject = secondCall(sessionId, nfcAdapter)

由于我在演示者中,所以我既没有活动也没有NfcAdapter(老实说,我不想这样做)。我在这里有两个选择:

  1. Detour over view在演示者中,我返回带有sessionToken view?.onFirstCallResult(sessionToken)的片段,并立即从CalculationFragment的onFirstCallResult()调用presenter.secondCall(sessionToken, NfcAdapter.getDefaultAdapter(activity))
  2. Short way, handled from pesenter我已经在第一个呼叫中为第二个呼叫移交了activity / NfcAdapter,并将其存储在演示者中。我不需要在视图和演示者之间进行很多乒乓球操作。此外,我可以留在主持人中处理所有电话吗?

什么是优雅的解决方案/模式?

2 个答案:

答案 0 :(得分:0)

Presenter 模型命令添加更多逻辑,从而将其从视图中删除。这通常是一种更好的方法。考虑到Single Responsibility principle,并从视图中移动应用程序/域逻辑。

您可以通过以下两种方式执行此操作:

  • 使用Command演示者创建调用命令完成后,演示者会将结果返回到视图

  • 设计您的演示者,以像在第二张图中一样完成工作。如果演示者很简单就可以。对于更复杂的场景,使用命令执行逻辑的职责与应在何时调用的职责分开。

    < / li>

根据您的情况,您需要从视图获取 NfcAdapter Presenter Command (如果你有一个)。

您可以通过以下几种方式进行操作:

  • 将其传递给 Presenter
  • 的构造函数
  • Presenter 中添加特殊的初始化方法,并将所需的所有依赖项传递给此方法(public void initialize(NfcAdapter adapter, ...)
  • 查看中添加一个方法,演示者可以调用该方法以在需要时获取适配器(NfcAdapter view.getAdapter())。
  • 将它作为参数传递给方法调用(就像您所做的那样);

选择一种方法取决于几个因素,开发人员的口味便是其中之一。就我个人而言,我会选择方法1或2。我想初始化对象的依赖关系(在这种情况下,是 Presenter )在生命周期的乞讨中,如果始终需要此对象,并且它们不会改变。如果每次调用此方法时它们都发生变化,则将它们传递给方法调用。在这种情况下,我认为您不会更改 NfcAdapter

让我们设计一个命令。因为您的描述比较笼统,而且没有描述确切的顺序(first_call(),second_call()太笼统),所以我将设计一个简单的非特定系统来进行几次调用。我将使用伪代码。大多数事情都是不确定的,因为我不知道返回类型和东西。

我们将此命令称为 CalculateCommand 。此命令将使用 CalculationModel 进行计算。接下来,让我们定义一个 TokenService ,其中将包含获取令牌(API调用)的逻辑。

public class TokenService {
   public SessionToken getToken() { ... }
}

public class CalculationResult {
  // represent whatever the result is...
}

public class CalculateCommand {

    private NfcAdapter mNfcAdapter;
    private TokenService mTokenService;
    private CalculationModel mCalculationModel;

    private SessionToken mSessionToken;

    public CalculateCommand(
      NfcAdapter nfcAdapter, 
      TokenService tokenService, 
      CalculationModel calculationModel) {

      mAdapter = adapter;
      mTokenService = tokenService;
      mCalculationModel = calculatioModel;
   }

   public CalculationResult Execute() {

     startSession();

     // do more stuff if you need to 

     val result = calculate();

     return result;
  }

  private void startSession() {
     mSessionToken = mTokenService.getToken();
  }

  private Result calcualte() {
    //not sure what parameters it needs but pass them here
    return mCalculatioModel.calculate(params...);
  }
}

public class Presenter {

    private View mView;
    private NfdAdapter mAdapter;
    private CalculationModel mModel;
    private TokenService mTokenService;

    public Presenter(View view, NfdAdapter adapter) {

      mView = view;
      mNfcAdapter = adapter;
      mModel = new CalculationModel();

     // or get if from a Service Locator, DI whatever.. if you need to mock the 
     // TokenService for unittests

     mTokenService = new TokenService(); 
   }

  public void performCalculation() {

    val cmd = CalculationCommand(mAdapter, mTokenService, mModel);

    val result = cmd.execute();

    mView.setResult(result);    
 }

 public class View {

    private Presenter mPresenter;

    public View() {
       mPresenter = new Presenter(this, NfcAdapter.getDefault(activity);
    }

    public void onViewCreated() {
       mPresenter.performCalculation();
    }

    public void setResult(Result result) {
       // do something with the result
    }
}

检查这些资源以获取有关MVP及其风味的更多信息:

https://martinfowler.com/eaaDev/uiArchs.html

https://www.martinfowler.com/eaaDev/PassiveScreen.html

https://martinfowler.com/eaaDev/SupervisingPresenter.html

答案 1 :(得分:0)

  

我需要进行多步计算或REST调用:

     

在CalculationFragment的onViewCreated()中,我称val sessionId =   presenter.firstCall()as String来获取sessionToken以便进一步使用   处理。然后,演示者通过REST检索sessionToken。所以   太酷了。然后,我需要链中的下一个通话,这消耗了   检索了sessionToken和一个NfcAdapter.getDefaultAdapter(活动)。

如果我正确理解了您的问题,

获取API令牌,然后自动进行另一个REST API调用的最常见方法,可能是使用 okhttp Authenticator Interceptor