A bit about your code
As per discussion in comments, I would like to share some experience.
I do not see nothing bad in your solution, but there are few options to improve it, depends what you want to do.
Your code looks like classic Factory. The Factory
is a pattern, that create object of a single family, based on some input parameters.
In Golang this is commonly used in simpler way as a Factory Method
, sometimes called Factory function
.
Example:
type Vehicle interface {};
type Car struct {}
func NewCar() Vehicle {
return &Car{}
}
But you can easily expand it to do something like you:
package main
import (
"fmt"
"strings"
)
type Vehicle interface {}
type Car struct {}
type Bike struct {}
type Motorbike struct {}
// NewDrivingLicenseCar returns a car for a user, to perform
// the driving license exam.
func NewDrivingLicenseCar(drivingLicense string) (Vehicle, error) {
switch strings.ToLower(drivingLicense) {
case "car":
return &Car{}, nil
case "motorbike":
return &Motorbike{}, nil
case "bike":
return &Bike{}, nil
default:
return nil, fmt.Errorf("Sorry, We are not allowed to make exam for your type of car: \"%s\"", drivingLicense)
}
}
func main() {
fmt.Println(NewDrivingLicenseCar("Car"))
fmt.Println(NewDrivingLicenseCar("Tank"))
}
Above code produces output:
&{} <nil>
<nil> Sorry, We are not allowed to make exam for your type of car: "Tank"
So probably you can improve your code by:
- Closing into a single function, that takes a
string
and produces the Response object
- Adding some validation and the error handling
- Giving it some reasonable name.
There are few related patterns to the Factory, which can replace this pattern:
- Chain of responsibility
- Dispatcher
- Visitor
- Dependency injection
Reflection?
There is also comment from @icza about Reflection. I agree with him, this is used commonly, and We cannot avoid the reflection in our code, because sometimes things are so dynamic.
But in your scenario it is bad solution because:
- You lose compile-time type checking
- You have to modify code when you are adding new type, so why not to add new line in this Factory function?
- You make your code slower(see references), it adds 50%-100% lose of performance.
- You make your code so unreadable and complex
- You have to add a much more error handling to cover not trivial errors from reflection.
Of course, you can add a lot of tests to cover a huge number of scenarios. You can support TypeA
, TypeB
, TypeC
in your code and you can cover it with tests, but in production code sometime you can pass TypeXYZ
and you will get runtime error if you do not catch it.
Conclusion
There is nothing bad with your switch/case
scenario, probably this is the most readable and the easiest way to do what you want to do.
Reference