From da08c340c8de9a2e2929309cc42260710b20b3fa Mon Sep 17 00:00:00 2001 From: candysmurf <77.ears@gmail.com> Date: Fri, 15 Jul 2016 23:40:49 -0700 Subject: [PATCH] added README.md for the power and elegance of reflection --- .../README.md | 49 +++++++++ .../code/basics/map/map.go | 49 +++++++++ .../code/basics/pointer/pointer.go | 39 +++++++ .../code/basics/slice/slice.go | 49 +++++++++ .../code/basics/struct/struct.go | 35 ++++++ .../code/inspect/integer/integer.go | 93 ++++++++++++++++ .../code/inspect/struct/struct.go | 101 ++++++++++++++++++ .../code/interface/map/map.go | 53 +++++++++ .../code/interface/pointer/pointer.go | 44 ++++++++ .../code/interface/slice/slice.go | 54 ++++++++++ .../code/interface/struct/struct.go | 39 +++++++ 11 files changed, 605 insertions(+) create mode 100644 EmilyGu-PowerAndEleganceOfReflection/README.md create mode 100644 EmilyGu-PowerAndEleganceOfReflection/code/basics/map/map.go create mode 100644 EmilyGu-PowerAndEleganceOfReflection/code/basics/pointer/pointer.go create mode 100644 EmilyGu-PowerAndEleganceOfReflection/code/basics/slice/slice.go create mode 100644 EmilyGu-PowerAndEleganceOfReflection/code/basics/struct/struct.go create mode 100644 EmilyGu-PowerAndEleganceOfReflection/code/inspect/integer/integer.go create mode 100644 EmilyGu-PowerAndEleganceOfReflection/code/inspect/struct/struct.go create mode 100644 EmilyGu-PowerAndEleganceOfReflection/code/interface/map/map.go create mode 100644 EmilyGu-PowerAndEleganceOfReflection/code/interface/pointer/pointer.go create mode 100644 EmilyGu-PowerAndEleganceOfReflection/code/interface/slice/slice.go create mode 100644 EmilyGu-PowerAndEleganceOfReflection/code/interface/struct/struct.go diff --git a/EmilyGu-PowerAndEleganceOfReflection/README.md b/EmilyGu-PowerAndEleganceOfReflection/README.md new file mode 100644 index 0000000..ac6ea7f --- /dev/null +++ b/EmilyGu-PowerAndEleganceOfReflection/README.md @@ -0,0 +1,49 @@ +## Reflection - Standard Library + +Developers are often told reflection is bad and should be avoided. However, when reflection can help reduce the amount of code needed to solve a problem, it should be considered. Reducing complexity while preserving maintainability must always be a priority. Reflection can be a tool that helps makes this possible. In this talk I am going to show you the power and elegance of the reflection package. + +https://candysmurf.github.io/GoReflectionTalk + +## Links + +http://blog.golang.org/laws-of-reflection + +## Code Review + +### Basics + +Example shows how to reflect over a struct type value. +[Struct Types](code/basics/struct/struct.go) ([Go Playground](https://play.golang.org/p/_b73a7uUOx)) + +Example shows how to reflect over a slice of struct type values. +[Slices](code/basics/slice/slice.go) ([Go Playground](https://play.golang.org/p/S4IKEOvbHG)) + +Example shows how to reflect over a map of struct type values. +[Maps](code/basics/map/map.go) ([Go Playground](https://play.golang.org/p/3j6gT94Ji3)) + +Example shows how to reflect over a struct type pointer. +[Pointers](code/basics/pointer/pointer.go) ([Go Playground](https://play.golang.org/p/2zMmWmSNgY)) + +### Interfaces + +Example shows how to reflect over a struct type value that is stored inside an interface value. +[Struct Types](code/interface/struct/struct.go) ([Go Playground](https://play.golang.org/p/vdOHbCHO-c)) + +Example shows how to reflect over a slice of struct type values that are stored inside an interface value. +[Slices](code/interface/slice/slice.go) ([Go Playground](https://play.golang.org/p/YXeogyWr-H)) + +Example shows how to reflect over a map of struct type values that are stored inside an interface value. +[Maps](code/interface/map/map.go) ([Go Playground](https://play.golang.org/p/mZIYNXBdF-)) + +Example shows how to reflect over a struct type pointer that is stored inside an interface value. +[Pointers](code/interface/pointer/pointer.go) ([Go Playground](https://play.golang.org/p/7XUMzJ3hql)) + +### Inspection / Decoding + +Example shows how to inspect a structs fields and display the field name, type and value. +[Struct Types](code/inspect/struct/struct.go) ([Go Playground](https://play.golang.org/p/71xtyS_sts)) + +Example shows how to use reflection to decode an integer. +[Integers](code/interface/integer/integer.go) ([Go Playground](https://play.golang.org/p/q-EIfJZUI-)) +___ +All material is licensed under the [Apache License Version 2.0, June 2016](http://www.apache.org/licenses/LICENSE-2.0). diff --git a/EmilyGu-PowerAndEleganceOfReflection/code/basics/map/map.go b/EmilyGu-PowerAndEleganceOfReflection/code/basics/map/map.go new file mode 100644 index 0000000..d67f283 --- /dev/null +++ b/EmilyGu-PowerAndEleganceOfReflection/code/basics/map/map.go @@ -0,0 +1,49 @@ +// All material is licensed under the Apache License Version 2.0, June 2016 +// http://www.apache.org/licenses/LICENSE-2.0 + +// Example shows how to reflect over a map of struct type values. +package main + +import ( + "fmt" + "reflect" +) + +// user represents a basic user in the system. +type user struct { + name string + age int + building float32 + secure bool + roles []string +} + +func main() { + + // Create a map of struct type user values. + um := map[string]user{ + "Cindy": { + name: "Cindy", + age: 27, + building: 321.45, + secure: true, + roles: []string{"admin", "developer"}, + }, + "Bill": { + name: "Bill", + age: 40, + building: 456.21, + secure: false, + roles: []string{"developer"}, + }, + } + + // Display the information about the map of users values. + v := reflect.ValueOf(um) + fmt.Printf("Kind: %v\tType: %v\n", v.Kind(), v.Type()) + + // Iterate over the map via reflection. + for i, key := range v.MapKeys() { + fmt.Println(i, ":", v.MapIndex(key)) + } +} diff --git a/EmilyGu-PowerAndEleganceOfReflection/code/basics/pointer/pointer.go b/EmilyGu-PowerAndEleganceOfReflection/code/basics/pointer/pointer.go new file mode 100644 index 0000000..1e55aff --- /dev/null +++ b/EmilyGu-PowerAndEleganceOfReflection/code/basics/pointer/pointer.go @@ -0,0 +1,39 @@ +// All material is licensed under the Apache License Version 2.0, June 2016 +// http://www.apache.org/licenses/LICENSE-2.0 + +// Example shows how to reflect over a struct type pointer. +package main + +import ( + "fmt" + "reflect" +) + +// user represents a basic user in the system. +type user struct { + name string + age int + building float32 + secure bool + roles []string +} + +func main() { + + // Create a struct type user value. + u := user{ + name: "Cindy", + age: 27, + building: 321.45, + secure: true, + roles: []string{"admin", "developer"}, + } + + // Display the information about the pointer value. + v := reflect.ValueOf(&u) + fmt.Printf("Kind: %v\tType: %v\n", v.Kind(), v.Type()) + + // Inspect the value that the pointer points to. + v = v.Elem() + fmt.Printf("Kind: %v\tType: %v\t\tNumFields: %v\n", v.Kind(), v.Type(), v.NumField()) +} diff --git a/EmilyGu-PowerAndEleganceOfReflection/code/basics/slice/slice.go b/EmilyGu-PowerAndEleganceOfReflection/code/basics/slice/slice.go new file mode 100644 index 0000000..71b5161 --- /dev/null +++ b/EmilyGu-PowerAndEleganceOfReflection/code/basics/slice/slice.go @@ -0,0 +1,49 @@ +// All material is licensed under the Apache License Version 2.0, June 2016 +// http://www.apache.org/licenses/LICENSE-2.0 + +// Example shows how to reflect over a slice of struct type values. +package main + +import ( + "fmt" + "reflect" +) + +// user represents a basic user in the system. +type user struct { + name string + age int + building float32 + secure bool + roles []string +} + +func main() { + + // Create a slice of struct type user values. + us := []user{ + { + name: "Cindy", + age: 27, + building: 321.45, + secure: true, + roles: []string{"admin", "developer"}, + }, + { + name: "Bill", + age: 40, + building: 456.21, + secure: false, + roles: []string{"developer"}, + }, + } + + // Display the information about the slice of users values. + v := reflect.ValueOf(us) + fmt.Printf("Kind: %v\tType: %v\n", v.Kind(), v.Type()) + + // Iterate over the slice via reflection. + for i := 0; i < v.Len(); i++ { + fmt.Println(v.Index(i)) + } +} diff --git a/EmilyGu-PowerAndEleganceOfReflection/code/basics/struct/struct.go b/EmilyGu-PowerAndEleganceOfReflection/code/basics/struct/struct.go new file mode 100644 index 0000000..6c96ec3 --- /dev/null +++ b/EmilyGu-PowerAndEleganceOfReflection/code/basics/struct/struct.go @@ -0,0 +1,35 @@ +// All material is licensed under the Apache License Version 2.0, June 2016 +// http://www.apache.org/licenses/LICENSE-2.0 + +// Example shows how to reflect over a struct type value. +package main + +import ( + "fmt" + "reflect" +) + +// user represents a basic user in the system. +type user struct { + name string + age int + building float32 + secure bool + roles []string +} + +func main() { + + // Create a struct type user value. + u := user{ + name: "Cindy", + age: 27, + building: 321.45, + secure: true, + roles: []string{"admin", "developer"}, + } + + // Display the information about the user value. + v := reflect.ValueOf(u) + fmt.Printf("Kind: %v\tType: %v\t\tNumFields: %v\n", v.Kind(), v.Type(), v.NumField()) +} diff --git a/EmilyGu-PowerAndEleganceOfReflection/code/inspect/integer/integer.go b/EmilyGu-PowerAndEleganceOfReflection/code/inspect/integer/integer.go new file mode 100644 index 0000000..05ae0f5 --- /dev/null +++ b/EmilyGu-PowerAndEleganceOfReflection/code/inspect/integer/integer.go @@ -0,0 +1,93 @@ +// All material is licensed under the Apache License Version 2.0, January 2016 +// http://www.apache.org/licenses/LICENSE-2.0 + +// Example shows how reflection provides integrity. +package main + +import ( + "fmt" + "reflect" + "strconv" +) + +func main() { + + // Decode the string into the integer variable. + var number int + decodeInt("10", &number) + fmt.Println("number:", number) + + // Decode the integer into the integer variable. + var age int + decodeInt(45, &age) + fmt.Println("age:", age) +} + +// decodeInt accepts a value of any type and will decode +// that value to an integer. +func decodeInt(v interface{}, number *int) error { + + // Inspect the concrete type value that is passed in. + rv := reflect.ValueOf(v) + + // Retrieve the value that the integer pointer points to. + val := reflect.ValueOf(number).Elem() + + // Based on the kind of data to decode, perform the specific logic. + switch getKind(rv) { + + case reflect.Int: + val.SetInt(rv.Int()) + + case reflect.Uint: + val.SetInt(int64(rv.Uint())) + + case reflect.Float32: + val.SetInt(int64(rv.Float())) + + case reflect.Bool: + if rv.Bool() { + val.SetInt(1) + return nil + } + + val.SetInt(0) + + case reflect.String: + i, err := strconv.ParseInt(rv.String(), 0, val.Type().Bits()) + if err != nil { + return fmt.Errorf("cannot parse as int: %s", err) + } + + val.SetInt(i) + + default: + return fmt.Errorf("expected type '%s', got unconvertible type '%s'", val.Type(), rv.Type()) + } + + return nil +} + +// getKind provides support for identifying predeclared numeric +// types with implementation-specific sizes. +func getKind(val reflect.Value) reflect.Kind { + + // Capture the value's Kind. + kind := val.Kind() + + // Check each condition until a case is true. + switch { + + case kind >= reflect.Int && kind <= reflect.Int64: + return reflect.Int + + case kind >= reflect.Uint && kind <= reflect.Uint64: + return reflect.Uint + + case kind >= reflect.Float32 && kind <= reflect.Float64: + return reflect.Float32 + + default: + return kind + } +} diff --git a/EmilyGu-PowerAndEleganceOfReflection/code/inspect/struct/struct.go b/EmilyGu-PowerAndEleganceOfReflection/code/inspect/struct/struct.go new file mode 100644 index 0000000..c9f9eb1 --- /dev/null +++ b/EmilyGu-PowerAndEleganceOfReflection/code/inspect/struct/struct.go @@ -0,0 +1,101 @@ +// All material is licensed under the Apache License Version 2.0, January 2016 +// http://www.apache.org/licenses/LICENSE-2.0 + +// Example shows how reflection provides readability and simplicity. +package main + +import ( + "fmt" + "reflect" +) + +// user represents a basic user in the system. +type user struct { + name string + age int + building float32 + secure bool + roles []string +} + +func main() { + + // Create a value of the conrete type user. + u := user{ + name: "Cindy", + age: 27, + building: 321.45, + secure: true, + roles: []string{"admin", "developer"}, + } + + // Display the value we are passing. + display(&u) +} + +// display will display the details of the provided value. +func display(v interface{}) { + + // Inspect the concrete type value that is passed in. + rv := reflect.ValueOf(v) + + // Was the value a pointer value. + if rv.Kind() == reflect.Ptr { + + // Get the value that the pointer points to. + rv = rv.Elem() + } + + // Based on the Kind of value customize the display. + switch rv.Kind() { + + case reflect.Struct: + displayStruct(rv) + } +} + +// displayStruct will display details about a struct type. +func displayStruct(rv reflect.Value) { + + // Show each field and the field information. + for i := 0; i < rv.NumField(); i++ { + + // Get field information for this field. + fld := rv.Type().Field(i) + fmt.Printf("Name: %s\tKind: %s", fld.Name, fld.Type.Kind()) + + // Display the value of this field. + fmt.Printf("\tValue: ") + displayValue(rv.Field(i)) + + // Add an extra line feed for the display. + fmt.Println() + } +} + +// displayValue extracts the native value from the reflect value that is +// passed in and properly displays it. +func displayValue(rv reflect.Value) { + + // Display each value based on its Kind. + switch rv.Type().Kind() { + + case reflect.String: + fmt.Printf("%s", rv.String()) + + case reflect.Int: + fmt.Printf("%v", rv.Int()) + + case reflect.Float32: + fmt.Printf("%v", rv.Float()) + + case reflect.Bool: + fmt.Printf("%v", rv.Bool()) + + case reflect.Slice: + for i := 0; i < rv.Len(); i++ { + displayValue(rv.Index(i)) + fmt.Printf(" ") + } + } +} diff --git a/EmilyGu-PowerAndEleganceOfReflection/code/interface/map/map.go b/EmilyGu-PowerAndEleganceOfReflection/code/interface/map/map.go new file mode 100644 index 0000000..e901927 --- /dev/null +++ b/EmilyGu-PowerAndEleganceOfReflection/code/interface/map/map.go @@ -0,0 +1,53 @@ +// All material is licensed under the Apache License Version 2.0, June 2016 +// http://www.apache.org/licenses/LICENSE-2.0 + +// Example shows how to reflect over a map of struct type values that +// are stored inside an interface value. +package main + +import ( + "fmt" + "reflect" +) + +// user represents a basic user in the system. +type user struct { + name string + age int + building float32 + secure bool + roles []string +} + +func main() { + + // Create a map of struct type user values. + um := map[string]user{ + "Cindy": { + name: "Cindy", + age: 27, + building: 321.45, + secure: true, + roles: []string{"admin", "developer"}, + }, + "Bill": { + name: "Bill", + age: 40, + building: 456.21, + secure: false, + roles: []string{"developer"}, + }, + } + + // Store a value of the map inside an empty interface value. + var i interface{} = um + + // Display the information about the map of users values. + v := reflect.ValueOf(i) + fmt.Printf("Kind: %v\tType: %v\n", v.Kind(), v.Type()) + + // Iterate over the map via reflection. + for i, key := range v.MapKeys() { + fmt.Println(i, ":", v.MapIndex(key)) + } +} diff --git a/EmilyGu-PowerAndEleganceOfReflection/code/interface/pointer/pointer.go b/EmilyGu-PowerAndEleganceOfReflection/code/interface/pointer/pointer.go new file mode 100644 index 0000000..58082d2 --- /dev/null +++ b/EmilyGu-PowerAndEleganceOfReflection/code/interface/pointer/pointer.go @@ -0,0 +1,44 @@ +// All material is licensed under the Apache License Version 2.0, June 2016 +// http://www.apache.org/licenses/LICENSE-2.0 + +// Example shows how to reflect over a struct type pointer that is stored +// inside an interface value. +package main + +import ( + "fmt" + "reflect" +) + +// user represents a basic user in the system. +type user struct { + name string + age int + building float32 + secure bool + roles []string +} + +func main() { + + // Create a struct type user value. + u := user{ + name: "Cindy", + age: 27, + building: 321.45, + secure: true, + roles: []string{"admin", "developer"}, + } + + // Store a pointer to the user value inside an empty interface value. + var i interface{} = &u + + // Display information about the pointer that is stored inside the + // empty interface value. + v := reflect.ValueOf(i) + fmt.Printf("Kind: %v\tType: %v\n", v.Kind(), v.Type()) + + // Inspect the value that the pointer points to. + v = v.Elem() + fmt.Printf("Kind: %v\tType: %v\t\tNumFields: %v\n", v.Kind(), v.Type(), v.NumField()) +} diff --git a/EmilyGu-PowerAndEleganceOfReflection/code/interface/slice/slice.go b/EmilyGu-PowerAndEleganceOfReflection/code/interface/slice/slice.go new file mode 100644 index 0000000..294e2db --- /dev/null +++ b/EmilyGu-PowerAndEleganceOfReflection/code/interface/slice/slice.go @@ -0,0 +1,54 @@ +// All material is licensed under the Apache License Version 2.0, June 2016 +// http://www.apache.org/licenses/LICENSE-2.0 + +// Example shows how to reflect over a slice of struct type values that +// are stored inside an interface value. +package main + +import ( + "fmt" + "reflect" +) + +// user represents a basic user in the system. +type user struct { + name string + age int + building float32 + secure bool + roles []string +} + +func main() { + + // Create a slice of struct type user values. + us := []user{ + { + name: "Cindy", + age: 27, + building: 321.45, + secure: true, + roles: []string{"admin", "developer"}, + }, + { + name: "Bill", + age: 40, + building: 456.21, + secure: false, + roles: []string{"developer"}, + }, + } + + // Store a value of the slice inside an empty interface value. + var i interface{} = us + + // Display information about the slice stored inside the empty + // interface value. + v := reflect.ValueOf(i) + fmt.Printf("Kind: %v\tType: %v\n", v.Kind(), v.Type()) + + // Iterate over the slice via reflection. + for i := 0; i < v.Len(); i++ { + fmt.Println(v.Index(i)) + } +} diff --git a/EmilyGu-PowerAndEleganceOfReflection/code/interface/struct/struct.go b/EmilyGu-PowerAndEleganceOfReflection/code/interface/struct/struct.go new file mode 100644 index 0000000..7a976c3 --- /dev/null +++ b/EmilyGu-PowerAndEleganceOfReflection/code/interface/struct/struct.go @@ -0,0 +1,39 @@ +// All material is licensed under the Apache License Version 2.0, June 2016 +// http://www.apache.org/licenses/LICENSE-2.0 + +// Example shows how to reflect over a struct type value that is stored +// inside an interface value. +package main + +import ( + "fmt" + "reflect" +) + +// user represents a basic user in the system. +type user struct { + name string + age int + building float32 + secure bool + roles []string +} + +func main() { + + // Create a struct type user value. + u := user{ + name: "Cindy", + age: 27, + building: 321.45, + secure: true, + roles: []string{"admin", "developer"}, + } + + // Store a copy of the user value inside an empty interface value. + var i interface{} = u + + // Display information about the user value that was stored. + v := reflect.ValueOf(i) + fmt.Printf("Kind: %v\tType: %v\t\tNumFields: %v\n", v.Kind(), v.Type(), v.NumField()) +}