Flyweight

Creating a large number of similar objects while sharing some of their state to save memory

In case many objects need to be created and some of their state is the same among them (or groups of them), then Flyweight pattern can be used to save memory.
The idea behind this pattern is to separate the common parts among all types of objects (intrinsic data) and their differences (extrinsic data). Then create a dictionary of some sort to store just one copy of each type and provide a way to set extrinsic data.
The reasoning behind such separation is to save on memory by not storing same data over and over again.
The following code imitates drawing various plants for a game background. Assumption is that there will need to be many plants in the background and the game needs to run smoothly on systems with even limited resources. The current choice of background plants is grass, fern, or pine trees, and name and description will be the same among each type of plant, but each of the plants will have some individual trait that will be shown in the display method.
First, an interface shared by the plants. It shows which items are intrinsic, specific to each class, repetitive items and kind of "baked in" to each of the classes. It also shows which items are extrinsic, size can vary, and another extrinsic item. This can be anything specific to each class, something that is not the cookie-cutter item.
interface IFloraItem {
  readonly name: string; //intrinsic state
  readonly description: string; //intrinsic state
  display(
    size: "small" | "medium" | "large",
    somethingElseExtrinsic: string
  ): void; //extrinsic
    
Next, a parent class that implements the display method, in this particular implementation it is the same among all three classes:
class FloraItemParent implements IFloraItem {
  name: string = "no name";
  description: string = "no description";
  display(
    size: "small" | "medium" | "large",
    somethingElseExtrinsic: string = ""
  ): void {
    console.log(
      `A ${size}-sized ${this.name}, looks like a ${this.description}`
    );
    if (somethingElseExtrinsic.length > 0) {
      console.log("Extra data: ", somethingElseExtrinsic);
    }
  }
}
Next, the three concrete classes of plants:
class Fern extends FloraItemParent {
  constructor() {
    super();
    this.name = "Fern";
    this.description =
      "A plant with a bunch of tiny leaves. Makes a great house plant.";
  }
}

class PineTree extends FloraItemParent {
  constructor() {
    super();
    this.name = "Pine tree";
    this.description = "Tall mighty tree. Watch out for pine cones!";
  }
}

class GrassBlade extends FloraItemParent {
  constructor() {
    super();
    this.name = "blade of grass";
    this.description = "green string.";
  }
}
    
The final piece of the puzzle is a factory that checks the dictionary if a type has already been instantiated, and if not, create a new class of that type and add it to the dictionary. The factory has extra method showDictionary that will show that regardless of how many various classes are created, only one type of each class is actually stored in that dictionary.
class FloraFactory {
  dictionary = {};
  constructor() {
    console.log("\n");
    console.log("FloraFactory is ready. Your options are: fern, pine, grass");
    console.log("\n");
  }
  getFloraItem(itemKey: string): FloraItemParent {
    switch (itemKey) {
      case "fern":
        if (this.dictionary.hasOwnProperty("fern"))
          return this.dictionary["fern"];
        else {
          this.dictionary["fern"] = new Fern();
          return this.dictionary["fern"];
        }
      case "pine":
        if (this.dictionary.hasOwnProperty("pine"))
          return this.dictionary["pine"];
        else {
          this.dictionary["pine"] = new PineTree();
          return this.dictionary["pine"];
        }
      case "grass":
        if (this.dictionary.hasOwnProperty("grass"))
          return this.dictionary["grass"];
        else {
          this.dictionary["grass"] = new GrassBlade();
          return this.dictionary["grass"];
        }

      default:
        break;
    }
    return new GrassBlade();
  }

  showDictionary(showIndividualRecords: boolean = false): void {
    if (showIndividualRecords)
      console.log("----- Current state of the factory:");
    let count = 0;
    for (const prop in this.dictionary) {
      if (showIndividualRecords) {
        console.log(
          `${prop}: ${(this.dictionary[prop] as FloraItemParent).name}`
        );
      }
      count++;
    }
    console.log("Total number of records in the factory:", count);
    console.log();
  }
}


    
Next, test code. The following code initializes 3 large items of each kind, then 3 medium, then 3 small, then 3 random items. After each item is initialized, there is a check that shows how many items are in the dictionary.
let floraFactory = new FloraFactory();
console.log("-- large items --");
let largePine = floraFactory.getFloraItem("pine");
largePine.display(
  "large",
  "a large pine, but seems that it has absolutely no cones"
);
floraFactory.showDictionary();
let largeGrass = floraFactory.getFloraItem("grass");
largeGrass.display("large", "some bug is sitting on this huge blade of grass");
floraFactory.showDictionary();
let largeFern = floraFactory.getFloraItem("fern");
largeFern.display("large", "this fern is so large, it's starting to wilt");
floraFactory.showDictionary(true);

console.log("-- medium items --");
let mediumPine = floraFactory.getFloraItem("pine");
mediumPine.display("medium", "nice and perfect size tree");
floraFactory.showDictionary();
let mediumGrass = floraFactory.getFloraItem("grass");
mediumGrass.display("medium", "this blade of grass is ready to be cut");
floraFactory.showDictionary();
let mediumFern = floraFactory.getFloraItem("fern");
mediumFern.display("medium", "this fern is a part of a huge fern patch");
floraFactory.showDictionary(true);

console.log("-- small items --");
let smallPine = floraFactory.getFloraItem("pine");
smallPine.display(
  "small",
  "this tree could make a great house tree for Christmas"
);
floraFactory.showDictionary();
let smallGrass = floraFactory.getFloraItem("grass");
smallGrass.display("small", "freshly cut and smells great");
floraFactory.showDictionary();
let smallFern = floraFactory.getFloraItem("fern");
smallFern.display("small", "a young fern");
floraFactory.showDictionary(true);

console.log("-- three random items --");
let largePine2 = floraFactory.getFloraItem("pine");
largePine2.display("large", "Another large pine tree!");
floraFactory.showDictionary();
let smallGrass2 = floraFactory.getFloraItem("grass");
smallGrass2.display("small", "ALSO freshly cut");
floraFactory.showDictionary();

let mediumFern2 = floraFactory.getFloraItem("fern");
smallFern.display(
  "medium",
  "not such young fern, but not as bushy as an adult plant"
);
floraFactory.showDictionary(true);

    
Results:
FloraFactory is ready. Your options are: fern, pine, grass


-- large items --
A large-sized Pine tree, looks like a Tall mighty tree. Watch out for pine cones!
Extra data:  a large pine, but seems that it has absolutely no cones
Total number of records in the factory: 1

A large-sized blade of grass, looks like a green string.
Extra data:  some bug is sitting on this huge blade of grass
Total number of records in the factory: 2

A large-sized Fern, looks like a A plant with a bunch of tiny leaves. Makes a great house plant.
Extra data:  this fern is so large, it is starting to wilt
-- Current state of the factory:
pine: Pine tree
grass: blade of grass
fern: Fern
Total number of records in the factory: 3

-- medium items --
A medium-sized Pine tree, looks like a Tall mighty tree. Watch out for pine cones!
Extra data:  nice and perfect size tree
Total number of records in the factory: 3

A medium-sized blade of grass, looks like a green string.
Extra data:  this blade of grass is ready to be cut
Total number of records in the factory: 3

A medium-sized Fern, looks like a A plant with a bunch of tiny leaves. Makes a great house plant.
Extra data:  this fern is a part of a huge fern patch
----- Current state of the factory:
pine: Pine tree
grass: blade of grass
fern: Fern
Total number of records in the factory: 3

-- small items --
A small-sized Pine tree, looks like a Tall mighty tree. Watch out for pine cones!
Extra data:  this tree could make a great house tree for Christmas
Total number of records in the factory: 3

A small-sized blade of grass, looks like a green string.
Extra data:  freshly cut and smells great
Total number of records in the factory: 3

A small-sized Fern, looks like a A plant with a bunch of tiny leaves. Makes a great house plant.
Extra data:  a young fern
----- Current state of the factory:
pine: Pine tree
grass: blade of grass
fern: Fern
Total number of records in the factory: 3

-- three random items --
A large-sized Pine tree, looks like a Tall mighty tree. Watch out for pine cones!
Extra data:  Another large pine tree!
Total number of records in the factory: 3

A small-sized blade of grass, looks like a green string.
Extra data:  ALSO freshly cut
Total number of records in the factory: 3

A medium-sized Fern, looks like a A plant with a bunch of tiny leaves. Makes a great house plant.
Extra data:  not such young fern, but not as bushy as an adult plant
----- Current state of the factory:
pine: Pine tree
grass: blade of grass
fern: Fern
Total number of records in the factory: 3