- Structural design pattern
- Allow two or more different interfaces to work together
- Bridge between two classes with different interfaces, allowing the two work together seamlessly.
Problems
You want to use some functionality.
There are so many services that have different interfaces.
Various inputs that only match with one of the interfaces.
Real-life Example
We can see Adapter pattern in literally memory card adapter,
we can read different type of cards (miniSD, microSD) and able to read them all using single adapter
Adapter Pattern
Most of the adapter pattern will have 3 components:
- Client - Needs to use adaptee, but incompatible
- Adapter - bridge client and adaptee
- Adaptee - More than 1 Class, needs to be implemented but doesn't share same interfaces or incompatible with each other.
For Example:
We will be building card reader:
- Client - Class MediaReader
- Adapter - Class MediaAdapter
- Adaptee - MicroSDdReader, MiniSDReader, FlashdiskReader
Kotlin Example
// Interface for adapter class implementation and functionality to be used
// Adapter will implement this
interface CardReader {
fun read(cardType: String, fileName: String)
}
interface SpecificMediaReader {
fun read(fileName: String)
}
class MicroSDReader : SpecificMediaReader {
override fun read(fileName: String) {
println("Reading from MicroSD: $fileName")
}
}
class MiniSDReader : SpecificMediaReader {
override fun read(fileName: String) {
println("Reading from MiniSD: $fileName")
}
}
class FlashdiskReader : SpecificMediaReader {
override fun read(fileName: String) {
println("Reading from Flashdisk: $fileName")
}
}
class CardReaderAdapter : CardReaderInterface {
override fun read(cardType: String, fileName: String) {
val reader: SpecificMediaReader? = when (cardType.lowercase()) {
"microsd" -> MicroSDReader()
"minisd" -> MiniSDReader()
"flashdisk" -> FlashdiskReader()
else -> null
}
if (reader != null) {
reader.read(fileName)
} else {
println("Unsupported card type: $cardType")
}
}
}
// Usage Example
fun main() {
val cardReader = CardReaderAdapter()
cardReader.read("MicroSD", "data.txt")
cardReader.read("MiniSD", "photo.jpg")
cardReader.read("Flashdisk", "video.mp4")
cardReader.read("BluRay", "movie.mkv") // Unsupported
}
Common Uses in Android Practices
- RecyclerView Adapters: Convert data into ViewHolder items for efficient display in a RecyclerView.
- ListView Adapters: Adapt data for ListView and GridView components.
- ViewPager Adapters: Adapt pages for ViewPager to display a series of fragments or views.
Here raw data will be translated to what RecylerView(RV) Understand which is UI elements, so that RV can render
// Data class representing an item
data class User(val name: String, val age: Int)
// ViewHolder class
class UserViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val nameTextView: TextView = view.findViewById(R.id.nameTextView)
val ageTextView: TextView = view.findViewById(R.id.ageTextView)
}
// Adapter class
class UserAdapter(private val userList: List<User>) : RecyclerView.Adapter<UserViewHolder>() {
// Specify layout
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_user, parent, false)
return UserViewHolder(view)
}
// Translating data to each view will be easier, can easily be customized here
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
val user = userList[position]
holder.nameTextView.text = user.name
holder.ageTextView.text = user.age.toString()
}
override fun getItemCount(): Int {
return userList.size
}
}
Pros
- Allows incompatible interfaces/class to work together
- Save time and effort, eliminating needs to modify existing code
- Promotes reusability
- Adapter handles translation logic, and other can focus on their own functionality
- Can add new adaptee and only touch the adapter layer
- Create ways to works with legacy code to match our needs
Cons
- Adding complexity, translating each classes to fit to the interfaces
- Overhead, since it needs to be instantiated and maintained
- More layers
- Adapter will be rigid because use hardcoded branching