This presentation was given at OSCON 2014.
Object Oriented (OO) programming has dominated software engineering for the
last two decades. The paradigm built on powerful concepts such as
Encapsulation, Inheritance, and Polymoprhism has been internalized by the
majority of software engineers. Although Go is not OO in the strict sense, we
can continue to leverage the skills we’ve honed as OO engineers to come up with
simple and solid designs.
Gopher Steve Francia, Author of
Hugo, Cobra, and many
other popular Go packages makes these difficult concepts accessible for everyone.
If you’re a OO programmer, especially one with a background with dynamic
languages and are curious about go then this talk is for you. We will cover
everything you need to know to leverage your existing skills and quickly start
coding in go including:
- How to use our Object Oriented programming fundamentals in go
- Static and pseudo dynamic typing in go
- Building fluent interfaces in go
- Using go interfaces and duck typing to simplify architecture
- Common mistakes made by those coming to go from other OO languages (Ruby, Python, Javascript, etc.),
- Principles of good design in go.
Slide Transcript
- Go for Object Oriented Programmers or Object Oriented Programming Without Objects
- • Author of Hugo , Cobra , Viper & More • Chief Developer Advocate for MongoDB • Gopher @spf13
- “Most of the appeal for me is not the features that Go has, but rather the features that have been intentionally left out.” — txxxxd
- “Why would you have a language that is not theoretically exciting? Because it’s very useful.” — Rob Pike
- “Objects” in Go
- Does Go have Objects? • Go lacks Classes • Go lacks “Objects”
- What is an Object?
- “An object is an abstract data type that has state (data) and behavior (code).” – Steve Francia
- type Rect struct { width int height int }
- Type Declaration (Struct) func (r *Rect) A rea() int { return r.width * r.height }
- Declaring a Method func main() { r := Rect{width: 10, height: 5} fmt.Println(“area: “, r.Area() ) }
- In Action type Rect s []Rect
- Type Declaration (Slice) func (rs Rects) Area() int { var a int for _, r := range rs { a += r.Area() } return a }
- Declaring a Method func main() { r := Rect{width: 10, height: 5} x := Rect{width: 7, height:10} rs := Rects{r, x} fmt.Println(“r’s area: “, r.Area()) fmt.Println(“x’s area: “, x.Area()) fmt.Println(“total area: “, rs.Area() ) }
- In Action http://play.golang.org/p/G1OWXPGvc3 type Foo func() int
- Type Declaration (Func) func ( f Foo ) Add(x int) int { return f() + x }
- Declaring a Method func main() { var x Foo x = func() int { return 1 } fmt.Println(x()) fmt.Println( x.Add(3) ) }
- In Action http://play.golang.org/p/YGrdCG3SlI
- Go Has “Objects”
- “Object Oriented” Go
- A language is usually considered object-based if it includes the basic capabilities for an object: identity, properties, and attributes . A language is considered object-oriented if it is object-based and also has the capability of polymorphism and inheritance . – Wikipedia
- Go is Object Based. Is it OO?
- Inheritance • Provides reuse of objects • Classes are created in hierarchies • Inheritance lets the structure and methods in one class pass down the hierarchy
- Go’s approach • Go explicitly avoided inheritance • Go strictly follows the composition over inheritance principle • Composition through embedded types
- Composition • Provides reuse of Objects • One object is declared by including other objects • Composition lets the structure and methods in one class be pulled into another
- Inheritance passes “knowledge” down ! Composition pulls “knowledge” up – Steve Francia
- type Person struct { Name string Address } type Address struct { Number string Street string City string State string Zip string }
- Embedding Types
- Inner Type func ( a *Address ) String() string { return a.Number+” “+a.Street+"\n”+ a.City+”, “+a.State+” “+a.Zip+"\n” }
- Declaring a Method func main() { p := Person{ Name: “Steve”, Address: Address{ Number: “13”, Street: “Main”, City: “Gotham”, State: “NY”, Zip: “01313”, }, } }
- Declare using Composite Literal
- func main() { p := Person{ Name: “Steve”, Address: Address{ Number: “13”, Street: “Main”, City: “Gotham”, State: “NY”, Zip: “01313”, }, } fmt.Println(p.String())
- In Action http://play.golang.org/p/9beVY9jNlW
- Promotion • Promotion looks to see if a single inner type can satisify the request and “promotes” it • Embedded fields & methods are “promoted” • Promot ion only occurs during usage, not declaration • Promoted methods are considered for interface adherance
- Not Overloading func (a * Address) String() string { return a.Number+” “+a.Street+"\n”+ a.City+”, “+a.State+” “+a.Zip+"\n” } ! func (p * Person) String() string { return p.Name + “\n” + p.Address.String() }
- Both Methods Available func main() { p := Person{ Name: “Steve”, Address: Address{ Number: “13”, Street: “Main”, City: “Gotham”, State: “NY”, Zip: “01313”, }, } ! f mt.Println(p.String()) fmt.Println(p.Address.String()) } http://play.golang.org/p/Aui0nGa5Xi
- Types Remain Distinct func isValidAddress(a *Address) bool { return a.Street != "” } ! func main() { p := Person{ Name: “Steve”, Address: Address{ Number: “13”, Street: “Main”, City: “Gotham”, State: “NY”, Zip: “01313”}} ! fmt.Println(isValidAddress(p)) // cannot use p (type Person) as type Address // in argument to isValidAddress fmt.Println(isValidAddress(p.Address)) } http://play.golang.org/p/KYjXZxNBcQ
- Promotion is NOT Subtyping
- Polymorphism • “The provision of a single interface to entities of different types” • Typically implmented via Generics, Overloading and/or Subtyping
- Go’s approach • Go explicitly avoided subtyping & overloading • Go does not provide Generics (yet) • Go’s interface provide polymorphic capabilities
- Interfaces • A list of required methods • Structural vs Nominal typing • ‘‘If something can do this, then it can be used here ” • Convention is call it a Something-er
- Interface Declaration type Shaper interface { Area() int }
- Using Interface as Param Type func Describe( s Shaper ) { fmt.Println(“Area is: “, s.Area()) }
- In Action http://play.golang.org/p/WL77LihUwi func main() { r := &Rect{width: 10, height: 5} x := &Rect{width: 7, height: 10} rs := &Rects{r, x} Describe(r) Describe(x) Describe(rs) }
- “If you could do Java over again, what would you change?” “I’d leave out classes,” he replied. After the laughter died down, he explained that the real problem wasn’t classes per se, but rather implementation inheritance (the extends relationship). Interface inheritance (the implements relationship) is preferable. You should avoid implementation inheritance whenever possible. – James Gosling (creator of Java)
- Go Interfaces are based on implementation, not declaration
- The Power of Interfaces
- type Reader interface { Read(p []byte) (n int, err error) } io.Reader io.Reader • Interface
- • Read reads up to len(p) bytes into p • Returns the # of bytes read & any error • Does not dictate how Read() is implemented • Used by os.File, bytes.Buffer, net.Conn, http.Request.Body, loads more
- type Writer interface { Write(p []byte) (n int, err error) } io.Writer io.Writer • Interface
- • Write writes up to len(p) bytes into p • Returns the # of bytes written & any error • Does not dictate how Write() is implemented • Used by os.File, bytes.Buffer, net.Conn, http.Response.Body, loads more
- func MarshalGzippedJSON(r io.Reader, v interface{}) error { raw , err := gzip.NewReader(r) if err != nil { return err } return json.NewDecoder(raw).Decode(&v) } io.Reader in Action
- f, err := os.Open(“myfile.json.gz”) if err != nil { log.Fatalln (err) } defer f.Close() m = make(map[string]interface{}) MarshalGzippedJSON(f , &m) Reading a json.gz file
- Practical Interoperability • Gzip.NewReader(io.Reader) • Works on files, http requests, byte buffers, network connections, …anything you create • Nothing special needed in gzip to be able to do this… Simply call Read(n) and leave the abstracting to the implementor
- func main() { resp, err := http.Get(”…") if err != nil { log.Fatalln(err) } defer resp.Body.Close() out, err := os.Create(“filename.ext”) if err != nil { log.Fatalln(err) } defer out.Close() io.Copy(out, resp.Body) // out io.Writer, resp.Body io.Reader } Pipe http response to file
- Go
- Simple can be harder than complex: You have to work hard to get your thinking clean to make it simple. But it’s worth it in the end because once you get there, you can move mountains. — Steve Jobs
- Go is simple, pratical & wonderful
- Go build something great
- Thank You