2.
Model View Controller Theory
Written by Yun Cheng
In this chapter, you will learn about the Model-View-Controller pattern and be familiarized with the sample app that you will build throughout this book: WeWatch.
The Model-View-Controller pattern
The Model View Controller pattern is a pattern that separates components of a software system, based on responsibilities. In this pattern, three components with distinct responsibilities are defined as follows:
- Model is the data layer. This includes the data objects, database classes, and other business logic, concerned with storing the data, retrieving the data, updating the data, etc.
- View renders the data using the Model in a way suitable for user interface. It is what gets displayed to the user.
- Controller is the brains of the system. It encapsulates the logic of the system, and it controls both the Model and View. The user interacts with the system through this controller.
The general idea is that the Model should not be concerned with how it’s ultimately displayed to the user. On the flip side, a View should not be concerned with the values of the actual data it is displaying, only that it needs to be displayed. The Controller then acts as the glue between these two components, orchestrating how the data should be displayed
Organizing a system this way allows for two advantages: separation of concerns and unit testability. Because components are isolated from each other, each focused on only one responsibility, the system is more flexible and modular. Each component is able to be unit tested on its own and could be swapped out for another version without affecting the other.
For example, the View could be changed out while the underlying data and logic remained the same.
When MVC was originally conceptualized, it was initially applied to desktop and web applications. A typical application of this pattern in the web realm would look like this:
- The user inputs are HTTP requests.
- The Controller handles those requests by updating the data in the Model.
- The View returned to the user is a rendered HTML page, displaying the data.
Applying MVC to Android
Seeing that this pattern worked well in organizing other software systems, developers naturally tried to apply the MVC pattern to the development of Android apps as well when Android was introduced. Here is one way in which the MVC pattern was adapted for Android:
In MVC, the main entry point to the app is through the Controller, so it makes sense to give the role of the Controller to the Android Activity where it can take in user inputs, such as a button click, and respond accordingly. The Model consists of the data objects, which, in Android, are just regular Kotlin data classes, as well as the classes to handle data locally and remotely. The View consists of the layout files in an Android app.
However, there is a problem with this oversimplified adaptation of MVC to Android.
When the Controller updates the View, the View cannot update if it is merely a static .xml layout. Instead, the Activity must almost always contain some view logic, such as showing or hiding views, displaying a progress bar or updating the text on screen, in response to user input. Furthermore, not all layouts are inflated through .xml; what if your Activity dynamically loaded your layouts?
If the Activity must hold references to views and logic for changing them as well as all the logic for its Controller responsibilities, then the Activity effectively serves as both the Controller and the View in this pattern.
Having the Activity act as both the Controller and the View is problematic for two reasons. First, it defeats the goal of MVC, which was to separate responsibilities among three distinct components of a software system. Second, having the Activity as the Controller in MVC poses a problem for unit testing, which you will read more about in the next chapter.
As you will discover, the sample app in this book starts out with precisely those problems described above.
WeWatch MVC code
Like many Android apps out there, WeWatch follows the standard Android architecture, which attempts to follow MVC architecture but falls short due to the nature of the Android framework and the role of the Android Activity. There ends up being a mix of both Controller logic and View logic in the app’s Activities, as you’ll see in the code for this app. It will, however, serve as the starting point for the refactoring you will perform in future chapters to address those issues and improve the app.
Model
The Model for WeWatch consists of the following classes:
- The Movie class itself which you’ll find in the Movie.kt file,
- The classes concerned with the local database are found in LocalDatabase.kt, LocalDataSource.kt, and MovieDao.kt.
- The classes concerned with remote data can be found in RemoteDataSource.kt and TmdbResponse.kt.
Open these files in the project and familiarize yourself with the classes and their workings.
Because the app gets its search results from The Movie Database, the Movie class must contain properties matching those of the movies found in the JSON object returned from the API. If you open Movie.kt, you’ll see that this class contains far more properties than you’ll ever need for this app, such as originalLanguage
and genreIds
; these are present in this class to be compatible with the movie JSON object returned from the API.
The actual object returned from The Movie Database is represented by TmdbResponse.kt, which includes properties such as number of results, and most importantly, the results themselves, which is a List<Movie>
.
Next, the app needs a local database to persist the user’s list of movies. The app uses the Room Persistence Library, one of the Android Architecture Components. The singleton for the local database is created in LocalDatabase.kt. Because Room does not know how to store a List<Int>
, a IntegerListTypeConverter
is needed to perform the conversion from List<Int>
to String
or String
to List<Int>
and is listed as a TypeConverter
at the top of LocalDatabase.kt. The data access object containing the SQL statements for this database is MovieDao.kt. Finally, Activities will interact with this Model component through the LocalDataSource
, which exposes the insert
, delete
and update
functions to the rest of the app.
Networking
The search screen in this app is powered by a call to The Movie Database (www.themoviedb.org) API’s search endpoint. RetrofitClient
and RetrofitInterface
contain all the functions needed to make this network call succeed.
Main Screen
Open MainActivity.kt and MainAdapter.kt and familiarize yourself with the code for displaying the user’s list of movies to watch. MainActivity.kt sets up the views and gets an instance of the LocalDataSource
. Then it gets the list of movies from the LocalDataSource
through an RxJava
Observable
and passes the movies to the MainAdapter
, which displays the movies in the RecyclerView. There is also logic to handle button clicks for adding and deleting a movie.
Add movie screen
Open AddMovieActivity.kt. This class sets up the views and gets an instance of the LocalDataSource
. There are click listeners for the Search button and the add movie button. When the search button is clicked, the SearchActivity
is started. Upon returning from the SearchActivity
, the views get updated, including loading an image of the movie poster. When the add movie button is clicked, the movie is inserted into the local database.
Search movie screen
Open SearchActivity.kt and SearchAdapter.kt. Much of the logic in these classes is similar to those on the main screen. The SearchActivity.kt sets up the views and gets an instance of the RemoteDataSource
. Then it gets the list of search results from the RemoteDataSource
through an RxJava
Observable
and passes the movies to the MainAdapter
, which displays the movies in the RecyclerView. There is also logic for clicking on a movie and returning to the previous screen.
Key points
In this chapter you gained an overview of the Model View Controller pattern as a way of separating components by responsibility in systems with user interfaces. Unfortunately, it’s not easy to apply MVC to Android due to the nature of the Activity class. As you saw in the sample app, when applying MVC to Android, you end up with huge Activity classes, containing both Controller logic and View logic. As you will learn in the next chapter, one of the main drawbacks of this design is lack of unit testability of your codebase.
More specifically:
- Model View Controller is a pattern originally used in desktop and web-based applications.
- MVC divides an app into three components with their own responsibilities.
- The Model consists of the data and business logic.
- The View is responsible for rendering the data in a way that is human readable on a screen.
- The Controller is the brains behind the app and communicates with both the Model and the View. The user interacts with the app via the Controller.
- When applying MVC to Android, the Android Activity ends up serving as both the View and Controller, which is problematic for separation of concerns and unit testing.