Lazy initialization

Z Wikipedie, otevřené encyklopedie

Odložená inicializace (anglicky Lazy Initialization) je jeden z návrhových vzoru využívaných v programování. Jíž z názvu lze postřehnout, že se jedná o odložení vytváření objektu, počítání hodnoty nebo provádění nějakého procesu, až do chvíle, kdy ho budeme poprvé potřebovat.

Účel[editovat | editovat zdroj]

V programování se často setkáváme se situacemi, kdy na jedné straně pracujeme s několika velkými objekty a na druhé potřebujeme program zoptimalizovat. Je sice možnost ty objekty inicializovat již od začátku, ale co když ho budeme potřebovat jenom jednou a někdy ke konci programu? Nebo svými podmínkami ten objekt celý program přeskočí a nevyužije vůbec? Tudíž vytváření takového objektu hned od začátku je nevhodné a východiskem z této situaci je tak zvaná odložená inicializace.

Odložená inicializace je pak často využívá s jinými návrhovými vzory, jako jsou Tovární metoda (Factory Method), Jedináček (Singleton) nebo Proxy.

Plusy[editovat | editovat zdroj]

  • Inicializace objektu probíhá ve chvíli kdy je opravdu nutná
  • Zrychluje se počáteční inicializace

Minusy[editovat | editovat zdroj]

  • Není možné vytvořit pořadí inicializovaných objektů
  • Vzniká odezva při dotazování se na objekt

Základní implementace[editovat | editovat zdroj]

Nejobyčejnější Java kód bez odložené inicializace by mohl vypadat asi takto:

// Normální statická inicializace (nikoli odložená)
private static final Foo foo = new Foo();

public static Foo getFoo() {
	return foo;
}

Objekt je vytvářen hned od začátku. Metoda getFoo() je rychlá ale nesynchronizovaná. Pro jednoduché aplikace to bohatě stačí. Kdybychom ale přece potřebovali pracovat s odloženou inicializací, museli bychom brat ohled i na aplikaci pracující s vlákny.

// Řádně synchronizovaná odložená inicializace
private static Foo foo = null;

public static synchronized Foo getFoo() {
	if (foo == null)
		foo = new Foo();
	return foo;
}

Ve výše uvedené podobě je už vidět, že se objekt nevytvářel hned od začátku, ale počká se na zavolání synchronizované metody getFoo(), která vrací objekt, pokud existuje, anebo pokud se jedná o první volání, ho vytvoří. Takový způsob ale zahrnuje i náklady na synchronizaci (když v poslední době poměrně malé). Kdybychom to chtěli optimalizovat i v tomto směru, můžeme využít i jiný způsob.

// Idiom držitelské třídy inicializace podle potřeby
private static class FooHolder {
	static final Foo foo = new Foo();
}

public static Foo getFoo() {
	return FooHolder.foo;
}

Když je metoda getFoo() volána poprvé, přečte atribut FooHolder.foo, čímž způsobí inicializaci třídy FooHolder. Takže se zase jedná o odloženou inicializaci, ale zároveň vynecháváme potřebu synchronizovat, protože volání metody zajišťuje pouze přístup k atributu. Nevýhodou takového triku ale je, že pracuje pouze se statickými atributy.

Příklad využití[editovat | editovat zdroj]

Jedna z možných situací, kdy je možné používat odloženou inicializaci je práce s poli. Aby bylo vhodné využívat takový vzor, měla by práce s polem splňovat alespoň tyto podmínky

  • Vytváření pole by bylo velmi náročné
  • Pole má volitelný charakter, jinými slovy může, ale nemusí se s ním pracovat.

Představme si, že máme košík, do kterého budeme vkládat různá ovoce. Zároveň ale nevíme, kolik jich tam bude, ani jakého budou druhu.

Jak je vidět, vytváření takového pole je poměrně náročné. Na druhou stranu pomoci Odložené inicializaci se problém zjednodušuje.

import java.util.*;

public class Ovoce
{
    private static final Map<String,Ovoce> typy = new HashMap<String,Ovoce>();
    private final String typ;

    // Používá se privátní konstruktor, aby zamezil vytváření z venčí   
    private Ovoce(String typ) {
      this.typ = typ;
    }
 
    /**
     * Odložená inicializace vrací instanci třídy Ovoce na základě jejího
     * typu. Inicializuje nový, pokud je to nutno.
     * @param type jakýkoliv String, určující typ ovoce (např."jablko")
     * @return instanci Ovoce na základě druhu ovoce.
     */
    public static synchronized Ovoce getOvoce(String typ) {
      if(!typy.containsKey(typ))
      {
        typy.put(typ, new Ovoce(typ)); // Zde je Odložená inicializace
      }
      return typy.get(typ);
    }
}

Související články[editovat | editovat zdroj]

Externí odkazy[editovat | editovat zdroj]