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?
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.
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)
}
https://play.golang.org/p/zI8GCCPj1ew
Related
I'm trying to extend fyne widget to have a simple clickable content with background.
I searched fyne widgets to find an example that could be used as a starter and found something similar in List/ListItem.
I basically copied the list item code and adapted it a little bit.
It does look similar to the simpler example on fyne documentation.
But for some unknown reason go gives me an error and I don't know what the cause is or how I can fix it:
custom_widget/simple_card.go:80:24: c.card.super undefined (type *SimpleCard has no field or method super)
Here is the code of the module (custom_widget/simple_card.go):
package custom_widget
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"log"
)
// Declare conformity with interfaces.
var _ fyne.Widget = (*SimpleCard)(nil)
var _ fyne.Tappable = (*SimpleCard)(nil)
type SimpleCard struct {
widget.BaseWidget
onTapped func()
background *canvas.Rectangle
content fyne.CanvasObject
selected bool
}
func NewSimpleCard(content fyne.CanvasObject, tapped func()) *SimpleCard {
card := &SimpleCard{onTapped: tapped, content: content}
card.ExtendBaseWidget(card)
return card
}
// CreateRenderer is a private method to Fyne which links this custom_widget to its renderer.
func (c *SimpleCard) CreateRenderer() fyne.WidgetRenderer {
c.ExtendBaseWidget(c)
c.background = canvas.NewRectangle(theme.SelectionColor())
c.background.Hide()
objects := []fyne.CanvasObject{c.background, c.content}
// NewBaseRenderer and BaseRenderer are copied from
// https://github.com/fyne-io/fyne/blob/master/internal/widget/base_renderer.go
// because the functionality is marked internal in fyne !?
return &SimpleCardRenderer{NewBaseRenderer(objects), c}
}
func (c *SimpleCard) Tapped(_ *fyne.PointEvent) {
log.Println("I have been tapped")
if c.onTapped != nil {
c.selected = true
c.Refresh()
c.onTapped()
}
}
// Declare conformity with the WidgetRenderer interface.
var _ fyne.WidgetRenderer = (*SimpleCardRenderer)(nil)
type SimpleCardRenderer struct {
BaseRenderer
card *SimpleCard
}
// MinSize calculates the minimum size of a SimpleCardRenderer.
// This is based on the size of the status indicator and the size of the child object.
func (c *SimpleCardRenderer) MinSize() fyne.Size {
return c.card.content.MinSize()
}
// Layout the components of the SimpleCardRenderer custom_widget.
func (c *SimpleCardRenderer) Layout(size fyne.Size) {
c.card.background.Resize(size)
c.card.content.Resize(size)
}
func (c *SimpleCardRenderer) Refresh() {
if c.card.selected {
c.card.background.FillColor = theme.SelectionColor()
c.card.background.Show()
} else {
c.card.background.Hide()
}
c.card.background.Refresh()
canvas.Refresh(c.card.super()) // compiler error !
}
Remove all of the renderer type you created and in the CreateRenderer just return widget.NewSimpleRenderer(container .NewMax(c.background, c.content)). It is simpler than you think.
Copying code out of the main widgets is often not the best way as we have shortcuts and/or must support more functionality than your own widgets.
I've been reading a book about Golang and it's common use global vars to expose the result of private functions. Why not to expose public functions? here's an exemple.
I am comming from java and this is tricky for me. What are advantages of using global vars like these?
EDIT: I notice that without parenthesis I am not calling the function. It's just a pointer. Is it a good pratice or does have some advantages?
package config
import "time"
var (
GetHTTPServerAddress = getHTTPServerAddress
GetHTTPReadTimeout = getHTTPReadTimeout
GetHTTPWriteTimeout = getHTTPWriteTimeout
)
func getHTTPServerAddress() string {
return getConfigString("http.server_address")
}
func getHTTPReadTimeout() time.Duration {
return getConfigDuration("http.read_timeout")
}
func getHTTPWriteTimeout() time.Duration {
return getConfigDuration("http.write_timeout")
}
I've been reading a book about Golang and it's common use global vars to expose the result of private functions.
To expose the result of private functions, not the functions themselves; your example is a little off. Without the parens you're exposing the function itself.
package main
import (
"fmt"
)
var(
FooItself = foo // (func() string)(0x108b720)
FooResult = foo() // "bar"
)
func main() {
fmt.Printf("%#v\n", FooItself)
fmt.Printf("%#v\n", FooResult)
}
func foo() string {
return "bar"
}
Try it.
Your example would look more like this.
package config
var (
// You wouldn't call the variable "Get*", that exposes
// the implementation.
HTTPServerAddress = getHTTPServerAddress()
HTTPReadTimeout = getHTTPReadTimeout()
HTTPWriteTimeout = getHTTPWriteTimeout()
)
func getHTTPServerAddress() string {
return getConfigString("http.server_address")
}
func getHTTPReadTimeout() time.Duration {
return getConfigDuration("http.read_timeout")
}
func getHTTPWriteTimeout() time.Duration {
return getConfigDuration("http.write_timeout")
}
But no private methods are necessary. Just run the function and assign it to a global.
package config
var (
HTTPServerAddress = getConfigString("http.server_address")
HTTPReadTimeout = getConfigDuration("http.read_timeout")
HTTPWriteTimeout = getConfigDuration("http.write_timeout")
)
Now people can use config.HTTPServerAddress and the details remain hidden without requiring an extra layer of methods.
And yes, it can be done on the result public functions. A very common example is error codes. For example, in net/http.
var (
ErrBodyNotAllowed = errors.New("http: request method or response status code does not allow body")
ErrHijacked = errors.New("http: connection has been hijacked")
)
In Java you might create an exception subclass for each type of error. In Go you make a single object, share it as a global variable, and everything references that object.
The purpose is to hide the details from the user. The exact wording of the error can change, but as long as everyone is referencing net.http.ErrBodyNotAllowed their code will still work.
io/fs has a more complex example exposing the result of its private methods.
var (
ErrInvalid = errInvalid() // "invalid argument"
ErrPermission = errPermission() // "permission denied"
ErrExist = errExist() // "file already exists"
ErrNotExist = errNotExist() // "file does not exist"
ErrClosed = errClosed() // "file already closed"
)
func errInvalid() error { return oserror.ErrInvalid }
func errPermission() error { return oserror.ErrPermission }
func errExist() error { return oserror.ErrExist }
func errNotExist() error { return oserror.ErrNotExist }
func errClosed() error { return oserror.ErrClosed }
Why use private method wrappers here? Why not ErrInvalid = oserror.ErrInvalid?
(I'm assuming) Go wants to keep oserror hidden, even from the documentation!
oserror is just making error objects to define its possible errors.
package oserror
import "errors"
var (
ErrInvalid = errors.New("invalid argument")
ErrPermission = errors.New("permission denied")
ErrExist = errors.New("file already exists")
ErrNotExist = errors.New("file does not exist")
ErrClosed = errors.New("file already closed")
)
If it's result of a function, it's probably something like static member initialization or perhaps even static final.
The example you linked suggests that it's some sort of config load procedure and values are exposed for global use.
Precomputing data is also not unheard of.
If you see function pointers, however, it could be done to enable mocking.
YMMV. Sometimes it's just an antipattern as well.
Now I'm working on GO RPC, I'm using gRPC+Protobuf. And I follow openconfig's data struct, so I could NOT redesign.
I need to fill protobuf struct and Marshal it and send it out, and then client will Unmarshal it and read datas.
My protobuf file(xxx.pb.go) is complicated, for example, just like this:
type ObjBase struct {
a *ObjChildAlice,
b *ObjChildBob,
... // there are many variables like ObjChildCheer, ObjChildDog ...
}
type ObjChildAlice struct {
child *ObjChildChild,
}
type ObjChildBob struct {
child *ObjChildChild,
}
// there are many types like ObjChildCheer, ObjChildDog ...
type ObjChildChild {
int i,
}
In server side, I need to fill ObjBase and send it out, this is my task:
// Code 1
func () {
init ob with ObjBase
if DataBase has Alice {
fill ob.a with ObjChildAlice
}
if DataBase has Bob {
fill ob.a with ObjChildBob
}
// there are many if..else.. DataBase has Cheer...
return ob
}
So I code like this first:
// Code 2
func () {
ob := new(ObjBase)
oba := new(ObjChildAlice)
obb := new(ObjChildBob)
if DataBase has Alice {
ob.a = oba
}
if DataBase has Bob {
ob.b = obb
}
...
return ob
}
But this code could NOT work, as I check member of ob.a and ob.b are all zero.
So I change like this:
// Code 3
func () {
if DataBase has Alice && DataBase has Bob {
ob := &ObjBase{
a: &ObjChildAlice{},
b: &ObjChildBob{},
}
} else if DataBase has Alice && NOT DataBase has Bob {
ob := &ObjBase{
a: &ObjChildAlice{},
}
} else if ...
return ob
}
This works. But in database, there are kinds of variables like Alice, Bob, Cheer, Dog ... It's impossible to use if..else.. to do this work.
So I have questions:
Why ob'member in Code2 is zero?
is there any way to set Go struct's member object dynamically?
Please take a look at this doc which talks the Go generated code for protobufs. https://developers.google.com/protocol-buffers/docs/reference/go-generated
You should be able to set a protobuf message field in the generated code by directly accessing the corresponding member field (which is exported).
I am junior Swift user but on this stage of life I need use serial connection in my project.
when I looking for availeble port ,I see them without problem but when try to send something I have problem why?
I have problem with ORSSerial this my code:
func applicationDidFinishLaunching(_ aNotification: Notification) {
let portEvString = ORSSerialPortManager.shared().availablePorts
let pathString = portEvString[0].path
let portClass = SerialController(path:pathString)
portClass.open()
portClass.SendString(data: "hello")
// Insert code here to initialize your application
RunLoop.current.run()
}
class SerialController : NSObject, ORSSerialPortDelegate {
var port : ORSSerialPort?
init(path: String){
port = ORSSerialPort(path: path)
port?.close()
}
func open(){
port?.baudRate=9600
port?.delegate=self
port?.open()
}
func close(){
port?.delegate=nil
port?.close()
}
func SendString(data: String){
let dataa = Data(data.utf8)
port?.send(dataa)
}
func serialPortWasOpened(serialPort: ORSSerialPort!) {
print("PORT IS OPEN....")
}
func serialPortWasClosed(serialPort: ORSSerialPort!) {
print("PORT IS CLOSE")
}
func serialPort(serialPort: ORSSerialPort!, didReceiveData data: NSData!) {
print(NSString(data: data as Data, encoding: String.Encoding.utf8.rawValue))
}
func serialPortWasRemovedFromSystem(_ serialPort: ORSSerialPort!) {
print("PORT REMOVED")
}
func serialPort(serialPort: ORSSerialPort!, didEncounterError error: NSError!) {
print("PORT ERR \(error)")
}
}
I believe the primary problem is that you don't store the instance of SerialController in a strong reference, so it gets deallocated immediately after you call SendString() on it. You need to store it in a property like so:
var serialController: SerialController?
func applicationDidFinishLaunching(_ aNotification: Notification) {
let availablePorts = ORSSerialPortManager.shared().availablePorts
let pathString = availablePorts[0].path
serialController = SerialController(path:pathString)
serialController?.open()
serialController?.SendString(data: "hello")
}
Beyond that problem, there are a number of things about this code that you should fix. The things that stick out most:
Don't call RunLoop.current.run() in applicationDidFinishLaunching(_:). You rarely need to manually run the run loop, and you definitely don't need to here.
As documented in ORSSerialPort, you must release any strong references to the port in your implementation of serialPortWasRemovedFromSystem(_:). That means in that method, you need to do port = nil.
The force unwrap operators (!) scattered through this code make me wonder if you're using an old version of ORSSerialPort, from before I added nullability annotations. Please make sure you're using the latest version (2.0.2 as of this writing).
Use more descriptive names for your variables. For example, portEvString is not a good name, because it's difficult to understand (what does "Ev" mean?), and its type is not a string, it's an array of ORSSerialPort objects (ie. [ORSSerialPort].
Your SendString method shouldn't start with a capital letter. Function (and method) names in Swift should always start with a lowercase letter.
After the disable SandBox, everything is OK.
When compiling the following Swift code (in Sample.swift):
import Cocoa
class Sample {
func doSomething() {
var stringArray = Array<String>()
stringArray.append("AAA")
addToString(stringArray)
stringArray.append("CCC")
}
func addToString(myArray:Array<String>) {
myArray.append("BBB")
}
}
I get the following error on the line 'myArray.append("BBB")':
Immutable value of type 'Array<String>' only has mutating members named 'append'
How do I fix the code to allow a call to this mutable method?
Many thanks in advance
If you want to modify the array you have to specify it as in an inout parameter like this func addToString(inout myArray:Array<String>). Then when you call the function you have to add & in front of your argument to show that it can be modified by the function. Your code will look something like this:
class Sample {
func doSomething() {
var stringArray = Array<String>()
stringArray.append("AAA")
addToString(&stringArray)
stringArray.append("CCC")
}
func addToString(inout myArray:Array<String>) {
myArray.append("BBB")
}
}
You may want to take a look at in-out parameters on this page.