Combinitorial Explosion You have lots of code that does almost the same thing.. but with tiny variations in data or behavior. This can be difficult to refactor-- perhaps using generics or an interpreter? - Jeff Atwood via Coding Horror
In this case it is not lots of code, but it is still bugging me. I have a shared problem, that is when trying to connect to an IP, if it fails, I should retry with the next IP.
I have one function which generates a producer for NSQ:
//Since we are in a critical system, we try with each IP until we get a producer
var err error
for i, success := 0, false; i < len(ips) && !success; i++ {
publisher, err = nsq.NewProducer(ips[i], nsq.NewConfig())
if err == nil {
success = true
}
}
The other function that almost shares the same code is one which takes a NSQ consumer and connects it:
var err error
for i, success := 0, false; i < len(ips) && !success; i++ {
err = consumer.ConnectToNSQD(ips[i])
if err == nil {
success = true
}
}
I would like to get rid of this almost repeated code without sacrificing legibility. Ideas?
You have it backwards. Your solution should follow the shape of the problem, not the shape of a particular solution. There's nothing in the solution that's worth refactoring. It's just going to add pointless complexity.
For example,
package main
import "github.com/nsqio/go-nsq"
// NewProducer is nsq.NewProducer with retries of an address list.
func NewProducer(addrs []string, config *nsq.Config) (producer *nsq.Producer, err error) {
if len(addrs) == 0 {
addrs = append(addrs, "")
}
for _, addr := range addrs {
producer, err = nsq.NewProducer(addr, config)
if err == nil {
break
}
}
return producer, err
}
// ConnectToNSQD is nsq.ConnectToNSQD with retries of an address list.
func ConnectToNSQD(c *nsq.Consumer, addrs []string) (err error) {
if len(addrs) == 0 {
addrs = append(addrs, "")
}
for _, addr := range addrs {
err = c.ConnectToNSQD(addr)
if err == nil {
break
}
}
return err
}
func main() {}
Maybe something like this?
var publisher *nsq.Producer
connectToWorkingIP(ips, func(ip string) error {
var err error
publisher, err = nsq.NewProducer(ip, nsq.NewConfig())
return err
})
connectToWorkingIP(ips, func(ip string) error {
return consumer.ConnectToNSQD(ip)
})
func connectToWorkingIP(ips []string, f func(string) error) {
for i, success := 0, false; i < len(ips) && !success; i++ {
err := f(ips[i])
if err == nil {
success = true
}
}
}