En programación de computadoras , la inicialización perezosa es la táctica de retrasar la creación de un objeto , el cálculo de un valor o algún otro proceso costoso hasta la primera vez que se necesita. Es una especie de evaluación perezosa que se refiere específicamente a la instanciación de objetos u otros recursos.
Por lo general, esto se logra aumentando un método de acceso (o un captador de propiedad) para verificar si un miembro privado, que actúa como caché, ya se ha inicializado. Si es así, se devuelve de inmediato. De lo contrario, se crea una nueva instancia, se coloca en la variable miembro y se devuelve a la persona que llama justo a tiempo para su primer uso.
Si los objetos tienen propiedades que se utilizan con poca frecuencia, esto puede mejorar la velocidad de inicio. El rendimiento promedio promedio del programa puede ser ligeramente peor en términos de memoria (para las variables de condición) y ciclos de ejecución (para verificarlos), pero el impacto de la instanciación de objetos se extiende en el tiempo ("amortizado") en lugar de concentrarse en la fase de inicio de un sistema y, por lo tanto, los tiempos de respuesta medios se pueden mejorar considerablemente.
En el código multiproceso , el acceso a los objetos / estados inicializados de forma diferida debe sincronizarse para protegerse contra las condiciones de carrera .
La "fábrica perezosa"
En una vista de patrón de diseño de software , la inicialización diferida se usa a menudo junto con un patrón de método de fábrica . Esto combina tres ideas:
- Usar un método de fábrica para crear instancias de una clase ( patrón de método de fábrica )
- Almacenar las instancias en un mapa y devolver la misma instancia a cada solicitud de una instancia con los mismos parámetros ( patrón multiton )
- Usar la inicialización diferida para crear una instancia del objeto la primera vez que se solicita (patrón de inicialización diferida)
Ejemplos de
ActionScript 3
El siguiente es un ejemplo de una clase con inicialización diferida implementada en ActionScript :
ejemplos de paquetes . lazyinstantiation { fruta de clase pública { var _typeName privada : String ; instancias var estáticas privadasByTypeName : Dictionary = new Dictionary (); función pública Fruit ( typeName : String ) : void { this . _typeName = typeName ; } función pública obtener typeName () : String { return _typeName ; } función estática pública getFruitByTypeName ( typeName : String ) : Fruit { return instancesByTypeName [ typeName ] || = new Fruit ( typeName ); } función estática pública printCurrentTypes () : void { para cada ( var fruit : Fruit in instanceByTypeName ) { // itera a través de cada traza de valor ( fruit . typeName ); } } } }
Uso básico:
paquete { ejemplos de importación . lazyinstantiation ;public class Main { función pública Main () : void { Fruit . getFruitByTypeName ( "Plátano" ); Fruta . printCurrentTypes (); Fruta . getFruitByTypeName ( "Apple" ); Fruta . printCurrentTypes (); Fruta . getFruitByTypeName ( "Plátano" ); Fruta . printCurrentTypes (); } } }
C
En C, la evaluación diferida normalmente se implementaría dentro de una sola función, o un solo archivo fuente, usando variables estáticas .
En una función:
#include #include #include #include struct fruit { char * name ; estructura fruta * siguiente ; int número ; / * Otros miembros * / };struct fruit * get_fruit ( char * name ) { estructura estática fruit * fruit_list ; static int seq ; estructura fruto * f ; for ( f = fruit_list ; f ; f = f -> next ) if ( 0 == strcmp ( name , f -> name )) return f ; if ( ! ( f = malloc ( sizeof ( estructura fruta )))) return NULL ; if ( ! ( f -> nombre = strdup ( nombre ))) { libre ( f ); return NULL ; } f -> número = ++ seq ; f -> siguiente = lista_de_frutas ; lista_de_frutas = f ; return f ; } / * Código de ejemplo * /int main ( int argc , char * argv []) { int i ; estructura fruto * f ; if ( argc < 2 ) { fprintf ( stderr , "Uso: frutas nombre-fruta [...] \ n " ); salida ( 1 ); } for ( i = 1 ; i < argc ; i ++ ) { if (( f = get_fruit ( argv [ i ]))) { printf ( "Fruit% s: number% d \ n " , argv [ i ], f -> número ); } } return 0 ; }
El uso de un solo archivo de origen permite que el estado se comparta entre múltiples funciones, mientras se sigue ocultando de las funciones no relacionadas.
fruit.h:
#ifndef _FRUIT_INCLUDED_ #define _FRUIT_INCLUDED_struct fruit { char * name ; estructura fruta * siguiente ; int número ; / * Otros miembros * / };struct fruit * get_fruit ( char * name ); void print_fruit_list ( ARCHIVO * archivo );#endif / * _FRUIT_INCLUDED_ * /
fruit.c:
#include #include #include #include #include "fruit.h" estructura estática 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 ( estructura fruta )))) return NULL ; if ( ! ( f -> nombre = strdup ( nombre ))) { libre ( f ); return NULL ; } f -> número = ++ seq ; f -> siguiente = lista_de_frutas ; lista_de_frutas = f ; return f ; }void print_fruit_list ( ARCHIVO * archivo ) { estructura fruta * f ; for ( f = fruit_list ; f ; f = f -> siguiente ) fprintf ( archivo , "% 4d% s \ n " , f -> número , f -> nombre ); }
C Principal:
#include #include #include "fruit.h"int main ( int argc , char * argv []) { int i ; estructura fruto * f ; if ( argc < 2 ) { fprintf ( stderr , "Uso: frutas nombre-fruta [...] \ n " ); salida ( 1 ); } for ( i = 1 ; i < argc ; i ++ ) { if (( f = get_fruit ( argv [ i ]))) { printf ( "Fruit% s: number% d \ n " , argv [ i ], f -> número ); } } printf ( "Se han generado las siguientes frutas: \ n " ); print_fruit_list ( stdout ); return 0 ; }
C#
En .NET Framework 4.0, Microsoft ha incluido una Lazy
clase que se puede utilizar para realizar cargas diferidas. A continuación se muestra un código ficticio que realiza una carga diferida de la claseFruit
var LazyFruit = new Lazy < Fruit > (); Fruta fruta = fruta perezosa . Valor ;
Aquí hay un ejemplo ficticio en C # .
La Fruit
clase en sí no hace nada aquí. La variable de clase _typesDictionary
es un diccionario / mapa que se usa para almacenar Fruit
instancias typeName
.
usando el sistema ; usando System.Collections ; utilizando System.Collections.Generic ; fruta pública clase { cadena privada _typeName ; privada estática IDictionary < string , Fruit > _typesDictionary = nuevo diccionario < string , Fruit > (); Private Fruit ( String typeName ) { this . _typeName = typeName ; } public static Fruit GetFruitByTypeName ( tipo de cadena ) { Fruit fruit ; if (! _typesDictionary . TryGetValue ( type , out fruit )) { // Inicialización perezosa fruit = new Fruit ( tipo ); _typesDictionary . Agregar ( tipo , fruta ); } devolver fruta ; } public static void ShowAll () { if ( _typesDictionary . Count > 0 ) { Console . WriteLine ( "Número de instancias realizadas = {0}" , _typesDictionary . Count ); foreach ( KeyValuePair < string , Fruit > kvp en _typesDictionary ) { Console . WriteLine ( clave kvp . ); } Consola . WriteLine (); } } public Fruit () { // requerido para que la muestra se compile } }class Program { static void Main ( string [] args ) { Fruit . GetFruitByTypeName ( "Plátano" ); Fruta . ShowAll (); Fruta . GetFruitByTypeName ( "Apple" ); Fruta . ShowAll (); // devuelve una instancia preexistente de la primera // vez que se creó Fruit con "Banana" Fruit . GetFruitByTypeName ( "Plátano" ); Fruta . ShowAll (); Consola . ReadLine (); } }
Un ejemplo bastante sencillo de 'rellenar los espacios en blanco' de un patrón de diseño de inicialización diferida, excepto que utiliza una enumeración para el tipo
espacio de nombres DesignPatterns.LazyInitialization { public class LazyFactoryObject { // colección interna de elementos // IDictionaery se asegura de que sean únicos IDictionary privado < LazyObjectSize , LazyObject > _LazyObjectList = new Dictionary < LazyObjectSize , LazyObject > (); // enumeración para pasar el nombre del tamaño requerido // evita pasar cadenas y es parte de LazyObject por delante public enum LazyObjectSize { None , Small , Big , Bigger , Huge } // tipo estándar de objeto que se construirá public struct LazyObject { public LazyObjectSize Size ; pública IList < int > Resultado ; } // toma el tamaño y crea una lista 'cara' privada IList < int > Result ( LazyObjectSize size ) { IList < int > result = null ; cambiar ( tamaño ) { caso LazyObjectSize . Pequeño : resultado = CreateSomeExpensiveList ( 1 , 100 ); romper ; caso LazyObjectSize . Grande : resultado = CreateSomeExpensiveList ( 1 , 1000 ); romper ; caso LazyObjectSize . Más grande : resultado = CreateSomeExpensiveList ( 1 , 10000 ); romper ; caso LazyObjectSize . Enorme : resultado = CreateSomeExpensiveList ( 1 , 100000 ); romper ; caso LazyObjectSize . Ninguno : resultado = nulo ; romper ; predeterminado : resultado = nulo ; romper ; } devolver resultado ; } // no es un artículo caro para crear, pero usted consigue el punto creación // retrasos de algún objeto caro hasta que se necesite privada IList < int > CreateSomeExpensiveList ( int inicio , int final ) { IList < int > resultado = nueva lista < int > ( ); para ( int contador = 0 ; contador < ( fin - inicio ); contador ++) { resultado . Agregar ( inicio + contador ); } devolver resultado ; } public LazyFactoryObject () { // constructor vacío } public LazyObject GetLazyFactoryObject ( LazyObjectSize size ) { // sí, sé que es analfabeto e inexacto LazyObject noGoodSomeOne ; // recupera LazyObjectSize de la lista a través de out, de lo contrario crea uno y lo agrega a la lista if (! _LazyObjectList . TryGetValue ( size , out noGoodSomeOne )) { noGoodSomeOne = new LazyObject (); noGoodSomeOne . Tamaño = tamaño ; noGoodSomeOne . Resultado = esto . Resultado ( tamaño ); _LazyObjectList . Agregar ( tamaño , noGoodSomeOne ); } return noGoodSomeOne ; } } }
C ++
Aquí hay un ejemplo en C ++ .
#include #include #include class Fruit { public : estática Fruit * GetFruit ( const std :: string & type ); PrintCurrentTypes vacío estático (); private : // Nota: el constructor privado obliga a uno a usar static | GetFruit |. Fruta ( const std :: cadena y tipo ) : tipo_ ( tipo ) {} estático std :: map < std :: string , Fruit *> tipos ; std :: string type_ ; };// std :: map estático < std :: string , Fruit *> Fruit :: tipos ;// El método Lazy Factory, obtiene el | Fruit | instancia asociada con un determinado // | tipo |. Crea nuevos según sea necesario. Fruta * Fruta :: GetFruit ( const std :: string & type ) { auto [ it , insertado ] = tipos . emplace ( tipo , nullptr ); if ( insertado ) { it -> second = new Fruit ( tipo ); } Devolver que -> segundos ; }// Por ejemplo, para ver el patrón en acción. void Fruit :: PrintCurrentTypes () { std :: cout << "Número de instancias realizadas =" << tipos . tamaño () << std :: endl ; for ( const auto & [ type , fruit ] : types ) { std :: cout << type << std :: endl ; } std :: cout << std :: endl ; }int main () { Fruit :: GetFruit ( "Plátano" ); Fruta :: PrintCurrentTypes (); Fruit :: GetFruit ( "Apple" ); Fruta :: PrintCurrentTypes (); // Devuelve una instancia preexistente de la primera vez | Fruit | con "Banana" // se creó. Fruit :: GetFruit ( "Plátano" ); Fruta :: PrintCurrentTypes (); }// SALIDA: // // Número de instancias hechas = 1 // Banana // // Número de instancias hechas = 2 // Apple // Banana // // Número de instancias hechas = 2 // Apple // Banana / /
Cristal
class Tipo de captador privado de fruta : Cadena @@ tipos = {} de Cadena => Fruta def initialize ( @type ) end def self . get_fruit_by_type ( tipo : String ) @@ tipos [ tipo ] || = Fruta . nuevo ( tipo ) final def self . show_all pone "Número de instancias realizadas: # { @@ tipos . tamaño } " @@ tipos . cada uno hace | tipo , fruta | pone " # { tipo } " final pone fin def self . tamaño @@ tipos . tamaño final finalFruta . get_fruit_by_type ( "Plátano" ) Fruta . mostrar todoFruta . get_fruit_by_type ( "Manzana" ) Fruta . mostrar todoFruta . get_fruit_by_type ( "Plátano" ) Fruta . mostrar todo
Producción:
Número de instancias realizadas: 1BananaNúmero de instancias realizadas: 2BananamanzanaNúmero de instancias realizadas: 2Bananamanzana
Haxe
Aquí hay un ejemplo en Haxe [1]
clase de frutas { privada estática var _instances = nuevo mapa < cadena , Fruit > (); nombre var público ( predeterminado , nulo ): String ; función pública nueva ( nombre : Cadena ) { esto . nombre = nombre ; } función estática pública getFruitByName ( nombre : String ): Fruit { if ( ! _instances . existe ( nombre )) { _instances . set ( nombre , nueva fruta ( nombre )); } return _instances . obtener ( nombre ); } públicas estáticas de función printAllTypes () { trace ([ a ( clave en _instances . teclas ()) clave ]); } }
Uso
class Test { función pública estática main () { var banana = Fruit . getFruitByName ( "Plátano" ); var apple = Fruta . getFruitByName ( "Apple" ); var banana2 = Fruta . getFruitByName ( "Plátano" ); trace ( banana == banana2 ); // cierto. mismo plátano Fruta . printAllTypes (); // ["Plátano", "Manzana"] } }
Java
Aquí hay un ejemplo en Java .
import java.util.HashMap ; import java.util.Map ; import java.util.Map.Entry ; Programa de clase pública { / ** * @param args * / public static void main ( String [] args ) { Fruit . getFruitByTypeName ( FruitType . banana ); Fruta . showAll (); Fruta . getFruitByTypeName ( FruitType . manzana ); Fruta . showAll (); Fruta . getFruitByTypeName ( FruitType . banana ); Fruta . showAll (); } }enum FruitType { none , apple , banana , }class Fruit { tipos de mapas estáticos privados < FruitType , Fruit > = new HashMap <> (); / ** * Usando un constructor privado para forzar el uso del método de fábrica. * @param type * / private Fruit ( tipo FruitType ) { } / ** * Método Lazy Factory, obtiene la instancia de Fruit asociada con un determinado * tipo. Crea instancias de nuevos según sea necesario. * @param type Cualquier tipo de fruta permitido, por ejemplo, APPLE * @return La instancia de Fruit asociada con ese tipo. * / Pública estática fruta getFruitByTypeName ( FruitType tipo ) { fruta frutas ; // Esto tiene problemas de concurrencia. Aquí la lectura de tipos no está sincronizada, // por lo que se puede llamar a types.put y types.containsKey al mismo tiempo. // No se sorprenda si los datos están corruptos. if ( ! types . containsKey ( type )) { // Inicialización perezosa fruit = new Fruit ( tipo ); tipos . poner ( tipo , fruta ); } else { // OK, está disponible actualmente fruit = types . obtener ( tipo ); } devolver fruta ; } / ** * Método Lazy Factory, obtiene la instancia de Fruit asociada con un determinado * tipo. Crea instancias de nuevos según sea necesario. Utiliza un patrón de bloqueo * verificado dos veces para su uso en entornos altamente concurrentes. * @param type Cualquier tipo de fruta permitido, por ejemplo, APPLE * @return La instancia de Fruit asociada con ese tipo. * / public static Fruit getFruitByTypeNameHighConcurrentVersion ( FruitType type ) { if ( ! types . containsKey ( type )) { synchronized ( types ) { // Verifique nuevamente, después de haber adquirido el bloqueo para asegurarse de que // la instancia no fue creada mientras tanto por otro thread if ( ! types . containsKey ( type )) { // Tipos de inicialización perezosa . put ( tipo , nueva fruta ( tipo )); } } } tipos de retorno . obtener ( tipo ); } / ** * Muestra todas las frutas ingresadas. * / public static void showAll () { if ( tipos . tamaño () > 0 ) { Sistema . fuera . println ( "Número de instancias realizadas =" + tipos . tamaño ()); para ( Entrada < FruitType , Fruit > entrada : tipos . entrySet ()) { Cadena fruta = entrada . getKey (). toString (); fruta = Carácter . toUpperCase ( fruta . charAt ( 0 )) + fruta . subcadena ( 1 ); Sistema . fuera . println ( fruta ); } Sistema . fuera . println (); } } }
Producción
Número de instancias realizadas = 1BananaNúmero de instancias realizadas = 2BananamanzanaNúmero de instancias realizadas = 2Bananamanzana
JavaScript
Aquí hay un ejemplo en JavaScript .
var Fruta = ( función () { tipos de var = {}; función Fruta () {}; // cuenta las propiedades propias en el objeto function count ( obj ) { return Object . claves ( obj ). longitud ; } var _static = { getFruit : function ( type ) { if ( typeof types [ type ] == 'undefined' ) { types [ type ] = new Fruit ; } tipos de retorno [ tipo ]; }, printCurrentTypes : function () { consola . log ( 'Número de instancias realizadas:' + recuento ( tipos )); para ( tipo var en tipos ) { consola . log ( tipo ); } } }; return _static ;}) ();Fruta . getFruit ( 'Apple' ); Fruta . printCurrentTypes (); Fruta . getFruit ( 'Plátano' ); Fruta . printCurrentTypes (); Fruta . getFruit ( 'Apple' ); Fruta . printCurrentTypes ();
Producción
Número de instancias realizadas: 1manzanaNúmero de instancias realizadas: 2manzanaBananaNúmero de instancias realizadas: 2manzanaBanana
PHP
Aquí hay un ejemplo de inicialización diferida en PHP 7.4:
php header ( 'Content-Type: text / plain; charset = utf-8' );class Fruit { cadena privada $ tipo ; matriz estática privada $ tipos = matriz (); función privada __construct ( cadena $ tipo ) { $ esto -> tipo = $ tipo ; } función estática pública getFruit ( string $ type ) { // La inicialización perezosa tiene lugar aquí si ( ! isset ( self :: types [ $ type ])) { self :: types [ $ type ] = new Fruit ( $ type ); } return self :: types [ $ tipo ]; } función estática pública printCurrentTypes () : void { echo 'Número de instancias realizadas:' . contar ( self :: tipos ) . " \ n " ; foreach ( array_keys ( self :: types ) as $ key ) { echo " $ key \ n " ; } echo " \ n " ; } } Fruit :: getFruit ( 'Apple' ); Fruit :: printCurrentTypes ();Fruit :: getFruit ( 'Plátano' ); Fruit :: printCurrentTypes ();Fruit :: getFruit ( 'Apple' ); Fruit :: printCurrentTypes ();/ * SALIDA:Número de instancias realizadas: 1 AppleNúmero de instancias realizadas: 2 Apple BananaNúmero de instancias realizadas: 2 Apple Banana * /
Pitón
Aquí hay un ejemplo en Python .
clase Fruta : def __init__ ( self , item : str ) -> None : self . item = item clase Frutos : def __init__ ( self ) -> None : self . items = {} def get_fruit ( self , item : str ) -> Fruit : si el elemento no está en self . artículos : uno mismo . items [ item ] = Fruta ( item ) volver a sí mismo . artículos [ artículo ]if __name__ == "__main__" : frutas = Frutas () print ( frutas . get_fruit ( "Apple" )) print ( frutas . get_fruit ( "Lime" ))
Rubí
Aquí hay un ejemplo en Ruby , de inicializar perezosamente un token de autenticación desde un servicio remoto como Google. La forma en que se almacena en caché @auth_token también es un ejemplo de memorización .
requiere la clase 'net / http' 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 y params se definen más adelante al final de la claseb = Blogger . nuevo b . instance_variable_get ( : @auth_token ) # devuelve nulo b . auth_token # devuelve el token b . instance_variable_get ( : @auth_token ) # devuelve el token
Scala
Scala tiene soporte integrado para el inicio de variables diferidas. [2]
scala > val x = { println ( "Hola" ); 99 } Hola x : Int = 99 scala > lazy val y = { println ( "¡Hola!" ); 31 } y : Int = < perezoso > scala > y Hola !! res2 : Int = 31 scala > y res3 : Int = 31
Charla
Aquí hay un ejemplo en Smalltalk , de un método de acceso típico para devolver el valor de una variable usando la inicialización diferida.
altura ^ altura ifNil: [ altura : = 2.0 ] .
La alternativa 'no perezosa' es usar un método de inicialización que se ejecuta cuando se crea el objeto y luego usar un método de acceso más simple para obtener el valor.
inicializar altura : = 2.0 altura ^ altura
Tenga en cuenta que la inicialización diferida también se puede utilizar en lenguajes no orientados a objetos .
Ciencias de la computación teóricas
En el campo de la informática teórica , la inicialización diferida [3] (también llamada matriz diferida ) es una técnica para diseñar estructuras de datos que pueden funcionar con memoria que no necesita inicializarse. Específicamente, suponer que tenemos acceso a una tabla T de n células de memoria sin inicializar (numeradas de 1 a n ), y desea asignar m células de esta matriz, por ejemplo, queremos asignar T [ k i ]: = v i para pares ( k 1 , v 1 ), ..., ( k m , v m ) siendo todos los k i diferentes. La técnica de inicialización diferida nos permite hacer esto en solo O ( m ) operaciones, en lugar de gastar O ( m + n ) operaciones para inicializar primero todas las celdas de la matriz. La técnica consiste simplemente en asignar una tabla V que almacene los pares ( k i , v i ) en algún orden arbitrario, y escribir para cada i en la celda T [ k i ] la posición en V donde se almacena la clave k i , dejando las otras células de T no inicializadas. Esto se puede usar para manejar consultas de la siguiente manera: cuando buscamos algo de k en la celda T [ k ] , podemos verificar si k está en el rango {1, ..., m }: si no lo está, entonces T [ k ] no está inicializado. De lo contrario, verificamos V [ T [ k ]] y verificamos que el primer componente de este par sea igual a k . Si no es así, entonces T [ k ] no está inicializado (y por accidente cayó en el rango {1, ..., m }). De lo contrario, sabemos que T [ k ] es de hecho una de las celdas inicializadas, y el valor correspondiente es el segundo componente del par.
Ver también
Referencias
- ^ "Inicialización perezosa - Patrones de diseño - Libro de cocina del lenguaje de programación Haxe" . 2018-01-11 . Consultado el 9 de noviembre de 2018 .
- ^ Pollak, David (25 de mayo de 2009). Comenzando Scala . ISBN 9781430219897.
- ^ Moret, BME; Shapiro, HD (1991). Algoritmos de P a NP, Volumen 1: Diseño y eficiencia . Compañía editorial de Benjamin / Cummings. págs. 191-192. ISBN 0-8053-8008-6.
enlaces externos
- Artículo " Consejo 67 de Java: instanciación diferida : equilibrio entre el rendimiento y el uso de recursos" de Philip Bishop y Nigel Warren
- Ejemplos de código Java
- Utilice la inicialización diferida para conservar recursos
- Descripción del repositorio de patrones de Portland
- Inicialización diferida de los servicios del servidor de aplicaciones
- Herencia perezosa en JavaScript
- Herencia perezosa en C #