Lazy Loading Design Pattern (original) (raw)

Last Updated : 6 May, 2026

Lazy Loading is a Creational design pattern where an object or resource is initialized only when it is actually needed, rather than at the time of application startup. This approach delays object creation to reduce memory usage and improve performance.

**Example: Consider an image viewer application where high-resolution images are not loaded at startup but only when the user opens them.

image_proxy

In the above diagram

Real-life Applications

Here are some clear real-life examples of Lazy Loading:

Components

Lazy Loading works by delaying object creation until it is actually needed, and its components help manage this deferred initialization.

Uses

Lazy Loading is useful when object creation is expensive or infrequent.

Implementations Example

There are four common implementations of Lazy Loading pattern

1. Virtual proxy

The Virtual Proxy pattern is a memory saving technique that recommends postponing an object creation until it is needed. It is used when creating an object that is expensive in terms of memory usage or processing involved.

C++ `

#include #include #include

class Employee { private: std::string employeeName; double employeeSalary; std::string employeeDesignation;

public: Employee(std::string employeeName, double employeeSalary, std::string employeeDesignation) { this->employeeName = employeeName; this->employeeSalary = employeeSalary; this->employeeDesignation = employeeDesignation; } std::string getEmployeeName() { return employeeName; } double getEmployeeSalary() { return employeeSalary; } std::string getEmployeeDesignation() { return employeeDesignation; } std::string toString() { return "Employee Name: " + employeeName + ", Employee Designation: " + employeeDesignation + ", Employee Salary: " + std::to_string(employeeSalary); } };

class ContactList { public: virtual std::vector getEmployeeList() = 0; };

class ContactListImpl : public ContactList { public: std::vector getEmployeeList() override { return getEmpList(); } private: static std::vector getEmpList() { std::vector empList;

    empList.push_back(Employee("Lokesh", 2565.55, "SE"));
    empList.push_back(Employee("Kushagra", 22574, "Manager"));
    empList.push_back(Employee("Susmit", 3256.77, "G4"));
    empList.push_back(Employee("Vikram", 4875.54, "SSE"));
    empList.push_back(Employee("Achint", 2847.01, "SE"));

    return empList;
}

};

class ContactListProxyImpl : public ContactList { private: ContactList* contactList; public: std::vector getEmployeeList() override { if (contactList == nullptr) { std::cout << "Fetching list of employees" << std::endl; contactList = new ContactListImpl(); } return contactList->getEmployeeList(); } };

class Company { private: std::string companyName; std::string companyAddress; std::string companyContactNo; ContactList* contactList;

public: Company(std::string companyName, std::string companyAddress, std::string companyContactNo, ContactList* contactList) { this->companyName = companyName; this->companyAddress = companyAddress; this->companyContactNo = companyContactNo; this->contactList = contactList; } std::string getCompanyName() { return companyName; } std::string getCompanyAddress() { return companyAddress; } std::string getCompanyContactNo() { return companyContactNo; } ContactList* getContactList() { return contactList; } };

int main() { ContactList* contactList = new ContactListProxyImpl(); Company company("Geeksforgeeks", "India", "+91-011-28458965", contactList);

std::cout << "Company Name: " << company.getCompanyName() << std::endl;
std::cout << "Company Address: " << company.getCompanyAddress() << std::endl;
std::cout << "Company Contact No.: " << company.getCompanyContactNo() << std::endl;
std::cout << "Requesting for contact list" << std::endl;

contactList = company.getContactList();
std::vector<Employee> empList = contactList->getEmployeeList();
for (Employee emp : empList) {
    std::cout << emp.toString() << std::endl;
}
return 0;

}

Java

import java.util.ArrayList; import java.util.List;

interface ContactList { public List getEmployeeList(); }

class Company { String companyName; String companyAddress; String companyContactNo; ContactList contactList;

public Company(String companyName,
               String companyAddress,
               String companyContactNo,
               ContactList contactList)
{
    this.companyName = companyName;
    this.companyAddress = companyAddress;
    this.companyContactNo = companyContactNo;
    this.contactList = contactList;
}

public String getCompanyName() { return companyName; }
public String getCompanyAddress()
{
    return companyAddress;
}
public String getCompanyContactNo()
{
    return companyContactNo;
}
public ContactList getContactList()
{
    return contactList;
}

}

class ContactListImpl implements ContactList { public List getEmployeeList() { return getEmpList(); } private static List getEmpList() { List empList = new ArrayList(5);

    empList.add(new Employee("Lokesh", 2565.55, "SE"));
    empList.add(
        new Employee("Kushagra", 22574, "Manager"));
    empList.add(new Employee("Susmit", 3256.77, "G4"));
    empList.add(new Employee("Vikram", 4875.54, "SSE"));
    empList.add(new Employee("Achint", 2847.01, "SE"));

    return empList;
}

}

class ContactListProxyImpl implements ContactList { private ContactList contactList; public List getEmployeeList() { if (contactList == null) { System.out.println( "Fetching list of employees"); contactList = new ContactListImpl(); } return contactList.getEmployeeList(); } }

class Employee { private String employeeName;

private double employeeSalary;
private String employeeDesignation;

public Employee(String employeeName,
                double employeeSalary,
                String employeeDesignation)
{
    this.employeeName = employeeName;
    this.employeeSalary = employeeSalary;
    this.employeeDesignation = employeeDesignation;
}
public String getEmployeeName() { return employeeName; }
public double getEmployeeSalary()
{
    return employeeSalary;
}
public String getEmployeeDesignation()
{
    return employeeDesignation;
}
public String toString()
{
    return "Employee Name: " + employeeName
        + ", Employee Designation: "
        + employeeDesignation
        + ", Employee Salary: " + employeeSalary;
}

}

class LazyLoading { public static void main(String[] args) { ContactList contactList = new ContactListProxyImpl(); Company company = new Company("Geeksforgeeks", "India", "+91-011-28458965", contactList);

    System.out.println("Company Name: "
                       + company.getCompanyName());
    System.out.println("Company Address: "
                       + company.getCompanyAddress());
    System.out.println("Company Contact No.: "
                       + company.getCompanyContactNo());
    System.out.println("Requesting for contact list");

    contactList = company.getContactList();
    List<Employee> empList
        = contactList.getEmployeeList();
    for (Employee emp : empList) {
        System.out.println(emp);
    }
}

}

Python

from typing import List

class Employee: def init(self, employee_name: str, employee_salary: float, employee_designation: str): self.employee_name = employee_name self.employee_salary = employee_salary self.employee_designation = employee_designation

def __str__(self):
    return f'Employee Name: {self.employee_name}, Employee Designation: {self.employee_designation}, Employee Salary: {self.employee_salary}'

class ContactList: def get_employee_list(self) -> List[Employee]: pass

class ContactListImpl(ContactList): def get_employee_list(self) -> List[Employee]: return self._get_emp_list()

@staticmethod
def _get_emp_list() -> List[Employee]:
    emp_list = [
        Employee('Lokesh', 2565.55, 'SE'),
        Employee('Kushagra', 22574, 'Manager'),
        Employee('Susmit', 3256.77, 'G4'),
        Employee('Vikram', 4875.54, 'SSE'),
        Employee('Achint', 2847.01, 'SE')
    ]
    return emp_list

class ContactListProxyImpl(ContactList): def init(self): self.contact_list = None

def get_employee_list(self) -> List[Employee]:
    if self.contact_list is None:
        print('Fetching list of employees')
        self.contact_list = ContactListImpl()
    return self.contact_list.get_employee_list()

class Company: def init(self, company_name: str, company_address: str, company_contact_no: str, contact_list: ContactList): self.company_name = company_name self.company_address = company_address self.company_contact_no = company_contact_no self.contact_list = contact_list

def get_company_name(self) -> str:
    return self.company_name

def get_company_address(self) -> str:
    return self.company_address

def get_company_contact_no(self) -> str:
    return self.company_contact_no

def get_contact_list(self) -> ContactList:
    return self.contact_list

def main(): contact_list = ContactListProxyImpl() company = Company('Geeksforgeeks', 'India', '+91-011-28458965', contact_list)

print(f'Company Name: {company.get_company_name()}')
print(f'Company Address: {company.get_company_address()}')
print(f'Company Contact No.: {company.get_company_contact_no()}')
print('Requesting for contact list')

contact_list = company.get_contact_list()
emp_list = contact_list.get_employee_list()
for emp in emp_list:
    print(emp)

if name == 'main': main()

` JavaScript ``

const LANGUAGE_MAP = ["cpp","c","java","python3","csharp","html","css","javascript","php","cpp14","cobol","dart","go","julia","kotlin","lisp","matlab","node","objc","perl","r","rust","ruby","scala","swift","solidity","xml"];

class Employee { constructor(employeeName, employeeSalary, employeeDesignation) { this.employeeName = employeeName; this.employeeSalary = employeeSalary; this.employeeDesignation = employeeDesignation; }

getEmployeeName() {
    return this.employeeName;
}

getEmployeeSalary() {
    return this.employeeSalary;
}

getEmployeeDesignation() {
    return this.employeeDesignation;
}

toString() {
    return `Employee Name: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mrow><mi>t</mi><mi>h</mi><mi>i</mi><mi>s</mi><mi mathvariant="normal">.</mi><mi>e</mi><mi>m</mi><mi>p</mi><mi>l</mi><mi>o</mi><mi>y</mi><mi>e</mi><mi>e</mi><mi>N</mi><mi>a</mi><mi>m</mi><mi>e</mi></mrow><mo separator="true">,</mo><mi>E</mi><mi>m</mi><mi>p</mi><mi>l</mi><mi>o</mi><mi>y</mi><mi>e</mi><mi>e</mi><mi>D</mi><mi>e</mi><mi>s</mi><mi>i</mi><mi>g</mi><mi>n</mi><mi>a</mi><mi>t</mi><mi>i</mi><mi>o</mi><mi>n</mi><mo>:</mo></mrow><annotation encoding="application/x-tex">{this.employeeName}, Employee Designation: </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal">t</span><span class="mord mathnormal">hi</span><span class="mord mathnormal">s</span><span class="mord">.</span><span class="mord mathnormal">e</span><span class="mord mathnormal">m</span><span class="mord mathnormal" style="margin-right:0.01968em;">pl</span><span class="mord mathnormal">oyee</span><span class="mord mathnormal" style="margin-right:0.10903em;">N</span><span class="mord mathnormal">am</span><span class="mord mathnormal">e</span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.05764em;">E</span><span class="mord mathnormal">m</span><span class="mord mathnormal" style="margin-right:0.01968em;">pl</span><span class="mord mathnormal">oyeeDes</span><span class="mord mathnormal">i</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">na</span><span class="mord mathnormal">t</span><span class="mord mathnormal">i</span><span class="mord mathnormal">o</span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">:</span></span></span></span>{this.employeeDesignation}, Employee Salary: ${this.employeeSalary}`;
}

}

class ContactListImpl { static #empList = null;

getEmployeeList() {
    if (!ContactListImpl.#empList) {
        ContactListImpl.#empList = this.#getEmpList();
    }
    return ContactListImpl.#empList;
}

#getEmpList() {
    const empList = [];

    empList.push(new Employee("Lokesh", 2565.55, "SE"));
    empList.push(new Employee("Kushagra", 22574, "Manager"));
    empList.push(new Employee("Susmit", 3256.77, "G4"));
    empList.push(new Employee("Vikram", 4875.54, "SSE"));
    empList.push(new Employee("Achint", 2847.01, "SE"));

    return empList;
}

}

class ContactListProxyImpl { #contactList = null;

getEmployeeList() {
    if (!this.#contactList) {
        console.log("Fetching list of employees");
        this.#contactList = new ContactListImpl();
    }
    return this.#contactList.getEmployeeList();
}

}

class Company { constructor(companyName, companyAddress, companyContactNo, contactList) { this.companyName = companyName; this.companyAddress = companyAddress; this.companyContactNo = companyContactNo; this.contactList = contactList; }

getCompanyName() {
    return this.companyName;
}

getCompanyAddress() {
    return this.companyAddress;
}

getCompanyContactNo() {
    return this.companyContactNo;
}

getContactList() {
    return this.contactList;
}

}

class LazyLoading { static main() { const contactList = new ContactListProxyImpl(); const company = new Company("Geeksforgeeks", "India", "+91-011-28458965", contactList);

    console.log(`Company Name: ${company.getCompanyName()}`);
    console.log(`Company Address: ${company.getCompanyAddress()}`);
    console.log(`Company Contact No.: ${company.getCompanyContactNo()}`);
    console.log("Requesting for contact list");

    const empList = contactList.getEmployeeList();
    empList.forEach(emp => {
        console.log(emp.toString());
    });
}

}

LazyLoading.main();

``

Output

Company Name: Geeksforgeeks Company Address: India Company Contact No.: +91-011-28458965 Requesting for contact list Fetching list of employees Employee Name: Lokesh, Employee Designation: SE, Employe...

2. Lazy Initialization

The Lazy Initialization technique consists of checking the value of a class field when it's being used. If that value equals to null then that field gets loaded with the proper value before it is returned. Here is the example :

Java `

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

enum CarType { none, Audi, BMW, }

class Car { private static Map<CarType, Car> types = new HashMap<>();

private Car(CarType type) {}

public static Car getCarByTypeName(CarType type)
{
    Car Car;

    if (!types.containsKey(type)) {
        // Lazy initialisation
        Car = new Car(type);
        types.put(type, Car);
    } else {
        // It's available currently
        Car = types.get(type);
    }

    return Car;
}

public static Car getCarByTypeNameHighConcurrentVersion(CarType type)
{
    if (!types.containsKey(type)) {
        synchronized(types)
        {
            // Check again, after having acquired the lock to make sure
            // the instance was not created meanwhile by another thread
            if (!types.containsKey(type)) {
                // Lazy initialisation
                types.put(type, new Car(type));
            }
        }
    }

    return types.get(type);
}

public static void showAll()
{
    if (types.size() > 0) {

        System.out.println("Number of instances made = " + types.size());

        for (Entry<CarType, Car> entry : types.entrySet()) {
            String Car = entry.getKey().toString();
            Car = Character.toUpperCase(Car.charAt(0)) + Car.substring(1);
            System.out.println(Car);
        }

        System.out.println();
    }
}

}

class Program { public static void main(String[] args) { Car.getCarByTypeName(CarType.BMW); Car.showAll(); Car.getCarByTypeName(CarType.Audi); Car.showAll(); Car.getCarByTypeName(CarType.BMW); Car.showAll(); } }

Python

from enum import Enum from typing import Dict

class CarType(Enum): none = 1 Audi = 2 BMW = 3

class Car: types: Dict[CarType, 'Car'] = {}

def __init__(self, type: CarType):
    pass

@staticmethod
def get_car_by_type_name(type: CarType):
    if type not in Car.types:
        # Lazy initialisation
        car = Car(type)
        Car.types[type] = car
    else:
        # It's available currently
        car = Car.types[type]
    return car

@staticmethod
def get_car_by_type_name_high_concurrent_version(type: CarType):
    if type not in Car.types:
        # This is a simple representation, in a real scenario
        # you would need a proper locking mechanism
        if type not in Car.types:
            # Lazy initialisation
            Car.types[type] = Car(type)
    return Car.types[type]

@staticmethod
def show_all():
    if len(Car.types) > 0:
        print(f'Number of instances made = {len(Car.types)}')
        for car_type, car in Car.types.items():
            car_str = car_type.name
            car_str = car_str[0].upper() + car_str[1:]
            print(car_str)
        print()

if name == 'main': Car.get_car_by_type_name(CarType.BMW) Car.show_all() Car.get_car_by_type_name(CarType.Audi) Car.show_all() Car.get_car_by_type_name(CarType.BMW) Car.show_all()

`

Output :

Number of instances made = 1
BMW

Number of instances made = 2
BMW
Audi

Number of instances made = 2
BMW
Audi

3. Value Holder

Basically, A value holder is a generic object that handles the lazy loading behavior and appears in place of the object's data fields.When the user needs to access it, they simply ask the value holder for its value by calling the GetValue method. At that time (and only then), the value gets loaded from a database or from a service.(this is not always needed).

C++ `

#include #include #include

template class ValueHolder { private: T value; std::function<T(const std::string&)> valueRetrieval;

public: // Constructor ValueHolder(std::function<T(const std::string&)> valueRetrieval) : valueRetrieval(valueRetrieval) {}

// We'll use the signature "GetValue" for convention
T GetValue(const std::string& parameter) {
    if (value == T())
        value = valueRetrieval(parameter);
    return value;
}

};

Java

public class ValueHolder { private T value; private readonly Func<object, T> valueRetrieval;

// Constructor
public ValueHolder(Func<object, T> valueRetrieval)
{
    valueRetrieval = this.valueRetrieval;
}

// We'll use the signature "GetValue" for convention
public T GetValue(object parameter)
{
    if (value == null)
        value = valueRetrieval(parameter);
    return value;
}

}

Python

class ValueHolder: def init(self, value_retrieval): self.value = None self.value_retrieval = value_retrieval

# We'll use the signature "get_value" for convention
def get_value(self, parameter):
    if self.value is None:
        self.value = self.value_retrieval(parameter)
    return self.value

JavaScript

class ValueHolder { constructor(valueRetrieval) { this.value = null; this.valueRetrieval = valueRetrieval; }

// We'll use the signature "getValue" for convention
getValue(parameter) {
    if (this.value === null)
        this.value = this.valueRetrieval(parameter);
    return this.value;
}

}

`

Note : The main drawback of this approach is that the user has to know that a value holder is expected.

4. Ghost

A Ghost Object is an object that is loaded in a partial state (e.g., only ID). It represents the real object but isn’t fully initialized. When uninitialized data is accessed, it loads the remaining data on demand.

C++ `

#include #include #include #include #include <uuid/uuid.h>

std::unordered_map<std::string, std::string> userData = { {"UID", uuid::generate()}, {"requestTime", std::to_string(time(0))}, {"dataType", ""}, {"request", ""} };

if (isset(post["data"]) && userData) { //... }

Java

$userData = array( "UID" = > uniqid(), "requestTime" = > microtime(true), "dataType" = > "", "request" = > "");

if (isset($_POST['data']) && $userData) { //... }

Python

import uuid import time

userData = { "UID": str(uuid.uuid4()), "requestTime": time.time(), "dataType": "", "request": "" }

if 'data' in request.form and userData: #...

JavaScript

const userData = { UID: Date.now().toString(36) + Math.random().toString(36).substr(2), requestTime: Date.now(), dataType: "", request: "" };

if (document.querySelector('input[name="data"]') && userData) { //... }

`

In the above example, the content from the online form can be accessed to the user in the form of text file or any source.

Advantages

Lazy Loading improves efficiency and resource utilization.

Disadvantages

Although useful, Lazy Loading can introduce complexity and potential issues.