Android Design Patterns
There are just too many reasons not to start thinking about using some sort of Design Pattern when you’re developing Android apps…scratch that when you’re developing any type of app of website. The urge to just start coding will always be there but you might want to wait a little and think about what you’re doing and how you’re going to do it before you crack open Android Studio and start coding.
Android has 4 major patterns, MVC, MVP, MVVM and Clean. Now I’d be lying if I said it was that simple. Different developers have different ideas on what constitutes an MVP implementation. Some developers create hybrid MVC or MVP designs and others use RXAndroid behind the scenes to keep the design pattern simple but also get the benefits of something like a more complex Clean architecture. Contrary to some blogs out there, Classic Android is not an MVC pattern. It’s more of a MV or Model-View pattern with little or no separation between classes.
If you’re new to design patterns you’re probably wondering why bother? You’ve probably seen the blog posts out there and thought nice idea but not worth the effort. If you write code in the Classic Android way then over time you’re very likely to have code that is often called a Big Ball of Mud. From a developer perspective it’s ugly and often difficult to understand what goes where. From a business owners perspective it’s disastrous because as the number of features increases so does the time to implement each and every new feature. So just as you’re hitting critical mass, your IT team are slowing down.
Ideally what we’re all looking for is scalable, maintainable and easy to test code. Sounds simple when you say it like that. Let’s take a little time to think about what that really means. We want scalable code that will allow us to add more features just as quickly as when we started writing the app - you never want to hear someone say it would be quicker to rewrite the app. We want the code to be maintainable - any developer should be able to quickly identify the cause of a defective and quickly fix it with a very limited risk of introducing any other defects. No spaghetti code (see big ball of mud above). We want code that is concise, easy to read and each section of the code does it’s job and nobody else’s job - what is known as separation of concerns. Finally we need to write code that we can unit test. If we have tests that cover most of the code then when we fix any defects and run our tests and if they all pass then we can be sure that we haven’t introduced any other weird side effects. Classic Android, by its very nature makes it much harder to write unit tests. Using the right design pattern with unit tests will make your life easier and let you rest easier at night knowing that someone isn’t killing your codebase.
Over the years I’ve found it’s better to be as objective as possible when you’re talking about code. Subjectivity often leads to confusion and hurt feelings. We want to help developers write code we don’t want to punish them for any supposed inadequacies. A good objective guide is the SOLID framework which we can explain using the following table from Wikipedia.
|S||Single responsibility principle||a class should have only a single responsibility|
|O||Open/Closed principle||open for extension, but closed for modification|
|L||Liskov substitution principle||design by contract|
|I||Interface segregation principle||many interfaces better than one general-purpose|
|D||Dependency inversion principle||depend upon abstractions, not concretions|
SOLID stands for Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation and Dependency Inversion Principles. Single Responsibility means that each class should only have one responsibility. Open/Closed means a class should be open for extension but closed for modification. So a class can be extended all day and all night but you shouldn’t need to “disturb any of its existing clients” while you’re doing it. Liskov Substitution Principle states “if S is a subtype of T, then objects of type T may be replaced with objects of type S”. That’s not that easy to understand so let’s use an example. Say if you have a Car class, then it has to be able to support a Wankel rotary engine from a Mazda RX8 as well as a internal combustion engine from a VW Beetle. Hint there’s no concept of number of pistons in a Wankel engine. If your Car class is going to satisfy the Liskov principle then it better support both types of car. Interface Segregation means have lots of small interfaces than one big one. Finally the Dependency Inversion Principle states that when you’re designing an interaction between two modules one should depend upon abstractions, not concretions, or to put it another way decouple your code, don’t hard code one module to another as one or the other end may change.
From a business perspective deign patterns also makes the code a lot easier to police. If everyone knows the rules of engagement then you can use code reviews, static code analysis and more to make sure everyone is following the rules. Developers like structure. Guide them. Don’t assume it’s going to happen on its own.
We’re going to look at each of these design patterns over the next few weeks. We’ll compare and contrast why you might and might not want to use each of these patterns by referring to the SOLID principles.