Skip to content

Commit 2cb9430

Browse files
committed
adding the flyweight pattern
1 parent 701e69c commit 2cb9430

File tree

4 files changed

+211
-0
lines changed

4 files changed

+211
-0
lines changed

8-flyweight/README.md

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# Design Patterns: Flyweight Pattern ⚖️
2+
3+
The Flyweight design pattern is a structural design pattern commonly used when we want to group similar data for multiple objects. The primary intent of this pattern is to store common data for multiple objects in a single place and hence optimise for memory usage.
4+
5+
Let's understand this pattern by taking the case of a sneaker store 👟
6+
7+
### Sneaker Store
8+
9+
Imagine running an online sneaker store. You've noticed that your customers always have a lot of questions and so, you've maintained every little detail about each pair of sneakers you have on display! 🗃
10+
11+
You have the following data points on **every** single pair:
12+
13+
- Name
14+
- Size
15+
- Color
16+
- Brand
17+
- Price
18+
- Materials Used
19+
- Description
20+
- Sample Image
21+
- laced
22+
23+
You soon realized that for a given _type_ of pair, you're storing the same information again and again. Take the case of the Nike Air Force 1. For any two pairs, the only thing different is the `size` and the `color`. Everything else is duplicate information 😱
24+
25+
This definitely isn't the best way to store the details because it's super memory intensive ad costly. To solve this problem, you decide to create an object to store all the _common_ details of the shoe and store only the `size`, `color` and a reference to the "common object" with every shoe instance.
26+
27+
If all of this is confusing, don't worry. Taking a look at the code should make things easier 😉
28+
29+
```go
30+
type SneakerDetails struct {
31+
name string
32+
brand string
33+
price float64
34+
materials []string
35+
description string
36+
image string
37+
laced bool
38+
}
39+
40+
var NikeAirForceDetails = SneakerDetails{
41+
name: "Nike Air Force 1",
42+
brand: "Nike",
43+
price: 144.99,
44+
materials: []string{"leather", "rubber"},
45+
description: "Originally released in 1982, the Nike Air Force 1 was the first Nike model to feature Air technology.",
46+
image: "https://via.placeholder.com/50",
47+
laced: true,
48+
}
49+
50+
var AdidasSuperstarDetails = SneakerDetails{
51+
name: "Adidas Superstar",
52+
brand: "Adidas",
53+
price: 85.99,
54+
materials: []string{"leather", "rubber"},
55+
description: "Originally made for basketball courts in the '70s.",
56+
image: "https://via.placeholder.com/50",
57+
laced: true,
58+
}
59+
```
60+
61+
```go
62+
const (
63+
NikeAirForce = "Nike Air Force 1"
64+
AdidasSuperstar = "Adidas Superstar"
65+
)
66+
67+
var DetailsMap = map[string]SneakerDetails{
68+
NikeAirForce: NikeAirForceDetails,
69+
AdidasSuperstar: AdidasSuperstarDetails,
70+
}
71+
72+
func getShoeDetails(shoeType string) SneakerDetails {
73+
return DetailsMap[shoeType]
74+
}
75+
```
76+
77+
```go
78+
type Sneaker struct {
79+
shoeType string
80+
size int
81+
color string
82+
}
83+
84+
func (s *Sneaker) describe() string {
85+
details := getShoeDetails(s.shoeType)
86+
return fmt.Sprintf("The %s is of size %d, color %s and costs %.2f. The Manufacturer is %s", details.name, s.size, s.color, details.price, details.brand)
87+
}
88+
```
89+
90+
Looking through the code written so far, we've created the details object for each type of shoe in our shop using a common struct. This way, we can add descriptions for more sneakers in the future 🤔
91+
92+
We've also created a map of the shoe type against the respective type. This way, every instance of the `Sneaker` struct can easily access the corresponding details.
93+
94+
Also notice that our `Sneaker` struct now only contains the `size`, `color` along with the `shoeType` that allows us to fetch it's other details.
95+
96+
Let's see this in action
97+
98+
```go
99+
func main() {
100+
sneaker1 := Sneaker{
101+
shoeType: NikeAirForce,
102+
size: 9,
103+
color: "white",
104+
}
105+
106+
sneaker2 := Sneaker{
107+
shoeType: AdidasSuperstar,
108+
size: 8,
109+
color: "black",
110+
}
111+
112+
fmt.Println(sneaker1.describe())
113+
fmt.Println(sneaker2.describe())
114+
}
115+
```
116+
117+
This should give the following output:
118+
119+
```text
120+
The Nike Air Force 1 is of size 9, color white and costs 144.99. The Manufacturer is Nike
121+
The Adidas Superstar is of size 8, color black and costs 85.99. The Manufacturer is Adidas
122+
```
123+
124+
That's about it for the Flyweight pattern! 😁
125+
126+
Note that one added benefit of this pattern (apart from memory efficiency) is also the fact that updating the common details, like price, for a type of sneaker would require you to make the change in only one place - the common details object as opposed to making the change in every individual instance of the sneaker

8-flyweight/details.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package main
2+
3+
type sneakerDetails struct {
4+
name string
5+
brand string
6+
price float64
7+
materials []string
8+
description string
9+
image string
10+
laced bool
11+
}
12+
13+
// NikeAirForceDetails is the details for air force sneaker
14+
var NikeAirForceDetails = sneakerDetails{
15+
name: "Nike Air Force 1",
16+
brand: "Nike",
17+
price: 144.99,
18+
materials: []string{"leather", "rubber"},
19+
description: "Originally released in 1982, the Nike Air Force 1 was the first Nike model to feature Air technology.",
20+
image: "https://via.placeholder.com/50",
21+
laced: true,
22+
}
23+
24+
// AdidasSuperstarDetails is the details for Superstar sneaker
25+
var AdidasSuperstarDetails = sneakerDetails{
26+
name: "Adidas Superstar",
27+
brand: "Adidas",
28+
price: 85.99,
29+
materials: []string{"leather", "rubber"},
30+
description: "Originally made for basketball courts in the '70s.",
31+
image: "https://via.placeholder.com/50",
32+
laced: true,
33+
}
34+
35+
// DetailsMap stores the details against the sneaker type
36+
var DetailsMap = map[string]sneakerDetails{
37+
NikeAirForce: NikeAirForceDetails,
38+
AdidasSuperstar: AdidasSuperstarDetails,
39+
}
40+
41+
func getShoeDetails(shoeType string) sneakerDetails {
42+
return DetailsMap[shoeType]
43+
}

8-flyweight/main.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package main
2+
3+
import "fmt"
4+
5+
const (
6+
// NikeAirForce is the identifier for the air force sneaker
7+
NikeAirForce = "Nike Air Force 1"
8+
// AdidasSuperstar is the identifier for the Superstar sneaker
9+
AdidasSuperstar = "Adidas Superstar"
10+
)
11+
12+
func main() {
13+
sneaker1 := Sneaker{
14+
shoeType: NikeAirForce,
15+
size: 9,
16+
color: "white",
17+
}
18+
19+
sneaker2 := Sneaker{
20+
shoeType: AdidasSuperstar,
21+
size: 8,
22+
color: "black",
23+
}
24+
25+
fmt.Println(sneaker1.describe())
26+
fmt.Println(sneaker2.describe())
27+
}

8-flyweight/sneaker.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package main
2+
3+
import "fmt"
4+
5+
// Sneaker is the struct to hold sneaker specific details
6+
type Sneaker struct {
7+
shoeType string
8+
size int
9+
color string
10+
}
11+
12+
func (s *Sneaker) describe() string {
13+
details := getShoeDetails(s.shoeType)
14+
return fmt.Sprintf("The %s is of size %d, color %s and costs %.2f. The Manufacturer is %s", details.name, s.size, s.color, details.price, details.brand)
15+
}

0 commit comments

Comments
 (0)