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 */