loading...
Cover image for Design Patterns: Flyweight Pattern ⚖️

Design Patterns: Flyweight Pattern ⚖️

shubhamzanwar profile image Shubham Zanwar Originally published at shubhamzanwar.com ・3 min read

Photo by Johann Walter Bantz on Unsplash

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.

Let's understand this pattern by taking the case of a sneaker store 👟

Sneaker Store

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! 🗃

You have the following data points on every single pair:

  • Name
  • Size
  • Color
  • Brand
  • Price
  • Materials Used
  • Description
  • Sample Image
  • laced

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 😱

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.

If all of this is confusing, don't worry. Taking a look at the code should make things easier 😉

type SneakerDetails struct {
    name        string
    brand       string
    price       float64
    materials   []string
    description string
    image       string
    laced       bool
}

var NikeAirForceDetails = SneakerDetails{
    name:        "Nike Air Force 1",
    brand:       "Nike",
    price:       144.99,
    materials:   []string{"leather", "rubber"},
    description: "Originally released in 1982, the Nike Air Force 1 was the first Nike model to feature Air technology.",
    image:       "https://via.placeholder.com/50",
    laced:       true,
}

var AdidasSuperstarDetails = SneakerDetails{
    name:        "Adidas Superstar",
    brand:       "Adidas",
    price:       85.99,
    materials:   []string{"leather", "rubber"},
    description: "Originally made for basketball courts in the '70s.",
    image:       "https://via.placeholder.com/50",
    laced:       true,
}
Enter fullscreen mode Exit fullscreen mode
const (
    NikeAirForce = "Nike Air Force 1"
    AdidasSuperstar = "Adidas Superstar"
)

var DetailsMap = map[string]SneakerDetails{
    NikeAirForce:    NikeAirForceDetails,
    AdidasSuperstar: AdidasSuperstarDetails,
}

func getShoeDetails(shoeType string) SneakerDetails {
    return DetailsMap[shoeType]
}
Enter fullscreen mode Exit fullscreen mode
type Sneaker struct {
    shoeType string
    size int
    color string
}

func (s *Sneaker) describe() string {
    details := getShoeDetails(s.shoeType)
    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)
}
Enter fullscreen mode Exit fullscreen mode

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 🤔

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.

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.

Let's see this in action

func main() {
    sneaker1 := Sneaker{
        shoeType: NikeAirForce,
        size:     9,
        color:    "white",
    }

    sneaker2 := Sneaker{
        shoeType: AdidasSuperstar,
        size:     8,
        color:    "black",
    }

    fmt.Println(sneaker1.describe())
    fmt.Println(sneaker2.describe())
}
Enter fullscreen mode Exit fullscreen mode

This should give the following output:

The Nike Air Force 1 is of size 9, color white and costs 144.99. The Manufacturer is Nike
The Adidas Superstar is of size 8, color black and costs 85.99. The Manufacturer is Adidas
Enter fullscreen mode Exit fullscreen mode

That's about it for the Flyweight pattern! 😁

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

You can find all the code for this tutorial on this this github repo

Cheers ☕️

Discussion

pic
Editor guide