I come from a long career as a Java developer and I am now moving to go(lang). I tried rust for a while but as my C and CPP experience is very poor I could not afford the time and effort. Go is a much better fit especially now I can create cross platform UI(s) using fyne. I have also been using flutter and dart and like it a lot. Anyway to my questions. See Things I need help with! below.
I have included the code below.
I have an interface (called NodeI) in the structure code. It has three methods.
- GetName() string
- GetNodeType() NodeType
- String() string
I have a node called ParentNode (I want to inherit its behaviour). It has two methods.
- GetName() string
- GetNodeType() NodeType
I have four struct(ures) that all have an unnamed parent. For example ListNode and All have a String() function (String() string)
type ListNode struct {
parentNode // In ALL four structures
value []*NodeI // Specific to ListNode
}
I am using pointers to prevent ‘copy’ behaviour and all the structures must be updatable (except name and NodeType in the parent. These are immutable)
I am creating a tree structure with ListNode(s) that contain leaf nodes and also more ListNode(s)…etc
Things I need help with!
Create a concrete object in a func and return it as its interface
With reference to makeStruct() in main.go. I would prefer to return *structure.NodeI. Sort of a factory method. However I cannot convert the *structure.ListNode to a *structure.NodeI and return it.
I tried ‘pp := (*p).(*structure.NodeI)’ where p is a *structure.ListNode and I have tried ‘many’ different formats to get the conversion to work, without any success.
Passing a concrete object to a method as it’s interface
With reference to the printName function that takes a *structure.NodeI as a parameter.
I can pass it a *structure.NodeI but not any of the concrete objects that have the interface.
For example printName(p) where p is *structure.ListNode fails. While printName(b) works. Here b is (T) *structure.NodeI. It was returned from the ListNode GetNodeAt(i) method and was created as a structure.NumberNode.
Please help. The code will be much easier to read and understand if the patterns can be applied.
Many thanks
Stuart
main.go
func main() {
p := makeStruct()
fmt.Printf(" 1: %T\n", p) // *structure.ListNode
fmt.Printf(" 2: %s", p) // String() on ListNode
b := p.GetValueAt(1)
//
// printName(p) // This does not compile. p is *structure.NumberNode
//
printName(b, " 3")
fmt.Printf(" 4: %T\n", b) // *structure.NodeI
fmt.Printf(" 5: %T\n", *b) // *structure.NumberNode
fmt.Printf(" 6: %s\n", *b) // String() on NumberNode
bb := (*b).(*structure.NumberNode)
fmt.Printf(" 7: %T\n", bb) // *structure.NumberNode
bb.SetValue(999)
fmt.Printf(" 8: %s\n", bb.GetName()) // NUM
fmt.Printf(" 9: %f\n", bb.GetValue()) // 999.000000
fmt.Printf("10: %s\n", bb) // String() on NumberNode
fmt.Printf("11: %s", p) // String() on ListNode
}
func printName(ni *structure.NodeI, id string) {
fmt.Printf("%s: Name: %s\n", id, (*ni).GetName())
}
func makeStruct() *structure.ListNode {
p := structure.NewListNode()
s := structure.NewStringNode("STR", "B")
n := structure.NewNumberNode("NUM", 123.5)
b := structure.NewBoolNode("BOO", true)
p.AddValue(s)
p.AddValue(n)
p.AddValue(b)
fmt.Printf(" 0: %T\n", p) // *structure.ListNode
// pp := (*p).(*structure.NodeI)
// fmt.Printf("makeStruct: %T", pp) // *structure.ListNode
return p
}
Code for nodes!
package structure
import (
"fmt"
"strings"
)
type NodeType int
const (
NT_NUMBER NodeType = iota
NT_STRING NodeType = iota
NT_BOOL NodeType = iota
)
type NodeI interface {
GetName() string
GetNodeType() NodeType
String() string
}
//
// Parent node interface (NodeI) and properties
//
type parentNode struct {
name string
nt NodeType
}
func newParentNode(name string, nt NodeType) parentNode {
return parentNode{name: name, nt: nt}
}
func (n *parentNode) GetName() string {
return n.name
}
func (n *parentNode) GetNodeType() NodeType {
return n.nt
}
type ListNode struct {
parentNode
value []*NodeI
}
//
// List node is a ParentNode with a value of type []NodeI
//
func NewListNode() *ListNode {
return &ListNode{parentNode: newParentNode("", NT_NUMBER), value: make([]*NodeI, 0)}
}
func (n *ListNode) GetValue() *[]*NodeI {
return &n.value
}
func (n *ListNode) GetValueAt(i int) *NodeI {
return n.value[i]
}
func (n *ListNode) AddValue(node NodeI) {
n.value = append(n.value, &node)
}
func (n *ListNode) String() string {
var sb strings.Builder
sb.WriteString("ListNode.String() :\n")
for i, v := range n.value {
sb.WriteString(fmt.Sprintf(" %d :", i))
sb.WriteString((*v).String())
sb.WriteString("\n")
}
return sb.String()
}
//
// String node is a ParentNode and a value of type string
//
type StringNode struct {
parentNode
value string
}
func NewStringNode(name string, value string) *StringNode {
return &StringNode{parentNode: newParentNode(name, NT_STRING), value: value}
}
func (n *StringNode) GetValue() string {
return n.value
}
func (n *StringNode) SetValue(newValue string) {
n.value = newValue
}
func (n *StringNode) String() string {
return fmt.Sprintf("StringNode.String(): \"%s\": \"%s\"", n.GetName(), n.value)
}
//
// Number node is a ParentNode and a value of type float64
//
type NumberNode struct {
parentNode
value float64
}
func NewNumberNode(name string, value float64) *NumberNode {
return &NumberNode{parentNode: newParentNode(name, NT_NUMBER), value: value}
}
func (n *NumberNode) GetValue() float64 {
return n.value
}
func (n *NumberNode) SetValue(newValue float64) {
n.value = newValue
}
func (n *NumberNode) String() string {
return fmt.Sprintf("NumberNode.String(): \"%s\": %f", n.GetName(), n.value)
}
//
// Bool node is a ParentNode and a value of type bool
//
type BoolNode struct {
parentNode
value bool
}
func NewBoolNode(name string, value bool) *BoolNode {
return &BoolNode{parentNode: newParentNode(name, NT_BOOL), value: value}
}
func (n *BoolNode) GetValue() bool {
return n.value
}
func (n *BoolNode) SetValue(newValue bool) {
n.value = newValue
}
func (n *BoolNode) String() string {
return fmt.Sprintf("BoolNode.String(): \"%s\": %t", n.GetName(), n.value)
}