We have 2 packages that implement the same function signatures. In my main I need to switch out the package being used based on a condition:
import (
packageA "deployment/sdk/packageA"
packageB "deployment/sdk/packageB"
)
someCondition := true
var packageToUse ?
if someCondition {
packageToUse = packageA
} else {
packageToUse = packageB
}
packageToUse.DoSomething()
Of course, this doesn't compile. I don't know what type to use for packageToUse. This approach is probably an anti-pattern. I'm just not sure the approach I should be taking.
any suggestions?
The error I get is use of package without selector
This isn't quite what you asked, but this is how I would accomplish the goal if I were you:
// I assumed both package functions have the same signature, so you can use a type
// TODO: Actual function signature you want to use here
type myFn func() error
var fnToUse myFn
if someCondition {
fnToUse = packageA.Func1
} else {
fnToUse = packageB.Func2
}
fnToUse()
Following up a bit on comment about using an interface
I would step back a bit and see if this can be better solved using an interface that you provide the concrete class for in the condition statement.
package packageA
type Thing struct {
}
func (t Thing) DoSomething() {
}
--
package packageB
type Thing struct {
}
func (t Thing) DoSomething() {
}
--
package main
import (
"packageA"
"packageB"
)
type PackageUser interface {
DoSomething()
}
func main() {
var pu PackageUser
if (condition) {
pu := packageA.Thing{}
} else {
pu := packageB.Thing{}
}
pu.DoSomething()
}
It is very much a pattern - aside from the dynamic import. It is called „Interfaces“.
Given an interface definition like
type Stringer interface{
String() string
}
and the type Foo implementing said interface
type foo int
func(f foo)String() string{
return fmt.Sprintf(”%d”,f)
}
as well as Bar implementing said interface
type bar string
func (b bar) String()string {
return string(b)
}
one can actually use both as a parameter for a function baz
func baz(s Stringer){
fmt.Println(s.String())
}
Note that unlike other languages, you do not have to declare the interfaces a type implements - as long as a type happens to factually implement an interface, the go compiler is ok with it. Run on Playground
So, with regards to your question of package imports: Unless you have ginormous dependency trees to import, it really is not worth the hassle.
Say we talk of an application using either BBolt or etcd and for good measure some MongoDB. The resulting size to include all of them, while relatively speaking is quite astonishing, is negligible. I compiled a go program with imports of bbolt, bbolt & etcd and bbolt, etcd & mongodb. These are the results in bytes.
11734884 size_all
2455544 size_bolt
10307700 size_boltetcd
We are talking of a few megabytes in file size. So say you use either Bolt or etcd or MongoDB, do you really want to jump through all the hoops and loops required to get dynamic imports done properly? I for my part would neither want to do it nor would I want Go to provide such a feature.
"Premature optimization is the root of all evil (or at least most of it) in programming."
– Donald Knuth