A student in my Android Architecture Masterclass course asked a very insightful question about MVC architectural pattern that I use. They suggested that, maybe, it’s possible to simplify the implementation of MVC views by replacing interfaces with abstract classes.
In this post you’ll see a simpler implemention of MVC views and I’ll also discuss the fundamental relationship between interfaces and abstract classes.
MVC View
The most important component of MVC (or any MVx really) is the view. That’s the entity which represents the UI of your application.
In MVC, I chose to implement views as standalone classes which don’t extend any Android-specific class. This idea proved itself very powerful over the years.
Let’s take this simple view as an example:
public class QuestionsListItemViewMvcImpl extends BaseObservableViewMvc<QuestionsListItemViewMvc.Listener> implements QuestionsListItemViewMvc { private final TextView mTxtTitle; private Question mQuestion; public QuestionsListItemViewMvcImpl(LayoutInflater inflater, @Nullable ViewGroup parent) { setRootView(inflater.inflate(R.layout.layout_question_list_item, parent, false)); mTxtTitle = findViewById(R.id.txt_title); getRootView().setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { for (Listener listener : getListeners()) { listener.onQuestionClicked(mQuestion); } } }); } @Override public void bindQuestion(Question question) { mQuestion = question; mTxtTitle.setText(question.getTitle()); } }
This view renders the title of a Question
and captures clicks on itself. Trivial functionality. However, you might be legitimately surprised by the complexity of the inheritance hierarchy of this simple view:
Let me explain the motivation behind each of the elements in this hierarchy.
QuestionsListItemViewMvc
interface:
public interface QuestionsListItemViewMvc extends ObservableViewMvc<QuestionsListItemViewMvc.Listener> { public interface Listener { void onQuestionClicked(Question question); } void bindQuestion(Question question); }
This interfaces abstracts out the implementation details of each specific view. That’s the most important abstraction in MVC because it makes maintainability much easier. For example, try to understand what this implementation of MVC view does. Challenging, right? For contrast, take a look at its interface. Now you should get the general idea about the functionality of that screen in just a minute.
ObservableViewMvc
interface:
public interface ObservableViewMvc<ListenerType> extends ViewMvc { void registerListener(ListenerType listener); void unregisterListener(ListenerType listener); }
This generic interface exists to avoid duplicated declarations of registerListener
and unregisterListener
methods in other view interfaces.
ViewMvc
interface:
public interface ViewMvc { View getRootView(); }
The role of this interface is to avoid repeated declarations of getRootView
method in other view interfaces.
BaseObservableViewMvc
abstract class:
public abstract class BaseObservableViewMvc<ListenerType> extends BaseViewMvc implements ObservableViewMvc<ListenerType> { private Set<ListenerType> mListeners = new HashSet<>(); @Override public final void registerListener(ListenerType listener) { mListeners.add(listener); } @Override public final void unregisterListener(ListenerType listener) { mListeners.remove(listener); } protected final Set<ListenerType> getListeners() { return Collections.unmodifiableSet(mListeners); } }
This generic abstract class provides common implementation of Observer design pattern used by all MVC views.
BaseViewMvc
abstract class:
public abstract class BaseViewMvc implements ViewMvc { private View mRootView; @Override public View getRootView() { return mRootView; } protected void setRootView(View rootView) { mRootView = rootView; } protected <T extends View> T findViewById(int id) { return getRootView().findViewById(id); } protected Context getContext() { return getRootView().getContext(); } protected String getString(@StringRes int id) { return getContext().getString(id); } }
In this abstract class I put various “utility” methods used by other MVC views.
So, each class and interface in this hierarchy has a purpose. However, it doesn’t change the fact that this scheme is very complex. I’ve been using MVC for years and stopped noticing this complexity long ago. For me, it’s just a bit of boilerplate code that I don’t even think about. However, that’s not the case for other developers.
MVC View Simplification Using Abstract Class
Back to the question that one of my students asked last week. Their idea was that, maybe, we can get rid of interfaces by converting the main view interface to an abstract class.
In the example from the previous section, that would amount to replacing QuestionsListItemViewMvc
interface with the following abstract class:
public abstract class QuestionsListItemViewMvc extends BaseObservableViewMvc<QuestionsListItemViewMvc.Listener> { public interface Listener { void onQuestionClicked(Question question); } public abstract void bindQuestion(Question question); }
The functionality of this MVC view is still clearly evident from the definition of this new abstract class, so we didn’t compromise the maintainability. However, following this change, the signature of QuestionsListItemViewMvcImpl
can be simplified to this:
public class QuestionsListItemViewMvcImpl extends QuestionsListItemViewMvc { ... }
As you see, this specific view doesn’t need to implement any interface and there is no generics argument anymore. Therefore, if we employ the same approach with all MVC views in the app, we can get rid of all view-related interfaces altogether!
The resulting inheritance hierarchy will look like this:
Much simpler, right?!
I tested this approach in several applications and it works like magic. The only “limitation” that I found was that you won’t be able to use final methods in abstract classes if you also want to mock them in unit tests. However, that’s not a big deal because in all these years that I had been using MVC, I didn’t benefit from this finalization even once.
All in all, I consider this to be a win.
Interfaces vs Abstract Classes
Now I want to discuss why abstract classes work so well in this scenario.
In general, interfaces are better abstractions than abstract classes. The reason is simple: each class can implement multiple interfaces, but extend only one abstract (or non-abstract) class. Therefore, interfaces allow for a more granular application of so-called Interface Segregation Principle (the “I” in SOLID), and are more versatile when it comes to Dependency Inversion (the “D” in SOLID).
However, in this case, switching from interfaces to abstract classes won’t make my abstractions any worse. Why is that?
The first reason is that abstract classes in this case implement clean separation between public and protected functionality. As a result, their aggregated public API is identical to the one I exposed through interfaces. Therefore, this refactoring didn’t change the public API of my views.
Another reason is that existing MVC views won’t need to extend any other class except for these “base” ones. Therefore, the fact that a class can’t extend more than one parent won’t become a limitation in this case.
Lastly, even if I’ll need to introduce alternative implementations of specific MVC views in the future (e.g. A/B testing), these implementations will also need to extend the same set of base classes. Therefore, I don’t shoot myself in the foot by coupling the API of specific MVC views to specific base classes.
So, there are three very specific reasons which make it possible to use abstract classes instead of interfaces in this specific case.
On a more philosophical note, this discussion reminds me of something that Uncle Bob said in one of his talks. He claimed that Java has interface constructs to begin with only due to its lack of support for multiple inheritance. I don’t remember the exact quote, but I think he basically called interfaces half-assed hacks. I wasn’t quite sure what he meant back when I heard that, but I think I get it now.
Summary
All in all, turned out that I was using non-optimal implementation of MVC views for years.
That’s not a big deal for me personally because I never care about few more lines of boilerplate code, but it might be an important improvement nonetheless. See, when I teach MVC to other developers, view’s inheritance hierarchy is always tricky to explain. Hopefully, this small change will lower the barrier to entry into MVC for others.
On a more personal note, I’m just exceptionally pleased that my students show this level of understanding and critical thinking. I’m very grateful that they invest time to educate me through their questions and criticism. Thank you all, folks!
As always, leave your comments and questions below and click that subscribe button to be notified of new content.
As the student in question, I feel an honor for having helped with this pattern as you can say that I am a “fan” and all my professionally made apps leverage the mvc with extracted observable views.
Have a great day Vasiliy, hope we can further discuss Android in the future!
Konstantinos
Hey Konstantinos,
Glad you wrote a comment because I wasn’t sure if I can give you proper credit by sharing your name in the article. Thanks for taking the time to share your ideas and challenge my assumptions.
Vasiliy
Of course you can, though it is definitely not needed đ
You’re welcome, glad to contribute.
I felt some discomfort with the first hierarchy. QuestionsListItemViewMvcImpl was three times ViewMvc due to use the interfaces I tought “That is not Vasiliy style”.
Excellent article!!! I’ll procede with the change in my current project.