Separated presentation

Chuck Greb
Android Testing
Published in
4 min readSep 2, 2017

Central to the concept of clean architecture are layers. Using separate layers for view, logic, and data improves code flexibility and testability.

Let’s look at a simple diagram for the Model-View-Presenter (MVP) architecture.

This diagram however does not tell the whole story. For optimal decoupling the domain logic should be fully decoupled from the presentation logic.

The presenter should only care about presentation logic: validating user input and formatting data for display. The domain logic associated with the data models should live in the same layer as the data itself.

Clean Calc

To illustrate this point, let’s look at a simplified example of a finance calculator. Our simple calculator only supports addition, accepting two user entered values and then displaying the sum.

Since finance types are often concerned with the bottom line, our app also color codes the sum. Positive sums are green and negative sums are red.

Positive sums are displayed in green
Negative sums are displayed in red

Finding the sum

Finding the sum is a straightforward operation that allows for clean separation of domain logic and presentation.

MainActivity.kt

class MainActivity : AppCompatActivity(), MainController {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)

val presenter = MainPresenter(this)
add_button.setOnClickListener {
presenter.onAddButtonClick(
input_text_1.text.toString(),
input_text_2.text.toString())
}
}
}

In the view layer, our Activity registers for add button click events. The only thing the click listener does is extract the raw values and pass them to the presenter as a String.

MainPresenter.kt

fun onAddButtonClick(input_1: String, input_2: String) {
val calculator = Calculator()
val result = calculator.add(input_1.toInt(), input_2.toInt())
controller.setResultText(result.toString())
}

The presenter is then responsible for unwrapping and validating user input. Rather than performing any calculations (domain logic) in the presenter, it then creates an instance of the Calculator class (domain model) to perform the operation.

Once obtaining the final result value, it formats the value for display by converting from an Int to a String.

Calculator.kt

fun add(addend1: Int, addend2: Int): Int {
return addend1 + addend2
}

Coloring the value

The logic to color code the final value is slightly more complex since it involves accessing Android color resources to format the final value.

MainPresenter.kt

fun onAddButtonClick(input_1: String, input_2: String) {
val calculator = Calculator()
val result = calculator.add(input_1.toInt(), input_2.toInt())
controller.setResultText(result.toString())

val category = calculator.getResultCategory(result)
val color = if (category == Category.HIGH)
Color.GREEN else Color.RED
controller
.setResultColor(color)
}

Calculator.kt

fun getResultCategory(result: Int): Category {
return if (result < 0) Category.LOW else Category.HIGH
}

After calculating the sum, the presenter then calls a new method on the Calculator class to get the Category of the result. But why Category and not Color?

Color is inherently associated with the presentation. If we want to change how high and low values are displayed we would only need to make a change in the presentation layer and not the underlying domain logic.

It is then the responsibility of the presenter to decide how high and low values are displayed, which are green and red respectively.

MainActivity.kt

override fun setResultText(text: String) {
result_text.text = text
}

override fun setResultColor(color: Color) {
val colorInt = if (color == Color.GREEN)
getColor(R.color.green) else getColor(R.color.red)
result_text.setTextColor(colorInt)
}

There is one catch. Since we want to keep Android classes in the view layer only, we cannot rely on calling Resources#getColor(id) in the presenter.

This ensures the presenter can be tested with pure JUnit. And so we rely on the view layer the fetch the actual color value to set on the TextView.

Conculsion

Separated presentation is a classic software design principle we can use to ensure our Android applications are more testable and maintainable. Testable since we can test UI, presentation, and domain logic decoupled and with the appropriate tools. Maintainable since a change to any individual layer should not require a change in the other layers of the architecture.

This post is part of a series on Android clean architecture that explores classic software design principles and patterns, and how they can be applied to modern Android development, with the goal of building applications that are robust, maintainable, and testable.

If you found this article helpful, please give it some applause 👏 to help others find it. Feel free to leave a comment below.

--

--

Chuck Greb
Android Testing

Mission-driven engineering leader. Community organizer. Digital minimalist.