Android Design Patterns
What Does Design Pattern Mean?
A design pattern is a repeatable solution to a software engineering problem. Unlike most program-specific solutions, design patterns are used in many programs. Design patterns are not considered finished products; rather, they are templates that can be applied to multiple situations and can be improved over time, making a very robust software engineering tool. Because development speed is increased when using a proven prototype, developers using design pattern templates can improve coding efficiency and final product readability.
Pattern #1 ???? Singleton
A singleton is a class that allows only a single instance of itself to be created and gives access to that created instance. It contains static variables that can accommodate unique and private instances of itself. It is used in scenarios when a user wants to restrict the instantiation of a class to only one object. This is helpful usually when a single object is required to coordinate actions across a system.
Creational Patterns
Properties of Singleton Class
- Only one instance
- Globally accessible
Rules for making a class Singleton
The following rules are followed to make a Singleton class:
- A private constructor
- A static reference of its class
- One static method
- Globally accessible object reference
- Consistency across multiple threads
Singleton Example
Following is the example of Singleton class in Kotlin:
Object Singleton { init { println("Hello Singleton") }}
Pattern #2 ???? Factory
As the name suggests, Factory takes care of all the object creational logic. In this pattern, a factory class controls which object to instantiate. Factory pattern comes in handy when dealing with many common objects. You can use it where you might not want to specify a concrete class.
Take a look at the code below for a better understanding:
interface Currency { fun symbol(): String fun code(): String } enum class Country { UnitedState, Spain } class USDollar : Currency { override fun symbol(): String { return "$" } override fun code(): String { return "USD" } } class Euro : Currency { override fun symbol(): String { return "€" } override fun code(): String { return "EUR" } } object CurrencyFactory { fun currency(country: Country): Currency { return when (country) { Country.UnitedState -> { USDollar() } Country.Spain -> { Euro() } } } }
Pattern #3 ???? Builder
Builder pattern aims to “Separate the construction of a complex object from its representation so that the same construction process can create different representations.” It is used to construct a complex object step by step and the final step will return the object.
Creational Patterns
Rules for making a Builder class
The following rules are followed to make a Builder class:
- A private constructor
- An inner class usually called Builder
- function for each field to set the field value return
- function build return instance of the Main class
Following is the example of Builder class in Kotlin:
class Hamburger private constructor( val cheese: Boolean, val beef: Boolean, val onions: Boolean ) { class Builder { private var cheese: Boolean = true private var beef: Boolean = true private var onions: Boolean = true fun cheese(value: Boolean) = apply { cheese = value } fun beef(value: Boolean) = apply { beef = value } fun onions(value: Boolean) = apply { onions = value } fun build() = Hamburger(cheese, beef, onions) } }
Pattern #4 ???? Facade
The Facade pattern provides a higher-level interface that makes a set of other interfaces easier to use. The following diagram illustrates this idea in more.
interface BooksApi { @GET("books") fun listBooks(): Call> }
Square’s Retrofit is an open-source Android library that helps you implement the Facade pattern. You create an interface to provide API data to client.
Structural Patterns
Pattern #5 ???? Dependency Injection
Dependency injection is like moving into a furnished apartment. Everything you need is already there. You don’t have to wait for furniture delivery or follow pages of IKEA instructions to put it together.
In software terms, dependency injection has you provide any required objects to instantiate a new object. This new object doesn’t need to construct or customize the objects themselves.
In Android, you might find you need to access the same complex objects from various points in your app, such as a network client, image loader or SharedPreferences
for local storage. You can inject these objects into your activities and fragments and access them right away.
Here’s an example. Without dependency injection, representing a Car
that creates its own Engine
dependency in code looks like this:
class Car { private val engine = Engine() fun start() { engine.start() } } fun main(args: Array) { val car = Car() car.start() }
What does the code look like with dependency injection? Instead of each instance of Car
constructing its own Engine
object on initialization, it receives an Engine
object as a parameter in its constructor:
class Car(private val engine: Engine) { fun start() { engine.start() } } fun main(args: Array) { val engine = Engine() val car = Car(engine) car.start() }
Pattern #6 ???? Adapter
The adapter pattern works as a bridge between two incompatible interfaces.
This pattern involves a single class that is responsible to join functionalities of independent or incompatible interfaces. A real-life example could be a case of a card reader which acts as an adapter between a memory card and a laptop. You plug in the memory card into a card reader and the card reader into the laptop so that the memory card can be read via the laptop.