refactor: stringer interface
This commit is contained in:
		| @@ -9,43 +9,20 @@ package main | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"strconv" |  | ||||||
| 	"time" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type book struct { | type book struct { | ||||||
| 	product | 	product | ||||||
| 	published interface{} | 	published timestamp | ||||||
| } | } | ||||||
|  |  | ||||||
| // book satisfies the fmt.Stringer | // book satisfies the fmt.Stringer | ||||||
| func (b *book) String() string { | func (b *book) String() string { | ||||||
| 	p := format(b.published) |  | ||||||
|  |  | ||||||
| 	// product.String() has a pointer receiver. | 	// product.String() has a pointer receiver. | ||||||
| 	// That's why you need to manually take the product's address here. | 	// That's why you need to manually take the product's address here. | ||||||
| 	// | 	// | ||||||
| 	// If you pass: "b.product", Go would pass it as a copy to Sprintf. | 	// If you pass: "b.product", Go would pass it as a copy to Sprintf. | ||||||
| 	// In that case, Go can't deference b.product automatically. | 	// In that case, Go can't deference b.product automatically. | ||||||
| 	// It's because: b.product would be different value—a copy. | 	// It's because: b.product would be different value—a copy. | ||||||
| 	return fmt.Sprintf("%s - (%v)", &b.product, p) | 	return fmt.Sprintf("%s - (%s)", &b.product, b.published) | ||||||
| } |  | ||||||
|  |  | ||||||
| func format(v interface{}) string { |  | ||||||
| 	var t int |  | ||||||
|  |  | ||||||
| 	switch v := v.(type) { |  | ||||||
| 	case int: |  | ||||||
| 		t = v |  | ||||||
| 	case string: |  | ||||||
| 		t, _ = strconv.Atoi(v) |  | ||||||
| 	default: |  | ||||||
| 		return "unknown" |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Mon Jan 2 15:04:05 -0700 MST 2006 |  | ||||||
| 	const layout = "2006/01" |  | ||||||
|  |  | ||||||
| 	u := time.Unix(int64(t), 0) |  | ||||||
| 	return u.Format(layout) |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -11,9 +11,9 @@ import "fmt" | |||||||
|  |  | ||||||
| func main() { | func main() { | ||||||
| 	store := list{ | 	store := list{ | ||||||
| 		&book{product{"moby dick", 10}, 118281600}, | 		&book{product{"moby dick", 10}, toTimestamp(118281600)}, | ||||||
| 		&book{product{"odyssey", 15}, "733622400"}, | 		&book{product{"odyssey", 15}, toTimestamp("733622400")}, | ||||||
| 		&book{product{"hobbit", 25}, nil}, | 		&book{product{"hobbit", 25}, unknown}, | ||||||
| 		&puzzle{product{"rubik's cube", 5}}, | 		&puzzle{product{"rubik's cube", 5}}, | ||||||
| 		&game{product{"minecraft", 20}}, | 		&game{product{"minecraft", 20}}, | ||||||
| 		&game{product{"tetris", 5}}, | 		&game{product{"tetris", 5}}, | ||||||
| @@ -26,4 +26,8 @@ func main() { | |||||||
| 	// but the Print function can print it. | 	// but the Print function can print it. | ||||||
| 	// it's because, the list satisfies the stringer. | 	// it's because, the list satisfies the stringer. | ||||||
| 	fmt.Print(store) | 	fmt.Print(store) | ||||||
|  |  | ||||||
|  | 	// timestamp is useful even if it's zero. | ||||||
|  | 	var ts timestamp | ||||||
|  | 	fmt.Println(ts) | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										51
									
								
								interfaces/09-stringer/timestamp.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								interfaces/09-stringer/timestamp.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | |||||||
|  | // For more tutorials: https://blog.learngoprogramming.com | ||||||
|  | // | ||||||
|  | // Copyright © 2018 Inanc Gumus | ||||||
|  | // Learn Go Programming Course | ||||||
|  | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ | ||||||
|  | // | ||||||
|  |  | ||||||
|  | package main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"strconv" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Mon Jan 2 15:04:05 -0700 MST 2006 | ||||||
|  | const layout = "2006/01" | ||||||
|  |  | ||||||
|  | // unknown is the zero value of a timestamp. | ||||||
|  | var unknown = timestamp(time.Time{}) | ||||||
|  |  | ||||||
|  | // Timestamp prints timestamps, it's a stringer. | ||||||
|  | // Timestamp is useful even if it's zero. | ||||||
|  | type timestamp time.Time | ||||||
|  |  | ||||||
|  | // String makes the timestamp a stringer. | ||||||
|  | func (ts timestamp) String() string { | ||||||
|  | 	t := time.Time(ts) | ||||||
|  |  | ||||||
|  | 	if t.IsZero() { | ||||||
|  | 		return "unknown" | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return t.Format(layout) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // toTimestamp was "book.format" before. | ||||||
|  | // Now it returns a timestamp value depending on the type of `v`. | ||||||
|  | func toTimestamp(v interface{}) timestamp { | ||||||
|  | 	var t int | ||||||
|  |  | ||||||
|  | 	switch v := v.(type) { | ||||||
|  | 	case int: | ||||||
|  | 		t = v | ||||||
|  | 	case string: | ||||||
|  | 		t, _ = strconv.Atoi(v) | ||||||
|  | 	default: | ||||||
|  | 		return unknown | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return timestamp(time.Unix(int64(t), 0)) | ||||||
|  | } | ||||||
| @@ -8,57 +8,14 @@ | |||||||
| package main | package main | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"encoding/json" |  | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"strconv" |  | ||||||
| 	"time" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type book struct { | type book struct { | ||||||
| 	product | 	product | ||||||
| 	Published interface{} | 	Published timestamp | ||||||
| } | } | ||||||
|  |  | ||||||
| func (b book) String() string { | func (b book) String() string { | ||||||
| 	p := format(b.Published) | 	return fmt.Sprintf("%s - (%s)", &b.product, b.Published) | ||||||
| 	return fmt.Sprintf("%s - (%v)", &b.product, p) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // MarshalJSON is an implementation of the json.Marshaler interface. |  | ||||||
| // json.Marshal and Decode call this method. |  | ||||||
| func (b *book) MarshalJSON() ([]byte, error) { |  | ||||||
| 	// Calling Marshal creates an infinite loop because Marshal calls |  | ||||||
| 	// MarshalJSON again and again. |  | ||||||
| 	// |  | ||||||
| 	// Declaring a clone type without MarshalJSON fixes the problem. |  | ||||||
| 	type jbook book |  | ||||||
|  |  | ||||||
| 	// book is convertable to jbook |  | ||||||
| 	// jbook's underlying type is book. |  | ||||||
| 	jb := (*jbook)(b) |  | ||||||
|  |  | ||||||
| 	// Customize the formatting of the published field. |  | ||||||
| 	// This will result in the published field of the resulting json. |  | ||||||
| 	jb.Published = format(b.Published) |  | ||||||
|  |  | ||||||
| 	return json.Marshal(jb) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func format(v interface{}) string { |  | ||||||
| 	var t int |  | ||||||
|  |  | ||||||
| 	switch v := v.(type) { |  | ||||||
| 	case int: |  | ||||||
| 		t = v |  | ||||||
| 	case string: |  | ||||||
| 		t, _ = strconv.Atoi(v) |  | ||||||
| 	default: |  | ||||||
| 		return "unknown" |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Mon Jan 2 15:04:05 -0700 MST 2006 |  | ||||||
| 	const layout = "2006/01" |  | ||||||
|  |  | ||||||
| 	u := time.Unix(int64(t), 0) |  | ||||||
| 	return u.Format(layout) |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -14,22 +14,85 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| func main() { | func main() { | ||||||
| 	store := list{ | 	// store := list{ | ||||||
| 		&book{product{"moby dick", 10}, 118281600}, | 	// 	&book{product{"moby dick", 10}, 118281600}, | ||||||
| 		&book{product{"odyssey", 15}, "733622400"}, | 	// 	&book{product{"odyssey", 15}, "733622400"}, | ||||||
| 		&book{product{"hobbit", 25}, nil}, | 	// 	&book{product{"hobbit", 25}, nil}, | ||||||
| 		&puzzle{product{"rubik's cube", 5}}, | 	// 	&puzzle{product{"rubik's cube", 5}}, | ||||||
| 		&game{product{"minecraft", 20}}, | 	// 	&game{product{"minecraft", 20}}, | ||||||
| 		&game{product{"tetris", 5}}, | 	// 	&game{product{"tetris", 5}}, | ||||||
| 		&toy{product{"yoda", 150}}, | 	// 	&toy{product{"yoda", 150}}, | ||||||
| 	} | 	// } | ||||||
|  |  | ||||||
| 	out, err := json.MarshalIndent(store, "", "\t") | 	// out, err := json.MarshalIndent(store, "", "\t") | ||||||
| 	if err != nil { | 	// if err != nil { | ||||||
| 		log.Fatalln(err) | 	// 	log.Fatalln(err) | ||||||
| 	} | 	// } | ||||||
| 	fmt.Println(string(out)) | 	// fmt.Println(string(out)) | ||||||
|  |  | ||||||
| 	// store.discount(.5) | 	// store.discount(.5) | ||||||
| 	// fmt.Print(store) | 	// fmt.Print(store) | ||||||
|  |  | ||||||
|  | 	encode() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func encode() { | ||||||
|  |  | ||||||
|  | 	books := []*book{ | ||||||
|  | 		{product{"moby dick", 10}, toTime(118281600)}, | ||||||
|  | 		{product{"odyssey", 15}, toTime("733622400")}, | ||||||
|  | 		{product{"hobbit", 25}, zeroTimestamp}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, b := range books { | ||||||
|  | 		fmt.Println(b) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	out, err := json.MarshalIndent(books, "", "\t") | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatalln(err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	fmt.Println(string(out)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func decode() { | ||||||
|  | 	// books = nil | ||||||
|  | 	out := []byte(`[ | ||||||
|  | 	{ | ||||||
|  | 		"Title": "moby dick", | ||||||
|  | 		"Price": 10, | ||||||
|  | 		"Published": "118281600" | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		"Title": "odyssey", | ||||||
|  | 		"Price": 15, | ||||||
|  | 		"Published": 733622400 | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		"Title": "hobbit", | ||||||
|  | 		"Price": 25, | ||||||
|  | 		"Published": -62135596800 | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		"Title": "poka", | ||||||
|  | 		"Price": 25, | ||||||
|  | 		"Published": "1983/03" | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		"Title": "fuka", | ||||||
|  | 		"Price": 25 | ||||||
|  | 	}	 | ||||||
|  | ]`) | ||||||
|  |  | ||||||
|  | 	var books []*book | ||||||
|  |  | ||||||
|  | 	err := json.Unmarshal(out, &books) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatalln(err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, b := range books { | ||||||
|  | 		fmt.Println(b) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user