Issue
I've recently started developing an Android app with a model–view–presenter architecture. One issue that keeps coming up that I haven't been able to find a good solution for is passing strings from the presenter to be displayed in the view.
I am trying to keep Android system dependencies out of the presenter layer to make unit testing easier. This is simple when providing a string that comes from a server or some other external source. When I need to display a simple error message that is always the same, I can just have a method like showWhateverError()
, where the view already knows which string resource to use and can handle loading the resource itself. And when I have business logic that determines which string resource to provide the view, I can just reference the string resource ID in the presenter (although that feels wrong too).
The case that I haven't come up with a good solution for is when I need to display a string that sometimes comes from the server and is sometimes a string resource, based on some business logic. Any ideas would be helpful!
Solution
For these situations I have additional helper class which I call a Formatter
. I pass the current state from my Presenter
to the View
which then asks the Formatter
for the appropriate strings based on that state. I think a small example will help:
You have an object which represents the data you wish to set on the View. Let's call it State
for now:
public class State {
private final boolean isServerString;
private final String serverString;
public State(boolean isServerString, String serverString) {
this.isServerString = isServerString;
this.serverString = serverString;
}
public boolean isServerString() {
return isServerString;
}
public String getServerString() {
return serverString;
}
}
In you Presenter
you would create this based on whatever logic you need and pass it to the View
:
public class MessagePresenter {
private void setMessage() {
// logic here
State state = new State(true, "Hello from the server");
view().setMessage(state);
}
}
Then in your Activity/Fragment/ViewGroup you'd have something along the lines of:
public class MyActivity extends Activity implements MessageView {
private MessageFormatter formatter;
private TextView messageTextView;
@Override
public setMessage(State state) {
String message = formatter.getMessage(state);
messageTextView.setText(message);
}
}
As you can see the view asks the Formatter
for the String to display in the TextView. The Formatter would look something like this:
public class MessageFormatter {
private Context context;
public MessageFormatter(Context context) {
this.context = context;
}
public String getMessage(State state) {
return state.isServerString()
? state.getServerString()
: context.getString(R.string.default_message);
}
}
It takes a Context
as a constructor param and, yes it does have a little bit of logic in it. However the heavy lifting logic remains in the Presenter
. Most of the time it should just be a simple boolean check.
The real power of using this method comes into play when you make a Formatter
interface and then use your Presenter
to decide which Formatter
to instantiate. As an example you can then create a HoliidayMessageFormatter
and a DefaultMessageFormatter
which would allow you to give you app a different theme based on a small logic check in the Presenter
.
Answered By - Jahnold
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.