Lat initiering
Inom datorprogrammering är lat initialisering taktiken att fördröja skapandet av ett objekt , beräkningen av ett värde eller någon annan dyr process tills första gången det behövs. Det är en sorts lat utvärdering som specifikt hänvisar till instansieringen av objekt eller andra resurser.
Detta åstadkoms vanligtvis genom att utöka en accessormetod (eller egenskapsgetter) för att kontrollera om en privat medlem, som fungerar som en cache, redan har initierats. Om den har det, returneras den direkt. Om inte skapas en ny instans, placeras i medlemsvariabeln och returneras till den som ringer just-in-time för första användning.
Om objekt har egenskaper som sällan används kan detta förbättra starthastigheten. Genomsnittlig programprestanda kan vara något sämre när det gäller minne (för tillståndsvariablerna) och exekveringscykler (för att kontrollera dem), men effekten av objektinstansieringen sprids i tiden ("amortiseras") snarare än koncentreras till startfasen av ett system, och därmed kan mediansvarstiderna förbättras avsevärt.
I flertrådad kod måste åtkomst till latinitierade objekt/tillstånd synkroniseras för att skydda mot tävlingsförhållanden .
"Den lata fabriken"
I en mjukvarudesignmönstervy används ofta lat initialisering tillsammans med ett fabriksmetodmönster . Detta kombinerar tre idéer:
- Använda en fabriksmetod för att skapa instanser av en klass ( fabriksmetodmönster )
- Lagra instanserna i en karta och returnera samma instans till varje begäran om en instans med samma parametrar ( multitonmönster )
- Använda lat initiering för att instansiera objektet första gången det efterfrågas (lat initieringsmönster)
Exempel
ActionScript 3
Följande är ett exempel på en klass med lat initialisering implementerad i ActionScript :
paketexempel . _ lazyinstantiation { public class Fruit { private var _typeName : String ; private static var instancesByTypeName : Dictionary = new Dictionary (); public function Fruit ( typeName : String ) : void { this . _typeName = typName ; } public function get typeName () : String { return _typeName ; } public static function getFruitByTypeName ( typName : String ) : Fruit { return instanserByTypeName [ typName ] ||= new Fruit ( typName ); } public static function printCurrentTypes () : void { for each ( var fruit : Fruit in instancesByTypeName ) { // itererar genom varje värdespår ( fruit . typeName ) ; } } } }
Grundläggande användning:
paket { importexempel . _ latinstantiation ; public class Main { public function Main () : void { Fruit . getFruitByTypeName ( "Banana" ); Frukt . printCurrentTypes (); Frukt . getFruitByTypeName ( "Apple" ); Frukt . printCurrentTypes (); Frukt . getFruitByTypeName ( "Banana" ); Frukt . printCurrentTypes (); } } }
C
I C skulle lat utvärdering normalt implementeras i en enda funktion, eller en enda källfil, med hjälp av statiska variabler .
I en funktion:
0
0
#include <string.h> #include <stdlib.h> #include <stddef.h> #include <stdio.h> struct fruit { char * name ; struktur frukt * nästa ; int nummer ; /* Andra medlemmar */ }; struktur frukt * get_frukt ( char * namn ) { statisk struktur frukt * frukt_lista ; statisk int seq ; struktur frukt * f ; for ( f = fruktlista ; f ; f = f -> nästa ) if ( == strcmp ( namn , f -> namn )) returnera f ; if ( ! ( f = malloc ( storleken på ( strukturfrukt ) ))) returnera NULL ; if ( ! ( f -> namn = strdup ( namn ))) { gratis ( f ); returnera NULL ; } f -> tal = ++ följ ; f -> nästa = fruktlista ; fruktlista = f ; returnera f ; } /* Exempelkod */ int main ( int argc , char * argv []) { int i ; struktur frukt * f ; if ( argc < 2 ) { fprintf ( stderr , "Användning: frukt fruktnamn [...] \n " ); utgång ( 1 ); } for ( i = 1 ; i < argc ; i ++ ) { if (( f = get_fruit ( argv [ i ]))) { printf ( "Fruit %s: number %d \n " , argv [ i ], f -> nummer ); } } returnera ; }
Genom att använda en enda källfil i stället kan tillståndet delas mellan flera funktioner, samtidigt som det döljs från icke-relaterade funktioner.
fruit.h:
#ifndef _FRUIT_INCLUDED_ #define _FRUIT_INCLUDED_ struct fruit { char * name ; struktur frukt * nästa ; int nummer ; /* Andra medlemmar */ }; struktur frukt * get_frukt ( char * namn ); void print_fruit_list ( FILE * fil ); #endif /* _FRUIT_INCLUDED_ */
frukt.c:
0
#inkludera <string.h> #inkludera <stdlib.h> #inkludera <stddef.h> #inkludera <stdio.h> #inkludera "fruit.h" statisk struktur fruit * fruit_list ; statisk int seq ; struktur frukt * get_frukt ( char * namn ) { struktur frukt * f ; for ( f = fruktlista ; f ; f = f -> nästa ) if ( == strcmp ( namn , f -> namn )) returnera f ; if ( ! ( f = malloc ( storleken på ( strukturfrukt ) ))) returnera NULL ; if ( ! ( f -> namn = strdup ( namn ))) { gratis ( f ); returnera NULL ; } f -> tal = ++ följ ; f -> nästa = fruktlista ; fruktlista = f ; returnera f ; } void print_fruit_list ( FILE * file ) { struct fruit * f ; för ( f = fruktlista ; f ; f = f -> nästa ) fprintf ( fil , "%4d %s \n " , f -> nummer , f -> namn ); }
main.c:
0
#include <stdlib.h> #include <stdio.h> #include "fruit.h" int main ( int argc , char * argv []) { int i ; struktur frukt * f ; if ( argc < 2 ) { fprintf ( stderr , "Användning: frukt fruktnamn [...] \n " ); utgång ( 1 ); } for ( i = 1 ; i < argc ; i ++ ) { if (( f = get_fruit ( argv [ i ]))) { printf ( "Fruit %s: number %d \n " , argv [ i ], f -> nummer ); } } printf ( "Följande frukter har genererats: \n " ); print_fruit_list ( stdout ); återvända ; }
C#
I .NET Framework 4.0 har Microsoft inkluderat en Lazy
-klass som kan användas för att göra lazy loading. Nedan är en dummy-kod som gör lat inläsning av Class Fruit
var lazyFruit = new Lazy < Fruit >(); Frukt frukt = lazyFruit . Värde ;
Här är ett dummy-exempel i C# .
Fruit gör ingenting här, klassvariabeln _typesDictionary
är en Dictionary/Map som används för att lagra Fruit-
instanser
efter typName
.
0
använder System ; använder System.Collections ; använder System.Collections.Generic ; public class Fruit { private string _typeName ; privat statisk ID-bok < string , Fruit > _typesDictionary = ny ordbok < sträng , Fruit >(); private Fruit ( String typeName ) { this . _typeName = typName ; } public static Fruit GetFruitByTypeName ( strängtyp ) { Fruit fruit ; _ if (! _typesDictionary . TryGetValue ( typ , out fruit )) { // Lazy initialization fruit = new Fruit ( typ ); _typesOrdbok . Lägg till ( typ , frukt ); } returnera frukt ; } public static void ShowAll () { if ( _typesDictionary . Count > ) { Console . WriteLine ( "Antal gjorda instanser = {0}" , _typesDictionary . Count ); foreach ( KeyValuePair < string , Fruit > kvp i _typesDictionary ) { Console . WriteLine ( kvp . Key ); } Konsol . WriteLine (); } } public Fruit () { // krävs så att provet kompileras } } class Program { static void Main ( sträng [] args ) { Fruit . GetFruitByTypeName ( "Banana" ); Frukt . Visa alla (); Frukt . GetFruitByTypeName ( "Apple" ); Frukt . Visa alla (); // returnerar redan existerande instans från första // gången Fruit med "Banana" skapades Fruit . GetFruitByTypeName ( "Banana" ); Frukt . Visa alla (); Konsol . ReadLine (); } }
Ett ganska okomplicerat "fyll-i-delarna"-exempel på ett designmönster för Lazy Initialization, förutom att detta använder en uppräkning för typen
0
namnutrymme DesignPatterns.LazyInitialization ; public class LazyFactoryObject { //intern samling av objekt //IDictionary ser till att de är unika privata IDictionary < LazyObjectSize , LazyObject > _LazyObjectList = ny ordbok < LazyObjectSize , LazyObject >(); //enum för att skicka namn på storlek som krävs //undviker att skicka strängar och är en del av LazyObject ahead public enum LazyObjectSize { None , Small , Big , Bigger , Huge } //standardtyp av objekt som kommer att konstrueras public struct LazyObject { public LazyObjectSize Storlek ; offentlig IList < int > Resultat ; } //tar storlek och skapa en 'dyr' lista privat IList < int > Resultat ( LazyObjectSize size ) { IList < int > result = null ; switch ( storlek ) { case LazyObjectSize . Small : result = CreateSomeExpensiveList ( 1,100 ) ; _ bryta ; fall LazyObjectSize . Big : result = CreateSomeExpensiveList ( 1 , 1000 ); bryta ; fall LazyObjectSize . Större : result = CreateSomeExpensiveList ( 1 , 10000 ); bryta ; fall LazyObjectSize . Huge : result = CreateSomeExpensiveList ( 1 , 100000 ); bryta ; fall LazyObjectSize . Ingen : resultat = null ; bryta ; default : result = null ; bryta ; } returnera resultat ; } //inte ett dyrt objekt att skapa, men du får poängen //fördröjer skapandet av något dyrt objekt tills det behövs privat IList < int > CreateSomeExpensiveList ( int start , int end ) { IList < int > result = new List < int > (); for ( int räknare = ; räknare < ( slut - start ); räknare ++) { resultat . Lägg till ( start + räknare ); } returnera resultat ; } public LazyFactoryObject () { //empty constructor } public LazyObject GetLazyFactoryObject ( LazyObjectSize size ) { //ja, jag vet att det är analfabet och felaktigt LazyObject noGoodSomeOne ; //hämtar LazyObjectSize från listan via ut, annars skapar en och lägger till den i listan om (! _LazyObjectList . TryGetValue ( size , out noGoodSomeOne )) { noGoodSomeOne = new LazyObject (); noGoodSomeOne . Storlek = storlek ; noGoodSomeOne . Resultat = detta . Resultat ( storlek ); _LazyObjectList . Lägg till ( storlek , noGoodSomeOne ); } returnera noGoodSomeOne ; } }
C++
Här är ett exempel i C++ .
#include <iostream> #include <map> #include <string> class Fruit { public : static Fruit * GetFruit ( const std :: string & type ); statisk tomrum PrintCurrentTypes (); privat : // Notera: privat konstruktor tvingar en att använda statisk |GetFruit|. Fruit ( const std :: sträng & typ ) : type_ ( typ ) {} statisk std :: map < std :: sträng , Fruit *> typer ; std :: strängtyp_ ; _ }; // static std :: map < std :: string , Fruit *> Fruit :: types ; // Lazy Factory-metoden, får |Fruit| instans associerad med en viss // |typ|. Skapar nya efter behov. Fruit * Fruit::GetFruit ( const std :: sträng & typ ) { auto [ it , infogat ] = typer . emplace ( typ , nullptr ); if ( insatt ) { it -> second = new Fruit ( typ ); } returnera det -> sekund ; } // Till exempel för att se mönster i aktion. void Fruit::PrintCurrentTypes () { std :: cout << "Antal gjorda instanser = " << typer . storlek () << std :: endl ; for ( const auto & [ typ , frukt ] : typer ) { std :: cout << typ << std :: endl ; } std :: cout << std :: endl ; } int main () { Fruit :: GetFruit ( "Banan" ); Fruit :: PrintCurrentTypes (); Fruit :: GetFruit ( "Apple" ); Fruit :: PrintCurrentTypes (); // Returnerar redan existerande instans från första gången |Fruit| med "Banana" skapades //. Fruit :: GetFruit ( "Banan" ); Fruit :: PrintCurrentTypes (); } // OUTPUT: // // Antal gjorda instanser = 1 // Banan // // Antal gjorda instanser = 2 // Äpple // Banan // // Antal gjorda instanser = 2 // Äpple // Banan //
Kristall
klass Frukt privat getter typ : String @@types = {} av String => Frukt def initialize ( @type ) end def self . get_fruit_by_type ( typ : String ) @@types [ typ ] ||= Frukt . ny ( typ ) slut def själv . show_all sätter "Antal gjorda instanser: #{ @@typer . storlek } " @@typer . varje gör | typ , frukt | sätter " #{ typ } " end sätter slut def self . storlek @@typer . storlek slutänd Frukt . _ get_fruit_by_type ( "Banan" ) Frukt . show_all Frukt . get_fruit_by_type ( "Äpple" ) Frukt . show_all Frukt . get_fruit_by_type ( "Banan" ) Frukt . visa allt
Produktion:
Antal gjorda instanser: 1 Banan Antal gjorda instanser: 2 Banana Apple Antal gjorda instanser: 2 Banana Apple
Haxe
Här är ett exempel i Haxe
class Fruit { private static var _instances = new Map < String , Fruit >(); public var name ( default , null ): String ; public function new ( namn : String ) { this . namn = namn ; } offentlig statisk funktion getFruitByName ( namn : String ): Fruit { if ( ! _instances . exists ( name )) { _instances . set ( namn , ny Frukt ( namn )); } returnerar _instanser . få ( namn ); } offentlig statisk funktion printAllTypes () { spåra ([ för ( nyckel in _instanser . nycklar ()) nyckel ]); } }
Användande
class Test { public static function main () { var banana = Fruit . getFruitByName ( "Banana" ); var äpple = Frukt . getFruitByName ( "Apple" ); var banan2 = Frukt . getFruitByName ( "Banana" ); spår ( banan == banan2 ); // Sann. samma banan Frukt . printAllTypes (); // ["Banan","Äppel"] } }
Java
Här är ett exempel i Java .
0
0
importera java.util.HashMap ; importera java.util.Map ; importera java.util.Map.Entry ; public class Program { /** * @param args */ public static void main ( String [] args ) { Fruit . getFruitByTypeName ( FruitType . banana ); Frukt . showAll (); Frukt . getFruitByTypeName ( FruitType . apple ); Frukt . showAll (); Frukt . getFruitByTypeName ( FruitType . banana ); Frukt . showAll (); } } enum FruitType { none , apple , banana , } class Fruit { private static Map < FruitType , Fruit > types = new HashMap <> (); /** * Att använda en privat konstruktör för att tvinga fram fabriksmetoden. * @param typ */ private Fruit ( FruitType type ) { } /** * Lazy Factory-metoden, hämtar Fruit-instansen associerad med en viss *-typ. Instantierar nya efter behov. * @param typ Alla tillåtna frukttyper, t.ex. APPLE * @return Fruit-instansen som är associerad med den typen. */ public static Fruit getFruitByTypeName ( FruitType- typ ) { Fruit fruit ; // Detta har samtidighetsproblem. Här är inte läs-till-typerna synkroniserade, // så types.put och types.containsKey kan anropas samtidigt. // Bli inte förvånad om informationen är skadad. if ( ! typer . containsKey ( typ )) { // Lazy initialization fruit = new Fruit ( typ ); typer . sätta ( typ , frukt ); } else { // OK, den är tillgänglig för närvarande frukt = typer . få ( typ ); } returnera frukt ; } /** * Lazy Factory-metoden, hämtar Fruit-instansen associerad med en viss * typ. Instantierar nya efter behov. Använder dubbelkontrollerat låsmönster * för användning i mycket samtidiga miljöer. * @param typ Alla tillåtna frukttyper, t.ex. APPLE * @return Fruit-instansen som är associerad med den typen. */ public static Fruit getFruitByTypeNameHighConcurrentVersion ( FruitType type ) { if ( ! typer . containsKey ( typ )) { synchronized ( types ) { // Kontrollera igen, efter att ha skaffat låset för att se till att // instansen inte skapades under tiden av en annan thread if ( ! typer . containsKey ( typ )) { // Lata initialiseringstyper . put ( typ , ny Frukt ( typ )); } } } returtyper . _ få ( typ ); } /** * Visar alla inmatade frukter. */ public static void showAll () { if ( types . size () > ) { System . ut . println ( "Antal gjorda instanser = " + typer . storlek ()); for ( Entry < FruitType , Fruit > entry : types . entrySet ()) { String fruit = entry . getKey (). toString (); frukt = Karaktär . till versaler ( frukt . charAt ( )) + frukt . delsträng ( 1 ); System . ut . println ( frukt ); } System . ut . println (); } } }
Produktion
Antal gjorda instanser = 1 Banan Antal gjorda instanser = 2 Banana Apple Antal gjorda instanser = 2 Banana Apple
JavaScript
Här är ett exempel i JavaScript .
var Fruit = ( function () { var types = {}; function Fruit () {}; // räkna egna egenskaper i objekt funktion count ( obj ) { return Object . keys ( obj ). length ; } var _static = { getFruit : funktion ( typ ) { if ( typ av typer [ typ ] == 'odefinierad' ) { typer [ typ ] = ny Frukt ; } returnerar typer [ typ ]; }, printCurrentTypes : function () { console . log ( 'Antal av gjorda instanser: ' + count ( types )); for ( var typ in types ) { console . log ( type ); } } }; return _static ; })(); Frukt . getFruit ( 'Apple' ); Frukt . printCurrentTypes (); Frukt . getFruit ( 'Banan' ); Frukt . printCurrentTypes (); Frukt . getFruit ( 'Apple' ); Frukt . printCurrentTypes ();
Produktion
Antal gjorda instanser: 1 Apple Antal gjorda instanser: 2 Apple Banana Antal gjorda instanser: 2 Apple Banana
PHP
Här är ett exempel på lat initiering i PHP 7.4:
<?php header ( 'Content-Type: text/plain; charset=utf-8' ); klass Fruit { privat sträng $type ; privat statisk array $typer = array (); privat funktion __construct ( sträng $type ) { $this -> type = $type ; } public static function getFruit ( sträng $type ) { // Lazy initialisering sker här if ( ! isset ( self :: types [ $type ])) { self :: types [ $type ] = new Fruit ( $type ); } returnera själv :: typer [ $typ ]; } public static function printCurrentTypes () : void { echo 'Antal gjorda instanser: ' . räkna ( själv :: typer ) . " \n " ; foreach ( array_keys ( self :: typer ) som $key ) { echo " $key\n " ; } echo " \n " ; } } Fruit :: getFruit ( 'Apple' ); Fruit :: printCurrentTypes (); Fruit :: getFruit ( 'Banan' ); Fruit :: printCurrentTypes (); Fruit :: getFruit ( 'Apple' ); Fruit :: printCurrentTypes (); /* OUTPUT: Antal gjorda instanser: 1 Apple Antal gjorda instanser: 2 Apple Banana Antal gjorda instanser: 2 Apple Banana */
Pytonorm
Här är ett exempel i Python .
klass Fruit : def __init__ ( själv , objekt : str ) -> Ingen : själv . item = item class Fruits : def __init__ ( self ) -> None : self . objekt = {} def get_fruit ( själv , objekt : str ) -> Frukt : om objektet inte finns i själv . föremål : själv . items [ item ] = Frukt ( artikel ) returnerar själv . items [ item ] if __name__ == "__main__" : fruits = Frukt () print ( fruits . get_fruit ( "Apple" )) print ( fruits . get_fruit ( "Lime" ))
Rubin
Här är ett exempel i Ruby , på att lattigt initiera en autentiseringstoken från en fjärrtjänst som Google. Sättet som @auth_token cachelagras på är också ett exempel på memoisering .
kräver 'net/http' klass Blogger def auth_token @auth_token ||= ( res = Net :: HTTP . post_form ( uri , params )) && get_token_from_http_response ( res ) end # get_token_from_http_response, uri och params definieras senare i klassens slut b = Bloggare . nya b . instance_variable_get ( :@auth_token ) # returnerar noll b . auth_token # returnerar token b . instance_variable_get ( :@auth_token ) # returnerar token
Scala
Scala har inbyggt stöd för lazy variabel initiering.
scala > val x = { println ( "Hej" ); 99 }
Hej x : Int = 99 scala > lazy val y = { println ( "Hej!!" ); 31 }
y : Int = < lazy > scala > y
Hej !! res2 : Int = 31 scala > y
res3 : Int = 31
Småprat
Här är ett exempel i Smalltalk på en typisk accessormetod för att returnera värdet på en variabel med hjälp av lat initialisering.
höjd ^ höjd om Ingen: [ höjd := 2,0 ] .
Alternativet "icke lata" är att använda en initialiseringsmetod som körs när objektet skapas och sedan använda en enklare accessormetod för att hämta värdet.
initialisera höjd := 2,0 höjd ^ höjd
Observera att lat initialisering också kan användas i icke -objektorienterade språk .
Teoretisk datavetenskap
Inom området teoretisk datavetenskap är lazy initiering (även kallad en lazy array ) en teknik för att designa datastrukturer som kan fungera med minne som inte behöver initieras. Antag specifikt att vi har tillgång till en tabell T med n oinitierade minnesceller (numrerade från 1 till n ), och vill tilldela m celler i denna array, t.ex. vill vi tilldela T [ k i ] := v i för par ( k 1 , v 1 ), ..., ( k m , v m ) där alla k i är olika. Den lata initieringstekniken tillåter oss att göra detta i bara O( m )-operationer, snarare än att spendera O( m + n )-operationer för att först initiera alla arrayceller. Tekniken är helt enkelt att allokera en tabell V som ki lagrar paren ( ki , v i ) i någon godtycklig ordning, och att skriva för varje i i cellen T [ ] positionen i V där nyckeln k i är lagrad, vilket lämnar de andra cellerna i T oinitialiserade. Detta kan användas för att hantera frågor på följande sätt: när vi slår upp cell T [ k ] för vissa k , kan vi kontrollera om k är i intervallet {1, ..., m }: om det inte är det, då T [ k ] är oinitierad. Annars kontrollerar vi V [ T [ k ]] och verifierar att den första komponenten i detta par är lika med k . Om den inte är det, T [ k ] oinitierad (och råkade bara av en slump falla inom intervallet {1, ..., m }). Annars vet vi att T [ k ] verkligen är en av de initierade cellerna, och motsvarande värde är den andra komponenten i paret.
Se även
externa länkar
- Artikel " Java Tips 67: Lazy instansiering - Balansering av prestanda och resursanvändning" av Philip Bishop och Nigel Warren
- Exempel på Java-kod
- Använd Lazy Initialization för att spara resurser
- Beskrivning från Portland Pattern Repository
- Lat initiering av applikationsservertjänster
- Lazy Arv i JavaScript
- Lazy Inheritance i C#