Design patterns are proven solutions to common problems in software design. They help structure code, improve reusability, and maintain consistency. Below, we explore various design patterns and their applications in Android development.
Join our upcoming 6 Weeks Android Mentorship Program
🏗️ Singleton Pattern
This pattern ensures a single instance of a class. Common use cases in Android include SharedPreferences
, Retrofit
, and Room database instances.
Example:
object RetrofitInstance {
val retrofit: Retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.build()
}
👷 Builder Pattern
The Builder Pattern helps construct complex objects step by step. In Android, AlertDialog.Builder
or Notification.Builder
are great examples.
Example:
val alertDialog = AlertDialog.Builder(this)
.setTitle("Title")
.setMessage("Message")
.setPositiveButton("OK", null)
.create()
alertDialog.show()
📢 Observer Pattern
The Observer Pattern is used to notify observers of changes. In Android, LiveData
is commonly used.
Example:
viewModel.liveData.observe(this) { data ->
textView.text = data
}
📊 Repository Pattern
This pattern abstracts data sources and provides a clean API. It’s widely used with Room and Retrofit.
Example:
class Repository(private val api: ApiService, private val dao: Dao) {
fun fetchData() = api.getData()
fun saveData(data: List<Data>) = dao.insertAll(data)
}
📡 Repository + UseCase Pattern
Combine Repository and UseCase patterns for better separation of concerns.
Example:
class GetUserUseCase(private val repository: UserRepository) {
fun execute() = repository.getUser()
}
🏭 Factory Pattern
The Factory Pattern is used to create objects without specifying the exact class. In Android, FragmentFactory
can be used to dynamically create fragments.
Example:
class CustomFragmentFactory : FragmentFactory() {
override fun instantiate(classLoader: ClassLoader, className: String): Fragment {
return when (className) {
MyFragment::class.java.name -> MyFragment(arguments)
else -> super.instantiate(classLoader, className)
}
}
}
🧳 Prototype Pattern
The Prototype Pattern involves cloning objects to avoid expensive object creation. In Android, you can use this pattern for UI elements.
Example:
val originalView = TextView(context)
val clonedView = originalView.copy()
🧩 Adapter Pattern
This pattern converts one interface into another. The RecyclerView.Adapter
is a common example in Android.
Example:
class MyAdapter(private val data: List<String>) : RecyclerView.Adapter<MyViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_view, parent, false)
return MyViewHolder(view)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.bind(data[position])
}
override fun getItemCount() = data.size
}
🏰 Facade Pattern
The Facade Pattern provides a simplified interface to a larger, more complex system. MediaPlayer
in Android is a great example.
Example:
val mediaPlayer = MediaPlayer().apply {
setDataSource("audio_file.mp3")
prepare()
start()
}
🛠️ Decorator Pattern
This pattern dynamically adds functionality to an object. Wrapping InputStream
in BufferedInputStream
is an example.
Example:
val inputStream = FileInputStream(file)
val bufferedStream = BufferedInputStream(inputStream)
🌉 Bridge Pattern
The Bridge Pattern decouples abstraction from implementation. In Android, it can be seen in custom views.
Example:
class CustomView(context: Context) : View(context) {
override fun onDraw(canvas: Canvas) {
// Custom drawing logic
}
}
🏗️ Composite Pattern
This pattern composes objects into tree structures. The ViewGroup
and View
hierarchy in Android are examples.
Example:
val linearLayout = LinearLayout(context).apply {
orientation = LinearLayout.VERTICAL
addView(TextView(context))
addView(Button(context))
}
👥 Strategy Pattern
This pattern defines a family of algorithms and lets you choose one at runtime. In Android, it is used for handling different UI states.
Example:
val strategy = if (isTablet) TabletLayoutStrategy() else PhoneLayoutStrategy()
strategy.applyLayout()
💡 Command Pattern
The Command Pattern encapsulates user actions as objects. Button clicks in Android can follow this pattern.
Example:
val command = ClickCommand(Button(context))
command.execute()
🧲 Memento Pattern
The Memento Pattern saves an object’s state. SavedStateHandle
in ViewModel is an example.
Example:
class MyViewModel(private val state: SavedStateHandle) : ViewModel() {
val data = state.getLiveData("key")
}
🔄 State Pattern
The State Pattern manages state-dependent behavior. In Android, it can be used for media player states.
Example:
class MediaPlayerState {
fun play() { /* ... */ }
fun pause() { /* ... */ }
}
💻 Service Locator Pattern
Simplify dependency lookup for services like LocationManager
or NotificationManager
.
Example:
val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
Note: You don’t have to memorize all these patterns by heart. Focus on understanding the core principles and applying them when needed in your projects. It’s a journey, not a race!
Join our upcoming 6 Weeks Android Mentorship Program