stringtranslate.com

Inicialización diferida

En programación de computadoras , la inicialización diferida 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 un tipo de evaluación diferida que se refiere específicamente a la creación de instancias de objetos u otros recursos.

Esto generalmente se logra aumentando un método de acceso (o captador de propiedad) para verificar si un miembro privado, que actúa como caché, ya se ha inicializado. Si es así, se devuelve inmediatamente. 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 rara vez se utilizan, esto puede mejorar la velocidad de inicio. El rendimiento medio del programa puede ser ligeramente peor en términos de memoria (para las variables de condición) y ciclos de ejecución (para comprobarlas), pero el impacto de la creación de instancias de objetos se distribuye en el tiempo ("amortizado") en lugar de concentrarse en la fase de inicio de un sistema y, por tanto, los tiempos medios de respuesta se pueden mejorar considerablemente.

En código multiproceso , el acceso a objetos/estados con inicialización diferida debe sincronizarse para protegerse contra condiciones de carrera .

La "fábrica perezosa"

En una vista de patrón de diseño de software , la inicialización diferida a menudo se usa junto con un patrón de método de fábrica . Esto combina tres ideas:

Ejemplos

ActionScript 3

El siguiente es un ejemplo de una clase con inicialización diferida implementada en ActionScript :

ejemplos de paquetes lazyinstantiation { clase pública Fruta { var privada _typeName : Cadena ; instancias de var estáticas privadasByTypeName : Diccionario = nuevo Diccionario (); función pública Fruta ( tipoNombre : Cadena ) : void { this . _typeName = tipoName ; } función pública obtener nombretipo () : cadena { retorno _nombretipo ; } función estática pública getFruitByTypeName ( typeName : String ) : Fruit { return instanciasByTypeName [ typeName ] ||= new Fruit ( typeName ); } función estática pública printCurrentTypes () : void { para cada ( var fruta : Fruta en instanciasByTypeName ) { // itera a través de cada seguimiento de valor ( fruta . typeName ); } } } }                                        

Uso básico:

paquete { ejemplos de importación . instanciación diferida ; clase pública Principal { función pública Principal () : vacío { Fruta . getFruitByTypeName ( "Plátano" ); Fruta . imprimirTiposActuales (); Fruta . getFruitByTypeName ( "Apple" ); Fruta . imprimirTiposActuales (); Fruta . getFruitByTypeName ( "Plátano" ); Fruta . imprimirTiposActuales (); } } }        

C

En C, la evaluación diferida normalmente se implementaría dentro de una única función, o un único archivo fuente, utilizando variables estáticas .

En una función:

#incluir <cadena.h> #incluir < stdlib.h > #incluir <stddef.h> #incluir <stdio.h>    estructura fruta { char * nombre ; estructura fruta * siguiente ; número entero ; /* Otros miembros */ };          struct fruit * get_fruit ( char * name ) { static struct fruit * fruit_list ; secuencia int estática ; estructura fruta * f ; for ( f = lista_frutas ; f ; f = f -> siguiente ) if ( 0 == strcmp ( nombre , f -> nombre )) return f ; if ( ! ( f = malloc ( sizeof ( struct fruit )))) devuelve NULL ; if ( ! ( f -> nombre = strdup ( nombre ))) { gratis ( f ); devolver NULO ; } f -> número = ++ secuencia ; f -> siguiente = lista_frutas ; lista_frutas = f ; devolver f ; }                                                        /* Código de ejemplo */int principal ( int argc , char * argv []) { int i ; estructura fruta * f ; if ( argc < 2 ) { fprintf ( stderr , "Uso: frutas nombre-fruta [...] \n " ); salir ( 1 ); } for ( i = 1 ; i < argc ; i ++ ) { if (( f = get_fruit ( argv [ i ]))) { printf ( "Fruta %s: número %d \n " , argv [ i ], f -> número ); } } devuelve 0 ; }                                        

En su lugar, el uso de un único archivo fuente permite que el estado se comparta entre varias funciones y, al mismo tiempo, se oculte de funciones no relacionadas.

fruta.h:

#ifndef _FRUTA_INCLUDIDA_ #define _FRUTA_INCLUDIDA_estructura fruta { char * nombre ; estructura fruta * siguiente ; número entero ; /* Otros miembros */ };          estructura fruta * get_fruta ( char * nombre ); void print_fruit_list ( ARCHIVO * archivo );     #endif /* _FRUTA_INCLUIDA_ */

fruta.c:

#incluir <cadena.h> #incluir <stdlib.h> #incluir < stddef.h> #incluir <stdio.h> #incluir "fruta.h"     estructura estática fruta * lista_frutas ; secuencia int estática ;     struct fruta * get_fruit ( char * nombre ) { struct fruta * f ; for ( f = lista_frutas ; f ; f = f -> siguiente ) if ( 0 == strcmp ( nombre , f -> nombre )) return f ; if ( ! ( f = malloc ( sizeof ( struct fruit )))) devuelve NULL ; if ( ! ( f -> nombre = strdup ( nombre ))) { gratis ( f ); devolver NULO ; } f -> número = ++ secuencia ; f -> siguiente = lista_frutas ; lista_frutas = f ; devolver f ; }                                                 void print_fruit_list ( ARCHIVO * archivo ) { estructura fruta * f ; for ( f = lista_frutas ; f ; f = f -> siguiente ) fprintf ( archivo , "%4d %s \n " , f -> número , f -> nombre ); }                  

C Principal:

#incluye <stdlib.h> #incluye <stdio.h> #incluye "fruta.h"   int principal ( int argc , char * argv []) { int i ; estructura fruta * f ; if ( argc < 2 ) { fprintf ( stderr , "Uso: frutas nombre-fruta [...] \n " ); salir ( 1 ); } for ( i = 1 ; i < argc ; i ++ ) { if (( f = get_fruit ( argv [ i ]))) { printf ( "Fruta %s: número %d \n " , argv [ i ], f -> número ); } } printf ( "Se han generado los siguientes frutos: \n " ); print_fruit_list ( salida estándar ); devolver 0 ; }                                          

C#

En .NET Framework 4.0, Microsoft ha incluido una Lazyclase que se puede utilizar para realizar carga diferida. A continuación se muestra un código ficticio que realiza una carga diferida de la clase.Fruit

var lazyFruit = new Lazy <Fruta> ( ) ; Fruta fruta = fruta perezosa . Valor ;       

A continuación se muestra un ejemplo ficticio en C# .

La Fruitclase en sí no hace nada aquí. La variable de clase _typesDictionary es un diccionario/mapa que se utiliza para almacenar Fruitinstancias typeName.

usando Sistema ; usando System.Collections ; usando System.Collections.Generic ;   fruta de clase pública { cadena privada _typeName ; IDictionario estático privado < cadena , Fruta > _typesDictionary = nuevo Diccionario < cadena , Fruta > ();               fruta privada ( nombre del tipo de cadena ) { this . _typeName = tipoName ; }        fruta estática pública GetFruitByTypeName ( tipo de cadena ) { fruta fruta ;        if ( ! _typesDictionary . TryGetValue ( tipo , fruta de salida )) { // Inicialización diferida fruta = nueva Fruta ( tipo );          _typesDiccionario . 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 < cadena , Fruta > kvp en _typesDictionary ) { Console . WriteLine ( kvp . Clave ); } Consola . Línea de escritura (); } } public Fruit () { // requerido para que el ejemplo se compile } }                               clase Programa { static void Principal ( cadena [] args ) { Fruta . GetFruitByTypeName ( "Plátano" ); Fruta . Mostrar todo ();         Fruta . GetFruitByTypeName ( "Apple" ); Fruta . Mostrar todo ();  // devuelve una instancia preexistente de la primera // vez que se creó Fruit con "Banana" Fruit . GetFruitByTypeName ( "Plátano" ); Fruta . Mostrar todo ();    Consola . Leerlínea (); } } 

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 // IDictionary 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 adelante public enum LazyObjectSize { Ninguno , Pequeño , Grande , Más grande , Enorme }            // tipo estándar de objeto que se construirá public struct LazyObject { public LazyObjectSize Size ; IList público <int> Resultado ;}            // toma el tamaño y crea una lista 'cara' privada IList < int > Resultado ( LazyObjectSize tamaño ) { IList < int > resultado = 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 ; }                                           resultado de retorno ; }   // no es un elemento costoso de crear, pero entiendes el punto // retrasa la creación de algún objeto costoso hasta que sea necesario private IList < int > CreateSomeExpensiveList ( int start , int end ) { IList < int > result = new List < int > ( );              for ( int contador = 0 ; contador < ( fin - inicio ); contador ++ ) { resultado . Agregar ( inicio + contador ); }                resultado de retorno ; }   public LazyFactoryObject () { // constructor vacío }     public LazyObject GetLazyFactoryObject ( tamaño LazyObjectSize ) { // sí, sé que es analfabeto e inexacto LazyObject noGoodSomeOne ;        // recupera LazyObjectSize de la lista mediante out, de lo contrario crea uno y lo agrega a la lista if ( ! _LazyObjectList . TryGetValue ( size , out noGoodSomeOne )) { noGoodSomeOne = new LazyObject (); no hay alguien bueno . Tamaño = tamaño ; no hay alguien bueno . Resultado = esto . Resultado ( tamaño );                _LazyObjectList . Agregar ( tamaño , noGoodSomeOne ); }   devolver noGoodSomeOne ; } }  

C++

Aquí hay un ejemplo en C++ .

#include <iostream> #include <mapa> #include <cadena>   clase Fruta { público : fruta estática * GetFruit ( const std :: cadena y tipo ); static void PrintCurrentTypes ();            privado : // Nota: el constructor privado obliga a usar estático |GetFruit|. Fruta ( const std :: cadena y tipo ) : tipo_ ( tipo ) {}        std estático :: mapa < std :: cadena , tipos de fruta *> ;    std :: tipo de cadena_ ; }; // estático std :: mapa < std :: cadena , Fruta *> Fruta :: tipos ;  // Método Lazy Factory, obtiene |Fruta| instancia asociada con un determinado // |tipo|. Crea nuevos según sea necesario. Fruta * Fruta::GetFruit ( const std :: cadena y tipo ) { auto [ it , insertado ] = tipos . emplazar ( tipo , nullptr ); if ( insertado ) { it -> segundo = nueva Fruta ( tipo ); } devolverlo - > segundo ; }                    // 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 & [ tipo , fruta ] : tipos ) { std :: cout << tipo << std :: endl ; } std :: cout << std :: endl ; }                          int main () { Fruta :: ObtenerFruta ( "Plátano" ); Fruta :: PrintCurrentTypes ();     Fruta :: ObtenerFruit ( "Manzana" ); Fruta :: PrintCurrentTypes ();  // Devuelve la instancia preexistente de la primera vez |Fruta| con "Banana" fue // creado. Fruta :: ObtenerFruta ( "Plátano" ); Fruta :: PrintCurrentTypes (); }   // SALIDA: // // Número de instancias realizadas = 1 // Plátano // // Número de instancias realizadas = 2 // Manzana // Plátano // // Número de instancias realizadas = 2 // Manzana // Plátano / /

Cristal

clase Fruta tipo de captador privado : Cadena @@ tipos = {} de Cadena => Fruta              def inicializar ( @tipo ) finalizar   definición de uno mismo . get_fruit_by_type ( tipo : Cadena ) @@types [ tipo ] ||= Fruta . nuevo ( tipo ) fin        definición de uno mismo . show_all pone "Número de instancias realizadas: #{ @@types . size } " @@types . cada uno hace | tipo de fruta | pone fin " #{ tipo } " pone fin             definición de uno mismo . tamaño @@tipos . tamaño final   Fruta . 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

Hax

Aquí hay un ejemplo en Haxe [1]

clase Fruta { privada estática var _instances = nuevo Mapa < Cadena , Fruta >();           nombre de var pública ( predeterminado , nulo ): Cadena ;    función pública nueva ( nombre : Cadena ) { this . nombre = nombre ; }        función estática pública getFruitByName ( nombre : Cadena ): Fruta { if ( ! _instancias . existe ( nombre )) { _instancias . set ( nombre , nueva fruta ( nombre )); } devolver _instancias . obtener ( nombre ); }               función estática pública printAllTypes () { trace ([ for ( clave en _instances . claves ()) clave ]); } }         

Uso

clase Prueba { función estática pública principal () { var banana = Fruta . getFruitByName ( "Plátano" ); var manzana = Fruta . getFruitByName ( "Manzana" ); var banana2 = Fruta . getFruitByName ( "Plátano" ); traza ( plátano == plátano2 ); // verdadero. mismo plátano Fruta . imprimirTodos los tipos (); // ["plátano","manzana"] } }                             

Java

Aquí hay un ejemplo en Java .

importar java.util.HashMap ; importar java.util.Map ; importar java.util.Map.Entry ;   Programa de clase pública {    /**  * @param args  */ public static void main ( String [] args ) { Fruta . getFruitByTypeName ( FruitType . plátano ); Fruta . mostrar todo (); Fruta . getFruitByTypeName ( FruitType . manzana ); Fruta . mostrar todo (); Fruta . getFruitByTypeName ( FruitType . plátano ); Fruta . mostrar todo (); } }             enum FruitType { ninguno , manzana , plátano , }     clase  fruta {  Mapa estático privado < FruitType , Fruit > tipos = nuevo HashMap <> (); /**  * Usar un constructor privado para forzar el uso del método de fábrica.  * @param type  */ private Fruit ( FruitType type ) { } /**  * Método Lazy Factory, obtiene la instancia de Fruit asociada con un determinado  * tipo. Crea instancias nuevas según sea necesario.  * @param type Cualquier tipo de fruta permitido, por ejemplo, APPLE  * @return La instancia de Fruit asociada con ese tipo.  */ fruta estática pública getFruitByTypeName ( tipo FruitType ) { Fruta fruta ; // Esto tiene problemas de concurrencia. Aquí la lectura de tipos no está sincronizada, // por lo que se pueden llamar a tipos.put y tipos.containsKey al mismo tiempo. // No se sorprenda si los datos están dañados. if ( ! tipos . contieneClave ( tipo )) { // Inicialización diferida fruta = nueva Fruta ( tipo ); tipos . poner ( tipo , fruta ); } else { // OK, actualmente está disponible fruta = tipos . obtener ( escribir ); } devolver fruta ; } /**  * Método Lazy Factory, obtiene la instancia de Fruit asociada con un determinado  * tipo. Crea instancias nuevas según sea necesario. Utiliza un patrón de bloqueo * verificado dos veces  para usar 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 tipo ) { if ( ! tipos . containsKey ( tipo )) { sincronizado ( tipos ) { // Verifique nuevamente, después de haber adquirido el bloqueo para asegurarse                                                                 // la instancia no fue creada mientras tanto por otro hilo if ( ! tipos . contieneClave ( tipo )) { // Tipos de inicialización diferida . put ( tipo , nueva fruta ( tipo )); } } } tipos de devolución . obtener ( escribir ); } /**  * Muestra todas las frutas ingresadas.  */ public static void showAll () { if ( tipos . tamaño () > 0 ) { System . afuera . println ( "Numero de instancias realizadas = " + tipos . tamano ()); for ( Entrada < TipoFruta , Fruta > entrada : tipos.entrySet ( ) ) { Cadena fruta = entrada . obtener la clave (). Encadenar (); fruta = Carácter . toUpperCase ( fruta . charAt ( 0 )) + fruta . subcadena ( 1 ); Sistema . afuera . println ( fruta ); } Sistema . afuera . imprimir (); } } }                                                     

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 = ( function () { var tipos = {}; function Fruta () {};            // contar propiedades propias en el objeto function count ( obj ) { return Objeto . claves ( obj ). longitud ; }       var _static = { getFruit : función ( tipo ) { if ( tipo de tipos [ tipo ] == 'indefinido' ) { tipos [ tipo ] = nueva fruta ; } tipos de retorno [ tipo ]; }, printCurrentTypes : función () { consola . log ( 'Número de instancias realizadas:' + recuento ( tipos )); for ( var tipo en tipos ) { consola . iniciar sesión ( tipo ); } } };                                      devolver _estático ; })();Fruta . obtenerFruta ( 'Manzana' ); Fruta . imprimirTiposActuales (); Fruta . obtenerFruta ( 'Plátano' ); Fruta . imprimirTiposActuales (); Fruta . obtenerFruta ( 'Manzana' ); Fruta . imprimirTiposActuales ();

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 encabezado ( 'Tipo de contenido: texto/plain; charset=utf-8' );clase  Fruta {  cadena privada  $tipo ; matriz estática privada $tipos = matriz ();         función  privada __construct ( cadena  $tipo )  {  $this -> tipo  =  $tipo ;  }  función estática  pública getFruit ( string $type ) { // La inicialización diferida tiene lugar aquí if ( ! isset ( self :: $types [ $type ])) { self :: $types [ $type ] = new Fruit ( $type ) ; }             return  self :: $tipos [ $tipo ];  }  función estática  pública printCurrentTypes () : void { echo 'Número de instancias realizadas: ' . contar ( self :: $tipos ) . " \n " ; foreach ( array_keys ( self :: $tipos ) as $clave ) { echo " $clave \n " ; } eco " \n " ; } }                    Fruta :: obtenerFruta ( 'Manzana' ); Fruta :: printCurrentTypes ();Fruta :: obtenerFruta ( 'Plátano' ); Fruta :: printCurrentTypes ();Fruta :: obtenerFruta ( 'Manzana' ); Fruta :: printCurrentTypes ();/* PRODUCCIÓN:Número de instancias realizadas: 1 AppleNúmero de instancias realizadas: 2 Manzana PlátanoNúmero de instancias realizadas: 2 Apple Banana */

Pitón

Aquí hay un ejemplo en Python .

clase  Fruta :  def  __init__ ( self ,  item :  str ):  self . artículo  =  artículo clase  FruitCollection :  def  __init__ ( self ):  self . elementos  =  {}  def  get_fruit ( self ,  item :  str )  ->  Fruit :  si  el elemento  no está  en  self . artículos :  uno mismo . artículos [ artículo ]  =  Fruta ( artículo )  regresar a  uno mismo . artículos [ artículo ]if  __name__  ==  "__main__" :  frutas  =  FruitCollection ()  print ( frutas . get_fruit ( " Apple " ))  print ( frutas . get_fruit ( " Lima " ))

Rubí

A continuación se muestra un ejemplo en Ruby de inicialización diferida de 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 . instancia_variable_get ( :@auth_token ) # devuelve nil b . auth_token # devuelve el token b . instancia_variable_get ( :@auth_token ) # devuelve el token     

escala

Scala tiene soporte incorporado para el inicio de variables diferidas. [2]

 escala > val x = { println ( "Hola" ); 99 }        Hola x : Int = 99 scala > lazy val y = { println ( "¡Hola!" ); 31 } y : Int = < lazy > scala > y ¡¡Hola !! res2 : Int = 31 escala > y res3 : Int = 31                               

Charla

A continuación se muestra un ejemplo en Smalltalk de un método de acceso típico para devolver el valor de una variable mediante inicialización diferida.

 altura  ^ altura  siNil: [ altura  : =  2.0 ] .

La alternativa 'no diferida' 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 recuperar 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 .

informática teórica

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 inicialización. Específicamente, supongamos que tenemos acceso a una tabla T de n celdas de memoria no inicializadas (numeradas del 1 al n ) y queremos asignar m celdas 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 solo con 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 ( ki , vi ) en algún orden arbitrario, y escribir para cada i en la celda T [ ki ] 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 la celda T [ k ] para algún k , podemos verificar si k está en el rango {1, ..., m }: si no es así, 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 casualidad 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

  1. ^ "Inicialización diferida - Patrones de diseño - Libro de recetas del lenguaje de programación Haxe". 2018-01-11 . Consultado el 9 de noviembre de 2018 .
  2. ^ Pollak, David (25 de mayo de 2009). Principio de Scala. ISBN 9781430219897.
  3. ^ Moret, BME; Shapiro, HD (1991). Algoritmos de P a NP, Volumen 1: Diseño y eficiencia . Compañía editorial Benjamín/Cummings. págs. 191-192. ISBN 0-8053-8008-6.

enlaces externos