There're 2 entities in a Go program: country states and years. A program calculates some data by receiving input from a user. The list of country states is constant, whereas years, as the time goes on, of course not.
I want to structure my program in such a way that it'll be flexible from the point of view of adding new year without changing the existing code. How would I do it? I need an architecture pattern for such a case, that is.
I want to avoid something like this:
func CalculateData(cs, year) -> ReturnDataStructure {
if year == 2000 {
return calculateDataFor2000(cs)
} else if year == 2001 {
return calculateDataFor2001(cs)
} else if year == 2002 {
return calculateDataFor2002(cs)
} else //and so on
// as years go by, add a new year...
} else if year == 2052 {
return calculateDataFor2052(cs)
}
//....
}
Instead I want to be able to add only: I'd add an implementation, perhaps in a separate file, without having to touch the existing code, as the years go by.
That is, the function CalculateData(...)
should become this flexible, or extensible, for it work properly and it shouldn't know how many years there are. The years must not be hard-coded.
Each new implementation must be compiled statically.
How would one do it?
CodePudding user response:
If you want to write Go code to do the calculations, it seems you're stuck with compiling it in. There's no Go equivalent to the opening of a shared object file. See Load package dynamically.
If I understand your question, your problem is that you don't know exactly what math will need to be performed for each year, so you don't want to hard code the math into your application. You don't need a dynamic library to do that. You can either build your own expression evaluator (see "The Go Programming Language" 7.9), or use an embeddable language such as Lua (https://pkg.go.dev/github.com/Shopify/go-lua). Either way, you can move the math "out" of the Go code and associate it with its year in a file. Each year you can write the math for that year, and then the Go won't have to change.
CodePudding user response:
I suggest to use a package level variable of type map[string]func(...)
so that for each year you can set an handler.
If you need to organize those functions into separate packages, you can consider adding a public Register function and let them register the handler at initialization during runtime execution.
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello, playground")
}
var computeYears = map[string]func(cs string) interface{}{
"2000": calculateDataFor2000,
"2001": calculateDataFor2001,
"2002": func(cs string) interface{} { return nil },
}
func CalculateData(cs, year string) interface{} {
x, ok := computeYears["year"]
if ok {
return x(cs)
}
/*
if year == 2000 {
return calculateDataFor2000(cs)
} else if year == 2001 {
return calculateDataFor2001(cs)
} else if year == 2002 {
return calculateDataFor2002(cs)
} else //and so on
// as years go by, add a new year...
} else if year == 2052 {
return calculateDataFor2052(cs)
}
*/
panic("no such implmentation for the year " year)
return nil
}
func calculateDataFor2000(cs string) interface{} {
return nil
}
func calculateDataFor2001(cs string) interface{} {
return nil
}
// In the case you need to delcare thse function in separate packages,
// use a public function t let them regser at init time.
func Register(year string, handler func(cs string) interface{}) {
computeYears[year] = handler
}
func init() {
fmt.Println("init time")
Register("2003", calculateDataFor2000)
}