Android – Design patterns

How many are there?

“Is there anywhere in this project where I’ll have to change the same thing in multiple places?”
A project that’s as reusable, readable, and recognizable as possible.

Creational patterns

How you create classes, objects

  • Builder
  • Dependency Injection
  • Singleton

Structural patterns

How you compose/ arrange classes and objects (e.g. Composite, Facade, Adapter)

  • Adapter
  • Facade

Behavioral patterns

How you coordinate object interactions, communicate between objects and classes (Command, Observer, Strategy, etc.)

  • Command
  • Observer
  • Model View Controller
  • Model View ViewModel
  • Clean Architecture

Creational patterns

“When I need a particular complex object, how do I get one?”

The answer is not “copy and paste the same code every time you need an instance of this object.”

Instead, creational patterns make object creation simple and easily repeatable.

Builder

  • Simplifies object creation in very clean and readable way
  • Helpful when we have some model classes with many parameters
  • We can make some of them optional or required, and we don’t force the user to use specific order (as in the constructor)
  • The most common use is in AlertDialog.Builder() class:
new AlertDialog.Builder(this)
        .setTitle("Design Patterns")
        .setMessage("Builder is awesome")
        .create();

Dependency Injection

  • Dependency injection has you provide any objects required when you instantiate a new object; new object doesn’t need to construct or customize the objects itself
  • In Android, you might find you need to access the same complex objects from various points in your app, such as a network client, an image loader, or SharedPreferences for local storage. You can inject these objects into your activities and fragments and access them right away.

Singleton

  • The Singleton Pattern specifies that only a single instance of a class should exist with a global point of access
object ExampleSingleton {
  fun exampleMethod() {
    // ...
  }
}

Structural Patterns

“So when I open up this class, how will I remember what’s it’s doing and how it’s put together?”

Adapter

This pattern lets two incompatible classes work together by converting the interface of a class into another interface the client expects

For example,

  • RecyclerView is the same basic object across all Android apps
  • “Product”, “User”, “Tribble” is business logic
  • RecyclerView” doesn’t know what “Product”, “User” is. Instead, it’s the adapter’s job to handle the data and send the bind command to the correct ViewHolder

class TribbleAdapter(private val tribbles: List<Tribble>) : RecyclerView.Adapter<TribbleViewHolder>() {
  override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): TribbleViewHolder {
    val inflater = LayoutInflater.from(viewGroup.context)
    val view = inflater.inflate(R.layout.row_tribble, viewGroup, false)
    return TribbleViewHolder(view)
  }

  override fun onBindViewHolder(viewHolder: TribbleViewHolder, i: Int) {
    viewHolder.bind(tribbles[i])
  }

  override fun getItemCount() = tribbles.size
}

Facade

  • If your Activity needs a list of books, it should be able to ask a single object for that list without understanding the inner workings of your local storage, cache, and API client
  • Beyond keeping your Activities and Fragments code clean and concise
  • This makes any required changes to the API implementation without any impact on the Activity

Behavioral Patterns

“So… how do I tell which class is responsible for what?”

Command

Observer

Model – View – Presenter

Be the first to comment

Leave a Reply

Your email address will not be published.


*