- Published on
Learning Design Patterns: Abstract Factory Pattern - Code Workshop 101
- Authors

- Name
- Dan Tech
- @dan_0xff
Factory Pattern is a design pattern that helps us create Objects from a single place. However, the Abstract Factory Pattern is what we should aim for. The wonderful uses of Abstract will be dissected in detail in today's article.
Problem Statement with Abstract Factory Pattern
In software development, we will encounter situations where we need to work with related groups of objects.
Imagine that your team needs to develop a feature on a mobile application to display various Components on a screen. These Components are displayed in any order and quantity, and these Components also belong to many different development teams.
The challenge here is to create Component objects flexibly, avoid hard coding, and ensure high compatibility and scalability when business logic changes. That's when the Abstract Factory Pattern comes into play.
Code Without Abstract Factory Pattern
class TitleComponent(val text: String) {
fun createView(parent: ViewGroup): View {
// dump return a view
}
}
class ImageComponent(val imageResId: Int) {
fun createView(parent: ViewGroup): View {
// dump return view
}
}
class TextComponent(val text: String) {
fun createView(parent: ViewGroup): View {
// dump return view
}
}
class ComponentAdapter(private val components: List) :
RecyclerView.Adapter() {
override fun onCreateViewHolder(parent: ViewGroup, position: Int): ViewHolder {
return when(components[position]) {
is Title -> TitleComponent(components[position])
is Image -> ImageComponent(components[position])
is Text -> TextComponent(components[position])
else -> View()
}
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(components[position])
}
override fun getItemCount() = components.size
}
fun main() {
val data = listOf(
TitleComponent("Welcome"),
ImageComponent(R.drawable.image1),
TextComponent("This is some text."),
ImageComponent(R.drawable.image2),
TitleComponent("Another Section"),
TextComponent("More details here.")
)
val parentView = RecyclerView()
val adapter = ComponentAdapter(data)
parentView.adapter = adapter
}
With the above usage, we can see that each time onCreateViewHolder is run, a constructor function is triggered manually. This is not wrong in logic, but when the logic increases and the involvement of many teams in a single source code will inadvertently make our source code confusing and difficult to control each person's responsibilities. The solution is to apply the Abstract Factory Pattern.
Code With Abstract Factory Pattern
interface TextComponentFactory {
fun createTitleComponent(data: Any): ViewHolder
fun createTextComponent(data: Any): ViewHolder
}
inteface ImageComponentFactory {
fun createImageComponent(data: Any): ViewHolder
}
class ComponentAdapter(
private val components: List,
private val textComponentFactory: TextComponentFactory,
private val imageComponentFactory: ImageComponentFactory
) :
RecyclerView.Adapter() {
override fun onCreateViewHolder(parent: ViewGroup, position: Int): ViewHolder {
return when(components[position]) {
is Title -> textComponentFactory.createTitleComponent(components[position])
is Image -> imageComponentFactory.createImageComponent(components[position])
is Text -> textComponentFactory.createTextComponent(components[position])
else -> View()
}
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(components[position])
}
override fun getItemCount() = components.size
}
With the above approach, we have made the Adapter's dependence on Components abstract, ensuring DIP (Dependency Inversion Principle) and also applying a stylized Abstract Factory Pattern. Indeed, all martial arts in the world are closely related to each other. When we master one move, we can easily understand the internal energy of many other schools :)
Lessons From Abstract Factory Pattern
When receiving a source code or starting to develop a feature, think about how many satellite modules and how many other programmers that feature or source code is dependent on. And find every way to make it abstract - abstract the dependencies of the module you are developing. That is the key to applying the Abstract Factory Pattern.
Good luck to you all. Remember to follow @dantech!