【问题标题】:How to avoid ping pong method call in MVP?如何避免 MVP 中的乒乓方法调用?
【发布时间】:2019-04-30 12:13:47
【问题描述】:

在我的 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我已经在第一次通话中交出了第二次通话的活动/NfcAdapter,并将其存储在演示者中。我不需要在视图和演示者之间频繁地打乒乓球。此外,我可以在所有通话中留在演示者中吗?

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

【问题讨论】:

  • @expandable 我尝试在上图中可视化流程(请参阅链接)。由于我的片段在活动中膨胀,我可以访问我的所有片段 fragment.activity 以获取相应的活动。这就是问题,如何按照 MVP 清洁架构原则移交 nfcAdapter。

标签: design-patterns mvp android-mvp


【解决方案1】:

PresenterModelCommand 添加更多逻辑,从而将其从 View 中删除。这通常是更好的方法。考虑Single Responsibility principle 并从View 中移动应用程序/域逻辑。

您可以通过以下几种方式做到这一点:

  • 使用CommandPresenter创建调用它。当 Command 完成时,Presenter 会将结果返回到 View

  • 设计您的 Presenter 来完成您在第二张图中所做的工作。如果演示者很简单,那没关系。对于更复杂的场景,使用命令执行逻辑的责任与何时应该调用它的责任分开。

在您的情况下,您需要将 NfcAdapterViewPresenterCommand (如果你有)。

您可以通过以下几种方式做到这一点:

  • Presenter 的构造函数中传递它
  • Presenter中添加一个特殊的initialization方法,并将它需要的所有依赖传递给这个方法(public void initialize(NfcAdapter adapter, ...))
  • View 添加一个方法,Presenter 可以在需要时调用以获取适配器 (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

【讨论】:

【解决方案2】:

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

在 CalculationFragment 的 onViewCreated() 中,我调用 val sessionId = presenter.firstCall() 作为 String 来检索 sessionToken 以便进一步 过程。然后演示者通过 REST 检索 sessionToken。所以 太酷了。然后我需要链中的下一个调用,什么消耗 检索到 sessionToken 和 NfcAdapter.getDefaultAdapter(activity)。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-08
    • 1970-01-01
    • 2020-06-13
    • 1970-01-01
    相关资源
    最近更新 更多