-
Notifications
You must be signed in to change notification settings - Fork 86
/
Copy pathstrategy.go
148 lines (128 loc) · 4.38 KB
/
strategy.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package main
import "fmt"
// struct is the way go group some state
// and encapsulation is done by first letter
// upper case letters indicates public, lower case letters indicates private
// this encapsulation is valid on the whole package(further discussions)
// We talk more about POO in another day.
// A Customer is a simple struct wiht name and fidelity points
type Customer struct {
name string
fidelity int
}
// A LineItem represents a item in a cart
type LineItem struct {
product string
quantity int
price float64
}
// Methods with receiver(item in function below) are binded with a struct
// Total returns quantity of items mutiplied by price
func (item LineItem) Total() float64 {
return float64(item.quantity) * item.price
}
// String is a better representation of an item
func (item LineItem) String() string {
return fmt.Sprintf("<LineItem product:%s quantity:%d price:%.2f>", item.product, item.quantity, item.price)
}
// Order is the relationship of a costumer, the cart and possible promo
type Order struct {
ctm Customer
cart []LineItem
promo Promotion
}
// Total is the sum of items purchased
func (order Order) Total() float64 {
total := 0.0
for _, item := range order.cart {
total += item.Total()
}
return total
}
// Due calculate order value considering discount
func (order Order) Due() float64 {
discount := 0.0
if order.promo != nil {
discount = order.promo.Discount(order)
}
return order.Total() - discount
}
// String returns the order representation when is printed
func (order Order) String() string {
return fmt.Sprintf("<Order total: %.2f due: %.2f>", order.Total(), order.Due())
}
// Promotion interface that had a method to calculate some discount
type Promotion interface {
Discount(Order) float64
}
// FidelityPromo is a concrete implementation of a Promotion
type FidelityPromo struct{}
// Discount is the method that should be implemented by the concrete implementation of a promotion
func (FidelityPromo) Discount(o Order) float64 {
if o.ctm.fidelity >= 1000 {
return o.Total() * 0.05
}
return 0.0
}
// BulkItemPromo is a concrete implementation of a Promotion
type BulkItemPromo struct{}
// Discount is the method that should be implemented by the concrete implementation of a promotion
func (b BulkItemPromo) Discount(o Order) float64 {
discount := 0.0
for _, item := range o.cart {
if item.quantity >= 20 {
discount += item.Total() * .1
}
}
return discount
}
// LargeOrderPromo is a concrete implementation of a Promotion
type LargeOrderPromo struct{}
// Discount is the method that should be implemented by the concrete implementation of a promotion
func (l LargeOrderPromo) Discount(o Order) float64 {
set := map[string]bool{}
for _, item := range o.cart {
set[item.product] = true
}
if len(set) >= 10 {
return o.Total() * 0.07
}
return 0.0
}
func main() {
// This example is the same present in excellent python book, Fluent Python, writted by Luciano Ramalho
// I really love this book <3 !
// There is a better way to implement this design pattern, but to make the benefits clearer, today, I have implemented the "traditional"
joe := Customer{"John Doe", 0}
ann := Customer{"Ann Smith", 1100}
cart := []LineItem{
LineItem{"banana", 4, 0.50},
LineItem{"apple", 10, 1.50},
LineItem{"watermellon", 5, 5.00},
}
// Joe don't have fidelity points, he don't win a discount
fmt.Printf("\n%s have %d fidelity points\n", joe.name, joe.fidelity)
fmt.Println(Order{joe, cart, FidelityPromo{}})
// Ann have 1100 fidelity points, this guarantees a discount
fmt.Printf("\n%s have %d fidelity points\n", ann.name, ann.fidelity)
fmt.Println(Order{ann, cart, FidelityPromo{}})
// 30 bananas??
bananaCart := []LineItem{
LineItem{"banana", 30, .5},
LineItem{"apple", 10, 1.5},
}
// Ok, many items guarantees dicount on BulkItemPromo
fmt.Printf("\n%s buy many items of the same product %s\n", joe.name, bananaCart)
fmt.Println(Order{joe, bananaCart, BulkItemPromo{}})
// 10 random items
largeOrder := []LineItem{}
for i := 0; i < 10; i++ {
largeOrder = append(largeOrder, LineItem{string(65 + i), 1, 1.0})
}
// only to check LargeOrderPromo
fmt.Printf("\n%s represents an order with many distinct items %s", joe.name, largeOrder)
fmt.Println(Order{joe, largeOrder, LargeOrderPromo{}})
// only 3 distinct items, no discount here!
fmt.Println("\nonly 3 distinct items, no discount here!")
fmt.Println(Order{joe, cart, LargeOrderPromo{}})
}