In my Android app, I have a Fragment in MVP pattern. Lets assume we have:
I need a multi-step calculation or REST call:
val sessionId = presenter.firstCall() as String
to retrieve a sessionToken for further process.NfcAdapter.getDefaultAdapter(activity)
. val jsonObject = secondCall(sessionId, nfcAdapter)
.
Since I am in the presenter, I do neither have the activity nor the NfcAdapter here (and I honestly do not want to). I have two options here:
view?.onFirstCallResult(sessionToken)
and call from the CalculationFragment's onFirstCallResult()
immediately presenter.secondCall(sessionToken, NfcAdapter.getDefaultAdapter(activity))
.What would be an elegant solution / pattern here?
Add more logic to the Presenter, Model or a Command thus removing it from the View. This is usually a better approach. Take into account the Single Responsibility principle and move application/domain logic from the View.
Here are couple of ways you can do this:
Use a Command. The Presenter will create and invoke it. When the Command finishes the Presenter will return results to the View.
Design your Presenter to do the work like you did in you second diagram. If the presenter is simple that's OK. For more complex scenarios use Commands to separate the responsibility of performing the logic from the responsibility of when it should be invoked.
In your case you need to get the NfcAdapter from the View to the Presenter and to a Command (if you have one).
Here are couple of ways you can do it:
public void initialize(NfcAdapter adapter, ...)
)NfcAdapter view.getAdapter()
).Choosing an approach depends of several factors, developer taste being one of them Personally I would choose either method 1 or 2. I like to initialize the dependencies of an object (the Presenter in this case) at the begging of it's life-cycle, if this object will be needed them all the time and they don't change. Pass them in the method call if they change for every time this method is called. In this case I don't think you will change the NfcAdapter.
Let's design a Command. Because you have a rather general description and the exact sequence is not described (first_call(), second_call() are too general), I'll design a simple non specific system that makes couple of calls. I'll use pseudo code. Most of the things are non specific as I don't know return types and stuff.
Let's call this command CalculateCommand. This command will use CalculationModel for the calculation. Next let's define a TokenService that will contain the logic for getting the token (API call).
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
}
}
Check these resources for more information on MVP and it's flavors:
https://martinfowler.com/eaaDev/uiArchs.html