Design patterns have a reputation problem on Android. Half the tutorials online are interview flashcards. The other half are disconnected UML diagrams. Neither helps when a code review lands and someone asks why your dialog creation logic is sprawling across three fragments.
The patterns that actually pay rent in an Android codebase are a small set: Singleton, Factory, Observer, and a few others. Used in the right place, they shrink classes, remove conditionals, and make features easier to swap. Used everywhere, they become noise. Here is the working subset, with the reasoning behind each one.
Singleton: the cheap one to get right
A singleton guarantees one instance of a class and gives every caller the same handle. On Android that fits a network client, a database wrapper, a preferences manager, anything you build once and reuse everywhere.
The easy version in Kotlin is the object keyword. Writing object NetworkClient gives you thread-safe lazy init, no private constructor boilerplate, and a single access point. Wrap a Retrofit or OkHttp builder inside, expose a method that takes a URL, and you have a service the whole app shares.
The trap is overusing it. Objects live for the process lifetime, which is fine for stateless clients and dangerous for anything holding a Context, a View, or mutable state. If your singleton owns a reference to an Activity, you have a leak. Keep singletons lean.
Factory: kill the if-else tree
When a feature has several flavors — different dialog types, different parser outputs, different view holders — the temptation is to write a when block and call it a day. That works until the third flavor arrives and the call site is on the other side of the codebase.
A Factory centralises the decision. You define a common interface, write one implementation per type, and let the factory pick. For dialogs, that means a Dialog interface, a DialogCreateChat, a DialogDeleteMessage, a DialogEditMessage, and a DialogFactory with a single create(type) method. The call site asks the factory by name and never knows which concrete class came back.
The win is testability and change locality. Add a new dialog, add a class and one factory branch, and nothing else moves.
Observer: the spine of reactive UI
Observer is the pattern that powers LiveData, StateFlow, RxJava, and most of the modern Android UI stack. One source of truth emits, many subscribers react, the UI stays in sync without manual refresh calls.
You rarely hand-roll Observer any more. You pick a library, subscribe in the view layer, and let the framework do the wiring. The pattern still matters because it explains the contract: the source owns the data, the subscribers own the reaction, and lifecycle handling is the subscriber's job, not the source's.
Strategy, Builder, Adapter: the everyday trio
Strategy swaps an algorithm behind a stable interface, useful for sorting policies, validation rules, and feature flags. Builder assembles a complex object step by step, which fits request objects, dialog configurations, and any DTO with more than four optional fields. Adapter bridges two incompatible interfaces, which is the pattern Retrofit, Room, and most third-party SDKs use to talk to your code.
None of these need a framework. They are conventions. The value is that the next developer opens the file and recognises the shape in five seconds.
Our analysis
Patterns are not free. Every layer of indirection costs a reader's time. The test is simple: does the pattern shrink the call site, or does it just move code around? If the answer is the second, delete the abstraction and write the straight code.
The interview framing has a kernel of truth. Hiring managers want to see that you can name a pattern, draw the diagram, and pick the right one under pressure. The bigger win is reading other people's code. Once you can spot a Factory in a medium-sized diff, you stop getting lost in unfamiliar projects, and you start fixing them.
Compared to the classic GoF catalogue, Android's working set is smaller. You do not need Visitor, Memento, or Mediator in a typical app. You do need Singleton, Factory, Observer, Strategy, Builder, and Adapter, in roughly that order of frequency.
When to use which
| Scenario | Pattern | Why |
|---|---|---|
| One shared network client across app | Singleton | One init, global access |
| Multiple dialog subtypes | Factory | Centralise creation logic |
| UI reacts to data changes | Observer | One source, many subscribers |
| Swappable algorithms or policies | Strategy | Stable interface, varied logic |
| Complex object with many optional parts | Builder | Step-by-step, readable |
| Third-party library needs your types | Adapter | Bridge incompatible interfaces |
A worked example: a dialog feature
Say you are building a chat screen with three dialog flows: create chat, delete message, edit message. Without a Factory, each fragment imports each dialog class and decides which to show. With a Factory, the fragment calls dialogFactory.create("DELETE_MESSAGE") and the factory returns a Dialog. Adding a fourth dialog is one new class and one new branch. The fragment never changes.
Bottom line
Pick patterns by the call site, not by the textbook. Singleton for shared resources, Factory for type selection, Observer for state, Strategy and Builder for complex objects, Adapter for incompatible interfaces. Use them where they shrink code, skip them where they do not, and your review cycle gets shorter within a sprint.
Key takeaways
- Object in Kotlin is the easiest Singleton you will ever write; use it for shared clients.
- A Factory beats a when block as soon as the third variant shows up.
- Observer is the contract behind LiveData and StateFlow, not a class you build by hand.
- Strategy, Builder, and Adapter cover most of the remaining Android-shaped work.
- A pattern earns its place when it shrinks the call site, not when it adds an extra file.
Frequently asked questions
Which Android design pattern should I learn first?
Singleton and Observer. They cover the largest amount of everyday code, and the rest of the patterns are easier to grasp once those two feel natural.
Is the Singleton pattern still relevant in Kotlin?
Yes, mostly as object declarations. Just avoid putting Context, Views, or mutable state inside a singleton, because it will outlive the screen that touched it.
When should I use Factory instead of a simple when expression?
When the branching logic is shared across more than one call site, or when the number of types is likely to grow. A one-off when inside a single function is fine.
Do I need to know all GoF patterns for Android interviews?
No. Singleton, Factory, Observer, Strategy, Builder, and Adapter cover the vast majority of Android interview questions and the vast majority of real Android code.
Can design patterns hurt code quality?
Yes, when applied for their own sake. If the pattern adds a file without shrinking the call site, it is overhead, not architecture.