Slide 1

Slide 1 text

understanding the interface @francesc

Slide 2

Slide 2 text

what is an interface?

Slide 3

Slide 3 text

"In object-oriented programming, a protocol or interface is a common means for unrelated objects to communicate with each other" - wikipedia

Slide 4

Slide 4 text

"In object-oriented programming, a protocol or interface is a common means for unrelated objects to communicate with each other" - wikipedia

Slide 5

Slide 5 text

"In object-oriented programming, a protocol or interface is a common means for unrelated objects to communicate with each other" - wikipedia

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

what is a Go interface?

Slide 10

Slide 10 text

abstract types concrete types

Slide 11

Slide 11 text

concrete types in Go - they describe a memory layout - behavior attached to data through methods int32 int64 int16 int8 type Number int func (n Number) Positive() bool { return n > 0 }

Slide 12

Slide 12 text

int *os.File *strings.Reader *gzip.Writer []bool

Slide 13

Slide 13 text

type Positiver interface { Positive() bool } abstract types in Go - they describe behavior - they define a set of methods, without specifying the receiver io.Reader io.Writer fmt.Stringer

Slide 14

Slide 14 text

type Reader interface { Read(b []byte) (int, error) } type Writer interface { Write(b []byte) (int, error) } two interfaces

Slide 15

Slide 15 text

int *os.File *strings.Reader *gzip.Writer []bool io.Reader io.Writer

Slide 16

Slide 16 text

type ReadWriter interface { Read(b []byte) (int, error) Write(b []byte) (int, error) } union of interfaces

Slide 17

Slide 17 text

type ReadWriter interface { Reader Writer } union of interfaces

Slide 18

Slide 18 text

int *os.File *strings.Reader *gzip.Writer []bool io.Reader io.Writer io.ReadWriter

Slide 19

Slide 19 text

int *os.File *strings.Reader *gzip.Writer []bool io.Reader io.Writer io.ReadWriter ?

Slide 20

Slide 20 text

interface{}

Slide 21

Slide 21 text

“interface{} says nothing” - Rob Pike in his Go Proverbs

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

why do we use interfaces?

Slide 24

Slide 24 text

- writing generic algorithms - hiding implementation details - providing interception points why do we use interfaces?

Slide 25

Slide 25 text

a) func WriteTo(f *os.File) error b) func WriteTo(w io.ReadWriteCloser) error c) func WriteTo(w io.Writer) error d) func WriteTo(w interface{}) error what function do you prefer?

Slide 26

Slide 26 text

Cons: ● how would you test it? ● what if you want to write to memory? Pros: ● ? a) func WriteTo(f *os.File) error

Slide 27

Slide 27 text

Cons: ● how do you even write to interface{}? ● probably requires runtime checks Pros: ● you can write really bad code d) func WriteTo(w interface{}) error

Slide 28

Slide 28 text

Which ones does WriteTo really need? - Write - Read - Close b) func WriteTo(w io.ReadWriteCloser) error c) func WriteTo(w io.Writer) error

Slide 29

Slide 29 text

“The bigger the interface, the weaker the abstraction” - Rob Pike in his Go Proverbs

Slide 30

Slide 30 text

“Be conservative in what you do, be liberal in what you accept from others” - Robustness Principle

Slide 31

Slide 31 text

“Be conservative in what you send, be liberal in what you accept” - Robustness Principle

Slide 32

Slide 32 text

Abstract Data Types

Slide 33

Slide 33 text

Abstract Data Types Mathematical model for data types Defined by its behavior in terms of: - possible values, - possible operations on data of this type, - and the behavior of these operations

Slide 34

Slide 34 text

top(push( , ))= X S X

Slide 35

Slide 35 text

pop(push( , ))= S X S

Slide 36

Slide 36 text

empty(new()) not empty(push(S, X))

Slide 37

Slide 37 text

Axioms: top(push(S, X)) = X pop(push(S, X)) = S empty(new()) !empty(push(S, X)) Example: stack ADT

Slide 38

Slide 38 text

type Stack interface { Push(v interface{}) Stack Pop() Stack Empty() bool } a Stack interface

Slide 39

Slide 39 text

func Size(s Stack) int { if s.Empty() { return 0 } return Size(s.Pop()) + 1 } algorithms on Stack

Slide 40

Slide 40 text

type Interface interface { Less(i, j int) bool Swap(i, j int) Len() int } a sortable interface

Slide 41

Slide 41 text

func Sort(s Interface) func Stable(s Interface) func IsSorted(s Interface) bool algorithms on sortable

Slide 42

Slide 42 text

type Reader interface { Read(b []byte) (int, error) } type Writer interface { Write(b []byte) (int, error) } remember Reader and Writer?

Slide 43

Slide 43 text

func Fprintln(w Writer, ar ...interface{}) (int, error) func Fscan(r Reader, a ...interface{}) (int, error) func Copy(w Writer, r Reader) (int, error) algorithms on Reader and Writer

Slide 44

Slide 44 text

is this enough?

Slide 45

Slide 45 text

No content

Slide 46

Slide 46 text

write generic algorithms on interfaces

Slide 47

Slide 47 text

“Be conservative in what you send, be liberal in what you accept” - Robustness Principle

Slide 48

Slide 48 text

a) func New() *os.File b) func New() io.ReadWriteCloser c) func New() io.Writer d) func New() interface{} what function do you prefer?

Slide 49

Slide 49 text

func New() *os.File

Slide 50

Slide 50 text

“Be conservative in what you send, be liberal in what you accept” - Robustness Principle

Slide 51

Slide 51 text

“Return concrete types, receive interfaces as parameters” - Robustness Principle applied to Go (me)

Slide 52

Slide 52 text

unless

Slide 53

Slide 53 text

Use interfaces to hide implementation details: - decouple implementation from API - easily switch between implementations / or provide multiple ones Hiding implementation details

Slide 54

Slide 54 text

context.Context

Slide 55

Slide 55 text

satisfying the Context interface context Context

Slide 56

Slide 56 text

satisfying the Context interface emptyCtx cancelCtx timerCtx valueCtx Context

Slide 57

Slide 57 text

interfaces hide implementation details

Slide 58

Slide 58 text

call dispatch

Slide 59

Slide 59 text

f.Do()

Slide 60

Slide 60 text

call dispatch Concrete types: static - known at compilation - very efficient - can’t intercept Abstract types: dynamic - unknown at compilation - less efficient - easy to intercept

Slide 61

Slide 61 text

type Client struct { Transport RoundTripper … } type RoundTripper interface { RoundTrip(*Request) (*Response, error) } interfaces: dynamic dispatch of calls

Slide 62

Slide 62 text

http.DefaultTransport http.Client

Slide 63

Slide 63 text

interfaces: dynamic dispatch of calls type headers struct { rt http.RoundTripper v map[string]string } func (h headers) RoundTrip(r *http.Request) *http.Response { for k, v := range h.v { r.Header.Set(k, v) } return h.rt.RoundTrip(r) }

Slide 64

Slide 64 text

interfaces: dynamic dispatch of calls c := &http.Client{ Transport: headers{ rt: http.DefaultTransport, v: map[string]string{“foo”: “bar”}, }, } res, err := c.Get(“http://golang.org”)

Slide 65

Slide 65 text

headers http.DefaultTransport http.Client

Slide 66

Slide 66 text

chaining interfaces

Slide 67

Slide 67 text

const input = `H4sIAAAAAAAA/3qyd8GT3WueLt37fk/Ps46JT/d1vFw942nrlqezFzzZsQskMmfFi/0zX7b3cAECA AD//0G6Zq8rAAAA` var r io.Reader = strings.NewReader(input) r = base64.NewDecoder(base64.StdEncoding, r) r, err := gzip.NewReader(r) if err != nil {log.Fatal(err) } io.Copy(os.Stdout, r) Chaining interfaces

Slide 68

Slide 68 text

*strings.Reader H4sIAAAAAAAA/3qyd8GT3WueLt37fk/Ps46JT/d1vF w942nrlqezFzzZsQskMmfFi/0zX7b3cAECAAD//0G6 Zq8rAAAA *base64.Decoder *gzip.Reader *os.File io.Copy 你们好,我很高兴 因为我在这里

Slide 69

Slide 69 text

interfaces are interception points

Slide 70

Slide 70 text

- writing generic algorithms - hiding implementation details - providing interception points why do we use interfaces?

Slide 71

Slide 71 text

so … what’s new?

Slide 72

Slide 72 text

implicit interface satisfaction

Slide 73

Slide 73 text

no “implements”

Slide 74

Slide 74 text

funcdraw

Slide 75

Slide 75 text

package parse func Parse(s string) *Func type Func struct { … } func (f *Func) Eval(x float64) float64 Two packages: parse and draw

Slide 76

Slide 76 text

Two packages: parse and draw package draw import “.../parse” func Draw(f *parse.Func) image.Image { for x := minX; x < maxX; x += incX { paint(x, f.Eval(y)) } … }

Slide 77

Slide 77 text

funcdraw package draw package parse

Slide 78

Slide 78 text

funcdraw with explicit satisfaction package draw package common package parse

Slide 79

Slide 79 text

funcdraw with implicit satisfaction package draw package parse

Slide 80

Slide 80 text

Two packages: parse and draw package draw import “.../parse” func Draw(f *parse.Func) image.Image { for x := minX; x < maxX; x += incX { paint(x, f.Eval(y)) } … }

Slide 81

Slide 81 text

Two packages: parse and draw package draw type Evaler interface { Eval(float64) float64 } func Draw(e Evaler) image.Image { for x := minX; x < maxX; x += incX { paint(x, e.Eval(y)) } … }

Slide 82

Slide 82 text

interfaces can break dependencies

Slide 83

Slide 83 text

define interfaces where you use them

Slide 84

Slide 84 text

But, how do I know what satisfies what, then?

Slide 85

Slide 85 text

guru a tool for answering questions about Go source code.

Slide 86

Slide 86 text

http://golang.org/s/using-guru

Slide 87

Slide 87 text

the super power of Go interfaces

Slide 88

Slide 88 text

type assertions

Slide 89

Slide 89 text

func do(v interface{}) { i := v.(int) // will panic if v is not int i, ok := v.(int) // will return false } type assertions from interface to concrete type

Slide 90

Slide 90 text

func do(v interface{}) { select v.(type) { case int: fmt.Println(“got int %d”, v) Default: } } type assertions from interface to concrete type

Slide 91

Slide 91 text

func do(v interface{}) { select t := v.(type) { case int: // t is of type int fmt.Println(“got int %d”, t) default: // t is of type interface{} fmt.Println(“not sure what type”) } } type assertions from interface to concrete type

Slide 92

Slide 92 text

avoid abstract to concrete assertions

Slide 93

Slide 93 text

func do(v interface{}) { s := v.(fmt.Stringer) // might panic s, ok := v.(fmt.Stringer) // might return false } type assertions from interface to interface

Slide 94

Slide 94 text

func do(v interface{}) { select v.(type) { case fmt.Stringer(): fmt.Println(“got Stringer %d”, v) Default: } } runtime checks interface to concrete type

Slide 95

Slide 95 text

func do(v interface{}) { select s := v.(type) { case fmt.Stringer: // s is of type int fmt.Println(s.String()) default: // t is of type interface{} fmt.Println(“not sure what type”) } } runtime checks interface to concrete type

Slide 96

Slide 96 text

Many packages check whether a type satisfies an interface: - fmt.Stringer - json.Marshaler/Unmarhsaler - ... and adapt their behavior accordingly. type assertions as extension mechanism

Slide 97

Slide 97 text

use type assertions to extend behaviors

Slide 98

Slide 98 text

Go Proverb Dave Cheney - GopherCon 2016 Don’t just check errors, handle them gracefully

Slide 99

Slide 99 text

type Context interface { Done() <-chan struct{} Err() error Deadline() (deadline time.Time, ok bool) Value(key interface{}) interface{} } var Canceled, DeadlineExceeded error the Context interface

Slide 100

Slide 100 text

var Canceled = errors.New("context canceled") errors in context

Slide 101

Slide 101 text

var Canceled = errors.New("context canceled") var DeadlineExceeded error = deadlineExceededError{} errors in context

Slide 102

Slide 102 text

var Canceled = errors.New("context canceled") var DeadlineExceeded error = deadlineExceededError{} errors in context

Slide 103

Slide 103 text

var Canceled = errors.New("context canceled") var DeadlineExceeded error = deadlineExceededError{} type deadlineExceededError struct{} func (deadlineExceededError) Error() string { return "..." } func (deadlineExceededError) Timeout() bool { return true } func (deadlineExceededError) Temporary() bool { return true } errors in context

Slide 104

Slide 104 text

var Canceled = errors.New("context canceled") var DeadlineExceeded error = deadlineExceededError{} type deadlineExceededError struct{} func (deadlineExceededError) Error() string { return "..." } func (deadlineExceededError) Timeout() bool { return true } func (deadlineExceededError) Temporary() bool { return true } errors in context

Slide 105

Slide 105 text

var Canceled = errors.New("context canceled") var DeadlineExceeded error = deadlineExceededError{} type deadlineExceededError struct{} func (deadlineExceededError) Error() string { return "..." } func (deadlineExceededError) Timeout() bool { return true } func (deadlineExceededError) Temporary() bool { return true } errors in context

Slide 106

Slide 106 text

if tmp, ok := err.(interface { Temporary() bool }); ok { if tmp.Temporary() { // retry } else { // report } } errors in context

Slide 107

Slide 107 text

use type assertions to classify errors

Slide 108

Slide 108 text

Adding methods to an interface breaks backwards compatibility. type ResponseWriter interface { Header() Header Write([]byte) (int, error) WriteHeader(int) } How could you add one more method without breaking anyone’s code? type assertions as evolution mechanism

Slide 109

Slide 109 text

Step 1: add the method to your concrete type implementations Step 2: define an interface containing the new method Step 3: document it type assertions as evolution mechanism

Slide 110

Slide 110 text

type Pusher interface { Push(target string, opts *PushOptions) error } func handler(w http.ResponseWriter, r *http.Request) { if p, ok := w.(http.Pusher); ok { p.Push(“style.css”, nil) } } http.Pusher

Slide 111

Slide 111 text

use type assertions to maintain compatibility

Slide 112

Slide 112 text

In conclusion

Slide 113

Slide 113 text

Interfaces provide: - generic algorithms - hidden implementation - interception points In conclusion

Slide 114

Slide 114 text

Interfaces provide: - generic algorithms - hidden implementation - interception points Implicit satisfaction: - break dependencies In conclusion

Slide 115

Slide 115 text

Interfaces provide: - generic algorithms - hidden implementation - interception points implicit satisfaction: - break dependencies Type assertions: - to extend behaviors - to classify errors - to maintain compatibility In conclusion

Slide 116

Slide 116 text

谢谢 Thanks, @francesc