Factory pattern
What is a design pattern? Where does it come from? To where does it go? And why should I care?
I’d say the main reason I got into studying design patterns is that I hate wasting time and effort. Design patterns in the end are just generalized solutions on how to do something in a certain way that worked for a lot of different situations and might be useful to you. Which is why there are so many different patterns. You should pick the one that makes sense to you, because there isn’t really a design pattern that is always the best in all situations (or maybe there is, but I just haven’t invented it yet).
What about factory pattern?
The factory pattern is there to solve some problems related to object creation. When writing code we try to make it as reusable as possible because many times it ends up being reused. The factory pattern helps us with that. In a situation where you need to create an object and do some operation with it, you can just go ahead and do that. But imagine that in the future you know that this part of the code might be reused to create another type of object and do the same operation with it. Then you’d have to change your code. But if you used the factory pattern, no need to change anything.
Okay, I was trying to avoid using an example, but it makes the explanation way easier. Let’s imagine we are running a zoo. In our zoo, we have 3 types of animals: dogs, cats and zebras. Why don’t we have more animals? What kind of zoo is this. We are writing a method that receives as input the type of the animal and returns as output the average weight of it. One not so good way to do it is to have an “if” condition where we check whether the input is a cat, a dog or a zebra, and then we instantiate it and return its weight.
A better way to do it is to use the factory pattern. In our example, we would need 2 abstractions: one to represent an animal and one to represent the factory that will create new animals. In our method, we would make a call to the factory, passing the type of animal we want and then call the method in the Animal class that returns the weight.
// Step 1: Define an interface or abstract class for the animals
interface Animal {
fun getWeight(): Double
}
// Step 2: Implement concrete classes for specific animals
class Dog : Animal {
override fun getWeight(): Double = 30.0
}
class Cat : Animal {
override fun getWeight(): Double = 10.0
}
class Zebra : Animal {
override fun getWeight(): Double = 400.0
}
// Step 3: Create the Factory to instantiate animals
object AnimalFactory {
fun createAnimal(type: String): Animal {
return when (type.lowercase()) {
"dog" -> Dog()
"cat" -> Cat()
"zebra" -> Zebra()
else -> throw IllegalArgumentException("Unknown animal type: $type")
}
}
}
Is the abstract factory pattern the same thing?
The abstract factory pattern is different from the factory pattern, but having the same concepts in mind can be helpful to understand it. The abstract factory pattern also deals with abstractions, with the difference that the abstract factory will instantiate a group of related objects. In our initial example, the pattern factory was instantiating animals, like cat and dog. The abstract factory would instantiate related animals, like mammals, which would give us cats and dogs or birds, which would give us ducks or crows. So, instead of having an “Animal Factory” like in the factory pattern, we would have an “Mammals factory” or “Birds factory”.
// Step 1: Define a broader interface for factory groups
interface AnimalFactory {
fun createAnimal(): Animal
}
// Step 2: Implement specific factories for related groups of animals
class MammalsFactory : AnimalFactory {
override fun createAnimal(): Animal {
return Dog() // Could return any mammal, e.g., Cat()
}
}
class BirdsFactory : AnimalFactory {
override fun createAnimal(): Animal {
return Duck() // Could return any bird, e.g., Crow()
}
}
Should I always use the factory pattern?
As with all design patterns, you should use them only when it makes sense. There are some limitations to the factory pattern, like the amount of new code you need to introduce for the abstractions. Eventually, you might end up evolving into the abstract factory pattern. Always keep in mind the YAGNI principle ("You Aren't Gonna Need It") to avoid overengineering. In any case, you should keep in mind that the factory pattern is a great tool and can be used in a variety of scenarios to provide you with a code that will most likely evolve better than if you didn’t use it.