Design/Code choice : Method chaining vs Reflection

I have a need to create a UI (output as html eventually) and I am trying to decide between using method chaining for creating the UI, or using structs with struct tag aliases (like the standard JSON which I have used)

Method chaining looks like this (this works):

  // create block
server_log := library.NewBlockType("server.log", "misc", setup)
  // add a text output showing 'Log '
server_log.Add(text.New("Log "))
  // below can generate an html text/string input
  // with an empty message with name 'greeting'
server_log.Add(stringinput.New("greeting").Empty("greeting"))

Struct aliasing would look like this (not yet written) :

type UI struct {
 	_        widget.Text   `Log `
 	Greeting widget.String `empty:"greeting"`
}
...
server_log := library.NewBlockType("server.log", "misc", setup, UI{})

It seems to me that a struct approach :

  • + automatically adds the name of the ‘field’ including compilation error if duplicated in the struct
  • + looks tidier
  • reflection could be a performance issue (unlikely for my use)
  • – there is no compile time checking of the struct tag/alias which sets up the UI structure and html names/keys
    • this is a likely candidate for runtime bugs

The method chaining :

  • + includes compile time checking of the UI creation methods
  • has more power if needed (it’s just code)
  • – looks less tidy and includes more code/text
  • – has to specific the name which isn’t compile time checked for duplicates
    • this is a likely candidate for run time bugs - there is a run time check which can warn/error/panic

I will need to create 100s (but not 1000s) of these in their own packages with attached custom Go func() (through setup) for my application and I am trying to reduce technical debt here.

I don’t think performance is significant since any reflection would be a run once only on server startup (of <1000 instances).

Any suggestions or personal experience would be welcome - thank you :slight_smile:

Another issue is that the field order might not be guaranteed. :expressionless: This would break my application completely…

See https://stackoverflow.com/questions/32392311/reflect-type-field-order

One issue I have just discovered - wasm (through tinygo) doesn’t support reflect?!

Actually - tinygo doesn’t FULLY support reflect, e.g. below works in tinygo playground

package main

import (
        "fmt"
        "reflect"
)

type Idb struct {
	txt string `example`
	greeting string `empty:greeting`
}

func main() {
	r := reflect.TypeOf(Id{})
	fmt.Println(r.Name())
	for i := 0; i < r.NumField(); i++ {
		fmt.Println(r.Field(i).Name, r.Field(i).Tag)
	}
}

Outputs

​Id
​txt example
​greeting empty:greeting