object oriented programming in go
TRANSCRIPT
Object-Oriented Programming inGoGo Seoul Meetup24 January 2015
장재휴Developer, Purpleworks
Who am I
Elandsystems ➜ purpleworks
Ruby, C#, Go
Agenda
OOP in Go
What is "Object Oriented" (wikipedia)
A language is usually considered object-based if it includes the basic capabilitiesfor an object: identity, properties, and attributes
A language is considered object-oriented if it is object-based and also has thecapability of polymorphism and inheritance
Is Go Object-based Language?
What is an "Object" (wikipedia)
An object is an abstract data type that has state(data) and behavior(code)
Object in Go (via Custom Type & Method)
package main
import "fmt"
// Type Declarationtype Item struct {type Item struct { name string price float64 quantity int}
// Method Declarationfunc (t Item) Cost() float64 {func (t Item) Cost() float64 { return t.price * float64(t.quantity)}
// In Actionfunc main() { shirt := Item{name: "Men's Slim-Fit Shirt", price: 25000, quantity: 3} fmt.Println("cost: ", shirt.Cost())fmt.Println("cost: ", shirt.Cost())} Run
Custom Type of built-in Type
// Type Declarationtype Items []Itemtype Items []Item
// Method Declarationfunc (ts Items) Cost() float64 {func (ts Items) Cost() float64 { var c float64 for _, t := range ts { c += t.Cost() } return c}
// In Actionfunc main() { shirt := Item{name: "Men's Slim-Fit Shirt", price: 25000, quantity: 3} shoes := Item{name: "Sports Shoes", price: 30000, quantity: 2} items := Items{shirt, shoes}
fmt.Println("cost of shirt: ", shirt.Cost()) fmt.Println("cost of shoes: ", shoes.Cost()) fmt.Println("total cost: ", items.Cost())} Run
Go is Object-based
Via custom type and methods
Is Go Object-oriented Language?
What is "Inheritance" (wikipedia)
Provides reuse of existing objects
Classes are created in hierarchies
Inheritance passes knowledge down!
Inheritance is not good
In Java user group meeting, James Gosling (Java’s inventor) says:
You should avoid implementation inheritance whenever possible.
Go's approach
Go avoided inheritance
Go strictly follows the Composition over inheritance principle
What is Composition
Provides reuse of Objects
One object is declared by containing other objects
Composition pulls knowledge into another
Composition in Go
// Type Declarationtype Item struct { name string price float64 quantity int}
type DiscountItem struct { ItemItem discountRate float64}
// In Actionfunc main() { shoes := Item{name: "Sports Shoes", price: 30000, quantity: 2} eventShoes := DiscountItem{eventShoes := DiscountItem{ Item{name: "Women's Walking Shoes", price: 50000, quantity: 3},Item{name: "Women's Walking Shoes", price: 50000, quantity: 3}, 10.00,10.00, }}
fmt.Println("shoes: ", shoes) fmt.Println("eventShoes: ", eventShoes)} Run
Call Method of Embedded Type
type DiscountItem struct { Item discountRate float64}
// Method Declarationfunc (t Item) Cost() float64 {func (t Item) Cost() float64 { return t.price * float64(t.quantity)return t.price * float64(t.quantity)}}
// In Actionfunc main() { shoes := Item{name: "Sports Shoes", price: 30000, quantity: 2} eventShoes := DiscountItem{ Item{name: "Women's Walking Shoes", price: 50000, quantity: 3}, 10.00, }
fmt.Println("cost of shoes: ", shoes.Cost())fmt.Println("cost of shoes: ", shoes.Cost()) fmt.Println("cost of eventShoes: ", eventShoes.Cost())fmt.Println("cost of eventShoes: ", eventShoes.Cost())} Run
Method Overriding
// Method Declarationfunc (t Item) Cost() float64 { return t.price * float64(t.quantity)}
func (t DiscountItem) Cost() float64 {func (t DiscountItem) Cost() float64 { return t.Item.Cost() * (1.0 - t.discountRate/100)return t.Item.Cost() * (1.0 - t.discountRate/100)}}
// In Actionfunc main() { shoes := Item{name: "Sports Shoes", price: 30000, quantity: 2} eventShoes := DiscountItem{ Item{name: "Women's Walking Shoes", price: 50000, quantity: 3}, 10.00, }
fmt.Println("cost of shoes: ", shoes.Cost()) fmt.Println("cost of eventShoes: ", eventShoes.Cost())} Run
Composition in Go 2
// Type Declaration (embedded field)type Order struct { ItemsItems taxRate float64}
// Overriding Methodsfunc (o Order) Cost() float64 {func (o Order) Cost() float64 { return o.Items.Cost() * (1.0 + o.taxRate/100)}
func main() { shirt := Item{name: "Men's Slim-Fit Shirt", price: 25000, quantity: 3} shoes := Item{name: "Sports Shoes", price: 30000, quantity: 2} items := Items{shirt, shoes} order := Order{Items: items, taxRate: 10.00}
fmt.Println("cost of shirt: ", shirt.Cost()) fmt.Println("cost of shoes: ", shoes.Cost()) fmt.Println("total cost: ", items.Cost()) fmt.Println("total cost(included Tax): ", order.Cost())} Run
Composition in Go 3
func main() { shirt := Item{name: "Men's Slim-Fit Shirt", price: 25000, quantity: 3} shoes := Item{name: "Sports Shoes", price: 30000, quantity: 2} eventShoes := DiscountItem{ Item{name: "Women's Walking Shoes", price: 50000, quantity: 3}, 10.00, } items := Items{shirt, shoes, eventShoes} order := Order{Items: items, taxRate: 10.00}
fmt.Println("cost of shirt: ", shirt.Cost()) fmt.Println("cost of shoes: ", shoes.Cost()) fmt.Println("cost of eventShoes: ", eventShoes.Cost()) fmt.Println("total cost: ", items.Cost()) fmt.Println("total cost(included Tax): ", order.Cost())} Run
Composition in Go 4
func main() { shirt := Item{name: "Men's Slim-Fit Shirt", price: 25000, quantity: 3} shoes := Item{name: "Sports Shoes", price: 30000, quantity: 2} eventShoes := DiscountItem{ Item{name: "Women's Walking Shoes", price: 50000, quantity: 3}, 10.00, } items := Items{shirt, shoes, eventShoes} order := Order{Items: items, taxRate: 10.00}
fmt.Println("cost of shirt: ", shirt.Cost()) fmt.Println("cost of shoes: ", shoes.Cost()) fmt.Println("cost of eventShoes: ", eventShoes.Cost()) fmt.Println("total cost: ", items.Cost()) fmt.Println("total cost(included Tax): ", order.Cost())} Run
What is "Polymorphism" (wikipedia)
The provision of a single interface to entities of different types
Via Generics, Overloading and/or Subtyping
Go’s approach
Go avoided subtyping & overloading
Go does not provide Generics
Polymorphism via interfaces
Interfaces in Go
Interfaces are just sets of methods
Interfaces define behavior (duck typing)
"If something can do this, then it can be used here”
Interfaces in Go
type Rental struct { name string feePerDay float64 periodLength int RentalPeriod}
type RentalPeriod int
const ( Days RentalPeriod = iota Weeks Months)
func (p RentalPeriod) ToDays() int { switch p { case Weeks: return 7 case Months: return 30 default: return 1 }}
Interfaces in Go
func (r Rental) Cost() float64 { return r.feePerDay * float64(r.ToDays()*r.periodLength)}
// Interface Declarationtype Coster interface {type Coster interface { Cost() float64Cost() float64}}
func DisplayCost(c Coster) {func DisplayCost(c Coster) { fmt.Println("cost: ", c.Cost())}
// In Actionfunc main() { shirt := Item{name: "Men's Slim-Fit Shirt", price: 25000, quantity: 3} video := Rental{"Interstellar", 1000, 3, Days}
fmt.Printf("[%v] ", shirt.name) DisplayCost(shirt)DisplayCost(shirt) fmt.Printf("[%v] ", video.name) DisplayCost(video)DisplayCost(video)} Run
Interfaces in Go 2
// Interface Declarationtype Coster interface { Cost() float64}
// Itemstype Items []Costertype Items []Coster
func main() { shirt := Item{name: "Men's Slim-Fit Shirt", price: 25000, quantity: 3} video := Rental{"Interstellar", 1000, 3, Days} eventShoes := DiscountItem{ Item{name: "Women's Walking Shoes", price: 50000, quantity: 3}, 10.00, } items := Items{shirt, video, eventShoes} order := Order{Items: items, taxRate: 10.00}
DisplayCost(shirt) DisplayCost(video) DisplayCost(eventShoes) DisplayCost(items) DisplayCost(order)}
Run
Satisfying Interface of Other Package
fmt.Println
func Println(a ...interface{}) (n int, err error) { return Fprintln(os.Stdout, a...)}
func Fprintln(w io.Writer, a ...interface{}) (n int, err error) { ... // print using Stringer interface ... return}
fmt.Stringer
type Stringer interface { String() string}
Satisfying Interface of Other Package
// Stringerfunc (t Item) String() string { return fmt.Sprintf("[%s] %.0f", t.name, t.Cost())}
func (t Rental) String() string { return fmt.Sprintf("[%s] %.0f", t.name, t.Cost())}
func (t DiscountItem) String() string { return fmt.Sprintf("%s => %.0f(%.0f%s DC)", t.Item.String(), t.Cost(), t.discountRate, "%")}
func (t Items) String() string { return fmt.Sprintf("%d items. total: %.0f", len(t), t.Cost())}
func (o Order) String() string { return fmt.Sprintf("Include Tax: %.0f(tax rate: %.2f%s)", o.Cost(), o.taxRate, "%")}
Satisfying Interface of Other Package
func main() { shirt := Item{name: "Men's Slim-Fit Shirt", price: 25000, quantity: 3} video := Rental{"Interstellar", 1000, 3, Days} eventShoes := DiscountItem{ Item{name: "Women's Walking Shoes", price: 50000, quantity: 3}, 10.00, } items := Items{shirt, video, eventShoes} order := Order{Items: items, taxRate: 10.00}
fmt.Println(shirt) fmt.Println(video) fmt.Println(eventShoes) fmt.Println(items) fmt.Println(order)} Run
Deep into Go's Standard Library
io.Writer interface
// http://godoc.org/io#Writertype Writer interface { Write(p []byte) (n int, err os.Error)}
fmt.Fprintln function
func Fprintln(w io.Writer, a ...interface{}) (n int, err error)
The Power of Interfaces
In handle function, just write to io.Writer object
func handle(w io.Writer, msg string) { fmt.Fprintln(w, msg)}
The os.Stdout can be used for io.Writer.
func main() { msg := []string{"hello", "world", "this", "is", "an", "example", "of", "io.Writer"} for _, s := range msg { time.Sleep(100 * time.Millisecond) handle(os.Stdout, s)handle(os.Stdout, s) }} Run
The Power of Interfaces
The http.ResponseWriter can be used for io.Writer.
localhost:4000/hello-world (http://localhost:4000/hello-world)
localhost:4000/this-is-an-example-of-io.Writer (http://localhost:4000/this-is-an-example-of-io.Writer)
func handle(w io.Writer, msg string) { fmt.Fprintln(w, msg)}
func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { handle(w, r.URL.Path[1:])handle(w, r.URL.Path[1:]) }) fmt.Println("start listening on port 4000") http.ListenAndServe(":4000", nil)} Run
Go is Object-Oriented