Flyweight Design Pattern

Flyweight Design Pattern is a Structural Design Pattern and is one of the Gang of Four Design Patterns
is mainly used for increasing performance and minimizing memory usage by reducing the number of objects created and sharing data and objects as much as possible to avoid OutOfMemoryError.

Reuse existing objects and only create if no matching object is found.

all objects created are immutable in nature and cannot be changed once created. hence can be shared and used at multiple places. it stores all existing objects into a collection or cache to reuse them and only creates new ones if required.

Flyweight Design Pattern is preferred when the application requires a large number of objects or object creation is complex and requires lots of memory.

a Flyweight Object has majorly 2 types of variables Intrinsic and Extrinsic, Let's understand them.

Intrinsic Variables
Intrinsic or Invariant variables whose state doesn't change once created or immutable in nature, these variables act as the key for the object to put in the map or cache. they can be reused or transferred throughout the application.

Extrinsic Variables
Extrinsic or Variant variables state can be changed as per requirement. these values pass at the run time as per the requirements. these objects are dependent on application context and hence cannot be reused or transferred

Some real-world examples of Flyweight Design Patterns are.
games have lots of bots or characters controlled by a computer, cars, or trees, they all behave similarly but with only a few differentiations for example color or shape, etc. One way is to create a lot of objects for each bot or car, or the better option is to create a single object with basic features then resue and update it again and again as per the requirement.

package org.wesome.design.patterns;

public interface Fruit {
    void setAppleTaste(AppleTaste appleTaste);

    void setQuantity(int quantity);

    void eatApple();
}
package org.wesome.design.patterns;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
@AllArgsConstructor
public class Apple implements Fruit {
    private final AppleType appleType;
    private AppleTaste appleTaste;
    private int quantity;

    @Override
    public void eatApple() {
        System.out.println("i am eating " + quantity + " " + appleType.name() + " apple, its very " + appleTaste);
    }
}
package org.wesome.design.patterns;

public enum AppleType {
    MACINTOSH, FUJI
}
package org.wesome.design.patterns;

public enum AppleTaste {
    SWEET, JUICY
}
package org.wesome.design.patterns;

import java.util.HashMap;
import java.util.Map;

public class AppleShop {
    public static final Map<AppleType, Fruit> appleShopInventoryCache = new HashMap<>();

    public static Fruit getApple(AppleType appleType, AppleTaste appleTaste, int quantity) {
        Fruit fruit;
        if (appleShopInventoryCache.containsKey(appleType)) {
            /*  object is present in the cache, get the object and update other properties   */
            System.out.println("shop has " + appleType.name() + " apple, serving from stock.");
            fruit = appleShopInventoryCache.get(appleType);
            fruit.setQuantity(quantity);
            fruit.setAppleTaste(appleTaste);
        } else {
            /*  object is not present in the cache, create a new object and add it to the inventory cache */
            System.out.println("shop doesn't have " + appleType.name() + " apple, placing order to farm.");
            fruit = new Apple(appleType, appleTaste, quantity);
            appleShopInventoryCache.put(appleType, fruit);
        }
        return fruit;
    }
}
package org.wesome.design.patterns;

public class Flyweight {
    public static void main(String[] args) {
        Fruit fruit;
        fruit = AppleShop.getApple(AppleType.MACINTOSH, AppleTaste.SWEET, 2);
        fruit.eatApple();
        fruit = AppleShop.getApple(AppleType.MACINTOSH, AppleTaste.JUICY, 3);
        fruit.eatApple();

        fruit = AppleShop.getApple(AppleType.FUJI, AppleTaste.SWEET, 4);
        fruit.eatApple();
        fruit = AppleShop.getApple(AppleType.FUJI, AppleTaste.JUICY, 5);
        fruit.eatApple();
    }
}
package org.wesome.design.patterns;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.stream.IntStream;

import static org.wesome.design.patterns.AppleShop.appleShopInventoryCache;

public class FlyweightTest {
    @Test
    void testFlyweight() {
        IntStream.rangeClosed(1, 10).forEach(value -> {
            Fruit fruit = AppleShop.getApple(AppleType.FUJI, AppleTaste.SWEET, value);
            fruit.eatApple();

            fruit = AppleShop.getApple(AppleType.MACINTOSH, AppleTaste.JUICY, value);
            fruit.eatApple();
        });
        Assertions.assertEquals(appleShopInventoryCache.size(), 2);
    }
}
plugins {
    id 'java'
    id "io.freefair.lombok" version "6.2.0"
}

group = 'org.wesome'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = JavaVersion.VERSION_1_8

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter:5.6.2'
}

test {
    useJUnitPlatform()
}

follow us on