Lusta inicializáció programtervezési minta
A számítógépes programozásban, a lusta inicializáció programtervezési minta egy olyan taktika, amely szerint késleltetjük egy objektum létrehozását, vagy valamely számításigényes művelet elvégzését egészen addig, amíg az objektumra vagy a számítás eredményére először ténylegesen szükség lesz.

Ezt tipikusan egy változó elérésére szolgáló eljárás vagy egy objektum-tulajdonság (property) definíció bevezetésével valósítjuk meg, amelyben ellenőrizzük, hogy az elérni (használni) kívánt objektum példány (vagy számítási eredmény) létezik-e már. Ha nem létezik, egy új példány kerül létrehozásra és tárolásra a kapcsolódó változóban, és ennek értéke kerül a hívónak (az adatra hivatkozó kódrészletnek) visszaadásra „éppen a kellő időben” (Just In Time) módon.

Ezzel a viselkedéssel az objektum létrehozását „elhalasztják” az első használatig, amely bizonyos körülmények között (pl. az objektum ritkább használata esetén), csökkenti a rendszer válaszidejét és gyorsítja az indítást azáltal, hogy elkerüli a nagyméretű objektumok előzetes létrehozását és memóriafoglalását. (Megjegyezzük, hogy akár ellenkező hatást okozhat az általános teljesítményben, ha a késleltetett objektum létrehozás előnyeit „elhasználjuk” a rendszer indító/előkészítő fázisa során.)

Több szálon futó kód esetén, a lusta inicializációval használt objektumokhoz való hozzáférést szinkronizálni kell (kölcsönös kizárás/mutex), a versenyhelyzet (race condition) elkerülése érdekében.

A késleltetett kiértékelés jól mutatja az alapötlet általános megvalósítását. Az erősen imperatív programozási nyelvekben ez a minta rejtett veszélyeket hordoz, csakúgy mint bármely programozási szokás amely megosztott állapotokra támaszkodik.

A „lusta gyár”

A programtervezési minták nézőpontjából vizsgálva, a lusta inicializáció gyakran együttesen használt a gyártó metódus programtervezési mintával. Ez a megoldás három elgondolást kombinál:
 Egy gyártó metódust használunk, hogy példányokat kapjunk egy osztályból (gyártó metódus programtervezési minta)
 A példányokat egy asszociatív tömbben tároljuk (létrehozási paraméterek/létrehozott példány), így amikor legközelebb „ugyanazokkal” a paraméterekkel kérnek példányt a gyártól, „ugyanazt” a példányt adja majd vissza, ami az adott paraméterekkel már létrehozásra került  (többke programtervezési minta)
 Lusta inicializációt használunk az objektumok létrehozására, azok csak akkor jönnek létre, amikor először kérik őket a gyártól

Példakódok

C
C-ben a lusta inicializáció egyetlen függvényben vagy egyetlen forrásfájlban kerül megvalósításra, statikus változók használatával.

Egy függvényben:
#include<string.h>
#include<stdlib.h>
#include<stddef.h>
#include<stdio.h>

struct fruit {
    char *name;
    struct fruit *next;
    int number;
    /* Egyéb adattagok */
};

struct fruit *get_fruit(char *name) {
    static struct fruit *fruit_list;
    static int seq;
    struct fruit *f;
    for (f=fruit_list; f; f=f->next)
        if (0==strcmp(name, f->name))
            return f;
    if (!(f = malloc(sizeof(struct fruit))))
        return NULL;
    if (!(f->name = strdup(name))) {
        free(f);
        return NULL;
    }
    f->number = ++seq;
    f->next = fruit_list;
    fruit_list = f;
    return f;
}

/* Példa kód */

int main(int argc, char *argv[]) {
    int i;
    struct fruit *f;
    if (argc<2) {
        fprintf(stderr, "Használat: fruits gyümölcs-név [...]\n");
        exit(1);
    }
    for (i=1; i<argc; i++) {
        if ((f = get_fruit(argv[i]))) {
            printf("Gyümölcs %s: száma %d\n", argv[i], f->number);
        }
    }
    return 0;
}
Egyetlen forrásfájl használata a fentiek helyett lehetővé teszi, hogy az állapotok megosztásra kerüljenek több függvény között úgy, hogy a nem kapcsolódó függvények elől továbbra is el vannak rejtve.

fruit.h:
#ifndef _FRUIT_INCLUDED_
#define _FRUIT_INCLUDED_

struct fruit {
    char *name;
    struct fruit *next;
    int number;
    /* Egyéb adattagok */
};

struct fruit *get_fruit(char *name);
void print_fruit_list(FILE *file);

#endif /* _FRUIT_INCLUDED_ */

fruit.c:
#include<string.h>
#include<stdlib.h>
#include<stddef.h>
#include<stdio.h>
#include"fruit.h"

static struct fruit *fruit_list;
static int seq;

struct fruit *get_fruit(char *name) {
    struct fruit *f;
    for (f=fruit_list; f; f=f->next)
        if (0==strcmp(name, f->name))
            return f;
    if (!(f = malloc(sizeof(struct fruit))))
        return NULL;
    if (!(f->name = strdup(name))) {
        free(f);
        return NULL;
    }
    f->number = ++seq;
    f->next = fruit_list;
    fruit_list = f;
    return f;
}

void print_fruit_list(FILE *file) {
    struct fruit *f;
    for (f=fruit_list; f; f=f->next)
        fprintf(file, "%4d  %s\n", f->number, f->name);
}

main.c:
#include<stdlib.h>
#include<stdio.h>
#include"fruit.h"

int main(int argc, char *argv[]) {
    int i;
    struct fruit *f;
    if (argc<2) {
        fprintf(stderr, "Használat: fruits gyümölcs-név [...]\n");
        exit(1);
    }
    for (i=1; i<argc; i++) {
        if ((f = get_fruit(argv[i]))) {
            printf("Gyümölcs %s: száma %d\n", argv[i], f->number);
        }
    }
    printf("A következő gyümölcsök (fruits) generálódtak:\n");
    print_fruit_list(stdout);
    return 0;
}

C#
A .NET 4.0-ban a Microsoft elérhetővé tett egy Lazy osztályt, amely lusta (késői) betöltéshez használható. Az alábbi néhány sor mintakód a Fruit osztály lusta betöltését mutatja:
Lazy<Fruit> lazyFruit = new Lazy<Fruit>();
Fruit fruit = lazyFruit.Value;

Az alábbi egy minta kód C# programozási nyelven.

A Fruit osztály maga nem csinál semmit a példában, a _typesDictionary osztály-változó egy szótár/asszociatív tömb amiben a Fruit példányokat tároljuk a typeName (típusnév) alapján.

using System;
using System.Collections;
using System.Collections.Generic;

public class Fruit
{
    private string _typeName;
    private static Dictionary<string, Fruit> _typesDictionary = new Dictionary<string, Fruit>();

    private Fruit(String typeName)
    {
        this._typeName = typeName;
    }

    public static Fruit GetFruitByTypeName(string type)
    {
        Fruit fruit;

        if (!_typesDictionary.TryGetValue(type, out fruit))
        {
            // Lusta inicializáció
            fruit = new Fruit(type);

            _typesDictionary.Add(type, fruit);
        }
        return fruit;
    }

    public static void ShowAll()
    {
        if (_typesDictionary.Count > 0)
        {
            Console.WriteLine("Az elkészített példányok száma = {0}", _typesDictionary.Count);
            
            foreach (KeyValuePair<string, Fruit> kvp in _typesDictionary)
            {
                Console.WriteLine(kvp.Key);
            }
            
            Console.WriteLine();
        }
    }
    
    public Fruit()
    {
        // szükséges a mintakód lefordításához
    }
}

class Program
{
    static void Main(string[] args)
    {
        Fruit.GetFruitByTypeName("Banana");
        Fruit.ShowAll();

        Fruit.GetFruitByTypeName("Apple");
        Fruit.ShowAll();

        // az előzőleg már létrehozott példányt adja vissza 
        // ami a "Banana" paraméterrel való első hívásnál létrejött
        Fruit.GetFruitByTypeName("Banana");
        Fruit.ShowAll();

        Console.ReadLine();
    }
}
Az alábbiakban egy meglehetősen lényegre törő példáját mutatjuk be a lusta inicializáció programtervezési mintának azzal, hogy a példa enumerációt (felsorolással definiált típust) használ alkalmazott típusként.
//IVSR: LazyInitialization design pattern
namespace IVSR.DesignPatterns.LazyInitialization
{
    public class LazyFactoryObject
    {
        // az elemek belsőleg használt gyűjteménye.
        // az IDictionary típus gondoskodik róla, hogy az elemek egyediek legyenek
        private IDictionary<LazyObjectType, LazyObject> _LazyObjectList = 
            new Dictionary<LazyObjectType, LazyObject>();

        // felsorolási típus a kívánt típusnevek megadásához.
        // így nem szükséges string paraméterátadást, 
        // és lehetséges a fordításkori típusellenőrzés
        public enum LazyObjectType
        { 
            None,
            Small,
            Big,
            Bigger,
            Huge
        }

        // egy általános típus a létrehozandó objektumoknak
        public struct LazyObject
        {
            public LazyObjectType Name;
            public IList<int> Result;
        }

        // egy felsorolási típus elemet vesz át, és létrehoz egy 
        // 'drága' (időigényes, számításigényes stb.) listát
        private IList<int> Result(LazyObjectType name)
        {
            IList<int> result = null;

            switch (name)
            { 
                case LazyObjectType.Small:
                    result = CreateSomeExpensiveList(1, 100);
                    break;
                case LazyObjectType.Big:
                    result = CreateSomeExpensiveList(1, 1000);
                    break;
                case LazyObjectType.Bigger:
                    result = CreateSomeExpensiveList(1, 10000);
                    break;
                case LazyObjectType.Huge:
                    result = CreateSomeExpensiveList(1, 100000);
                    break;
                case LazyObjectType.None:
                    result = null;
                    break;
                default:
                    result = null;
                    break;
            }

            return result;
        }

        // nem lenne 'drága' (számításigényes) létrehozni az elemet 
        // de hogy érthető legyen a lényeg, késleltetjük néhány 
        // számításigényes objektum létrehozását addig, 
        // amíg szükség nem lesz rájuk
        private IList<int> CreateSomeExpensiveList(int start, int end)
        {
            IList<int> result = new List<int>();

            for (int counter = 0; counter < (end - start); counter++)
            {
                result.Add(start + counter);
            }

            return result;
        }

        public LazyFactoryObject()
        { 
            // üres konstruktor
        }

        public LazyObject GetLazyFactoryObject(LazyObjectType name)
        {
            LazyObject noGoodSomeOne;

            // 'előveszi' a LazyObjectType elemet a listáról ha van már ott,
            // egyébként (ha nincs) létrehoz egyet és a listára (is) teszi
            if (!_LazyObjectList.TryGetValue(name, out noGoodSomeOne))
            {
                noGoodSomeOne = new LazyObject();
                noGoodSomeOne.Name = name;
                noGoodSomeOne.Result = this.Result(name);

                _LazyObjectList.Add(name, noGoodSomeOne);
            }

            return noGoodSomeOne;
        }
    }
}

C++
Az alábbiakban egy C++ példa.

#include <iostream>
#include <string>
#include <map>
 
using namespace std;
 
class Fruit {
    public:
        static Fruit* getFruit(const string& type);
        static void printCurrentTypes();

    private:
        static map<string,Fruit*> types;
        string type;

        // megjegyzés: a privát konstruktor kikényszeríti, hogy a statikus 
        // getFruit() függvénnyel kelljen objektum példányt létrehozni
        Fruit(const string& t) : type( t ) {}
};

// szükséges definíció a statikus tag-változó használatához
map<string,Fruit*> Fruit::types;        
 
/*
 * Lusta gyártó metódus, azt a 'Fruit' példányt veszi elő, amire a paraméterként
 * átadott névvel hivatkoztak. Létrehoz egy új példányt, ha szükséges.
 * előfeltétel: a 'type' paraméter valamilyen gyümölcs név legyen pl. "alma"
 * utófeltétel: visszaadja a 'Fruit' példányt, amire a névvel hivatkoztunk
 */
Fruit* Fruit::getFruit(const string& type) {
    // próbálunk keresni egy létező példányt; ha nem találunk, 
    // az std::map függvény 'types.end()' értéket ad vissza
    map<string,Fruit*>::iterator it = types.find(type);  
 
    Fruit *f;
    if (it == types.end()) { 
        // ha nem találtunk példányt a kért típusból, hozzunk létre egyet
        f = new Fruit(type); // lusta inicializációs rész
        // hozzáadjuk az újonnan létrehozott 'Fruit' példányt 
        // a 'types' asszociatív tömbhöz, hogy később megtaláljuk
        types[type] = f;     
    } else { // ha már van ilyen példányunk
        // a visszaadott érték a megtalált 'Fruit' példány lesz
        f = it->second; 
    }
    return f;
}

/*
 * Példaként lássuk a minta használatát
 */
void Fruit::printCurrentTypes() {
    if (!types.empty()) {
        cout << "A létrehozott példányok száma = " << types.size() << endl;
        for (map<string,Fruit*>::iterator iter = types.begin(); iter != types.end(); ++iter) {
            cout << (*iter).first << endl;
        }
        cout << endl;
    }
}

int main(void) {
    Fruit::getFruit("Banana");
    Fruit::printCurrentTypes();
 
    Fruit::getFruit("Apple");
    Fruit::printCurrentTypes();
 
    // az előzőleg már létrehozott példányt adja vissza 
    // ami a "Banana" paraméterrel való első hívásnál létrejött
    Fruit::getFruit("Banana");
    Fruit::printCurrentTypes();
 
    return 0;
}

/*
KIMENET:
A létrehozott példányok száma = 1
Banana
 
A létrehozott példányok száma = 2
Apple
Banana
 
A létrehozott példányok száma = 2
Apple
Banana
*/

Java
Az alábbiakban egy Java példát mutatunk be.

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

public class Program {

    /**
     * @param args
     */
    public static void main(String[] args) {
        Fruit.getFruitByTypeName(FruitType.BANANA);
        Fruit.showAll();
        Fruit.getFruitByTypeName(FruitType.APPLE);
        Fruit.showAll();
        Fruit.getFruitByTypeName(FruitType.BANANA);
        Fruit.showAll();
    }
}

enum FruitType {
    NONE,
    APPLE,
    BANANA,
}

class Fruit {

    private static Map<FruitType, Fruit> types = new HashMap<>();
    
    /**
     * A privát konstruktor kikényszeríti a gyártó metódus használatát.
     * @param type
     */
    private Fruit(FruitType type) {
    }
    
    /**
     * Lusta gyártó metódus, azt a 'Fruit' példányt veszi elő, amire a paraméterrel
     * hivatkoztak. Létrehoz egy új példányt, ha szükséges.
     * @param type Bármilyen a típusnál megengedett gyümölcs név, pl. APPLE
     * @return A 'Fruit' példány, amire a típus-elem névvel hivatkoztunk
     */
    public static Fruit getFruitByTypeName(FruitType type) {
        Fruit fruit;
        // Ennek a programrésznek konkurencia problémái lehetnek.
        // Az alábbiakban a 'types'-hoz való hozzáférés nem szinkronizált, 
        // így a 'types.put' és a 'types.containsKey' egyszerre is meghívásra kerülhet.
        // Nem kell meglepődni, ha az adatok sérülnek... :)
        if (!types.containsKey(type)) {
            // Lusta inicializáció
            fruit = new Fruit(type);
            types.put(type, fruit);
        } else {
            // OK, már elérhető
            fruit = types.get(type);
        }
        
        return fruit;
    }
    
    /**
     * Lusta gyártó metódus, azt a 'Fruit' példányt veszi elő, amire a paraméterrel
     * hivatkoztak. Létrehoz egy új példányt, ha szükséges. Duplán ellenőrzött lockolás
     * mintát használ a konkurens hozzáférés biztonságossá tételére.
     * @param type Bármilyen a típusnál megengedett gyümölcs név, pl. APPLE
     * @return A 'Fruit' példány, amire a típus-elem névvel hivatkoztunk
     */
    public static Fruit getFruitByTypeNameHighConcurrentVersion(FruitType type) {
        if (!types.containsKey(type)) {
            synchronized (types) {
                // Újra ellenőrizzük a 'type' létezését, miután lockoltuk a 'types'-t
                // hogy biztosak legyünk benne, hogy nem került időközben létrehozásra
                // belőle példány egy másik programszálon
                if (!types.containsKey(type)) {
                    // Lusta inicializáció
                    types.put(type, new Fruit(type));
                }
            }
        }
        
        return types.get(type);
    }
    
    /**
     * Kiírja az összes létrehozott 'Fruit' példányt.
     */
    public static void showAll() {
        if (types.size() > 0) {
 
           System.out.println("A létrehozott példányok száma = " + types.size());
            
            for (Entry<FruitType, Fruit> entry : types.entrySet()) {
                String fruit = entry.getKey().toString();
                fruit = Character.toUpperCase(fruit.charAt(0)) + fruit.substring(1);
                System.out.println(fruit);
            }
            
            System.out.println();
        }
    }
}

Kimenet
A létrehozott példányok száma = 1
Banana

A létrehozott példányok száma = 2
Banana
Apple

A létrehozott példányok száma = 2
Banana
Apple

JavaScript
Az alábbiakban egy JavaScript példa.

var Fruit = (function() {
  var types = {};
  function Fruit() {};

  // visszaadja a paraméterként kapott objektum 
  // tulajdonságainak(property) számát
  function count(obj) {
    return Object.keys(obj).length;
  }

  var _static = {
    getFruit: function(type) {
      if (typeof types[type] == 'undefined') {
        types[type] = new Fruit;
      }
      return types[type];
    },
    printCurrentTypes: function () {
      console.log('A létrehozott példányok száma: ' + count(types));
      for (var type in types) {
        console.log(type);
      }
    }
  };

  return _static;

})();

Fruit.getFruit('Apple');
Fruit.printCurrentTypes();
Fruit.getFruit('Banana');
Fruit.printCurrentTypes();
Fruit.getFruit('Apple');
Fruit.printCurrentTypes();
Kimenet
A létrehozott példányok száma: 1
Apple

A létrehozott példányok száma: 2
Apple
Banana

A létrehozott példányok száma: 2
Apple
Banana

PHP
Az alábbiakban egy példa a lusta inicializációra PHP 5-ben:
<?php
header('Content-type:text/plain; charset=utf-8');

class Fruit {
    private $type;
    private static $types = array();

    private function __construct($type) {
        $this->type = $type;
    }

    public static function getFruit($type) {
        // A lusta inicializáció itt történik
        if (!isset(self::$types[$type])) {
            self::$types[$type] = new Fruit($type);
        }

        return self::$types[$type];
    }

    public static function printCurrentTypes() {
        echo 'A létrehozott példányok száma: ' . count(self::$types) . "\n";
        foreach (array_keys(self::$types) as $key) {
            echo "$key\n";
        }
        echo "\n";
    }
}

Fruit::getFruit('Apple');
Fruit::printCurrentTypes();

Fruit::getFruit('Banana');
Fruit::printCurrentTypes();

Fruit::getFruit('Apple');
Fruit::printCurrentTypes();

/*
KIMENET:

A létrehozott példányok száma: 1
Apple

A létrehozott példányok száma: 2
Apple
Banana

A létrehozott példányok száma: 2
Apple
Banana
*/

?>

Python
Az alábbiakban egy példa Python programozási nyelven (2.x verziójú Python).

class Fruit:
    def __init__(self, sort):
        self.sort = sort
    
class Fruits:
    def __init__(self):
        self.sorts = {}
    
    def get_fruit(self, sort):
        if sort not in self.sorts:
            self.sorts[sort] = Fruit(sort)
        
        return self.sorts[sort]

if __name__ == '__main__':
    fruits = Fruits()
    print fruits.get_fruit('Apple')
    print fruits.get_fruit('Lime')

Ruby
Az alábbiakban egy példa Ruby nyelven, egy távoli szolgáltatástól származó (mint pl. a Google-é) autentikációs token lusta inicializálására.

require 'net/http'
class Blogger
  def auth_token
    @auth_token ||=
      (res = Net::HTTP.post_form(uri, params)) &&
      get_token_from_http_response(res)
  end

  # a 'get_token_from_http_response', az 'uri' és a 'params' később vannak definiálva az osztályban
end

b = Blogger.new
b.instance_variable_get(:@auth_token) # nil értéket ad vissza
b.auth_token # a tokent adja vissza
b.instance_variable_get(:@auth_token) # a tokent adja vissza

Smalltalk
Az alábbiakban egy Smalltalk nyelvű példa, egy tipikus változó-elérést biztosító metódusra, amely a változó értékét lusta inicializáció használatával adja vissza.

    height
        ^height ifNil: [height := 2.0].
A fentiek 'nem-lusta' alternatívája egy inicializációs metódus használata, ami már akkor lefut, amikor az objektum létrejön. Ebben az esetben egy egyszerűbb változó-elérésre szolgáló metódus használható az érték kiolvasására.

    initialize
        height := 2.0

    height
        ^height

Megjegyezzük, hogy a lusta inicializáció a nem objektumorientált nyelvekben is használható.

Scala
A Scala programozási nyelvnek beépített támogatása van a lusta változó inicializációhoz.
 scala> val x = { println("Hello"); 99 }
 Hello
 x: Int = 99
 scala> lazy val y = { println("Hello!!"); 31 }
 y: Int = <lazy>
 scala> y
 Hello!!
 res2: Int = 31
 scala> y
 res3: Int = 31

Jegyzetek

Külső hivatkozások (angolul)
 Article "Java Tip 67: Lazy instantiation - Balancing performance and resource usage" by Philip Bishop and Nigel Warren
 Java code examples
 Use Lazy Initialization to Conserve Resources
 Description from the Portland Pattern Repository
 Lazy Initialization of Application Server Services
 Lazy Inheritance in JavaScript
 Lazy Inheritance in C#

Fordítás 

Programtervezési minták
C Sharp-kódpéldák
Java-kódpéldák