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 thebind
command to the correctViewHolder
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?”
Leave a Reply