Jump to content

Ленивая инициализация

В компьютерном программировании ленивая инициализация — это тактика отсрочки создания объекта , вычисления значения или какого-либо другого дорогостоящего процесса до тех пор, пока он не понадобится в первый раз. Это своего рода ленивая оценка , которая относится конкретно к созданию объектов или других ресурсов.

Обычно это достигается путем дополнения метода доступа (или метода получения свойства) для проверки того, был ли уже инициализирован закрытый член, действующий как кэш. Если да, то он немедленно возвращается. В противном случае создается новый экземпляр, помещается в переменную-член и возвращается вызывающему объекту как раз вовремя для его первого использования.

Если объекты имеют свойства, которые редко используются, это может повысить скорость запуска. Средняя производительность программы может быть немного хуже с точки зрения памяти (для условных переменных) и циклов выполнения (для их проверки), но влияние создания экземпляра объекта распределяется во времени («амортизируется»), а не концентрируется на этапе запуска системы, и, таким образом, среднее время ответа может быть значительно улучшено.

В многопоточном коде доступ к лениво инициализированным объектам/состоянию должен быть синхронизирован для защиты от условий гонки .

«ленивая фабрика»

[ редактировать ]

В представлении шаблона проектирования программного обеспечения ленивая инициализация часто используется вместе с шаблоном фабричного метода . Это объединяет три идеи:

  • Использование фабричного метода для создания экземпляров класса ( шаблон фабричного метода )
  • Сохранение экземпляров на карте и возврат одного и того же экземпляра при каждом запросе экземпляра с одинаковыми параметрами ( многотонный шаблон ).
  • Использование ленивой инициализации для создания экземпляра объекта при первом запросе (шаблон ленивой инициализации).

Экшнскрипт 3

[ редактировать ]

Ниже приведен пример класса с отложенной инициализацией, реализованной в ActionScript :

package examples.lazyinstantiation
{
	public class Fruit 
	{
		private var _typeName:String;
		private static var instancesByTypeName:Dictionary = new Dictionary();
	 
		public function Fruit(typeName:String):void
		{
			this._typeName = typeName;
		}
		
		public function get typeName():String 
		{
			return _typeName;
		}
	 
		public static function getFruitByTypeName(typeName:String):Fruit
		{
			return instancesByTypeName[typeName] ||= new Fruit(typeName);
		}
	 
		public static function printCurrentTypes():void
		{
			for each (var fruit:Fruit in instancesByTypeName) 
			{
				// iterates through each value
				trace(fruit.typeName);
			}
		}
	}
}

Основное использование:

package
{
	import examples.lazyinstantiation;
	
	public class Main 
	{
		public function Main():void
		{
			Fruit.getFruitByTypeName("Banana");
			Fruit.printCurrentTypes();
	 
			Fruit.getFruitByTypeName("Apple");
			Fruit.printCurrentTypes();
	 
			Fruit.getFruitByTypeName("Banana");
			Fruit.printCurrentTypes();
		}
	}
}

В C ленивые вычисления обычно реализуются внутри одной функции или одного исходного файла с использованием статических переменных .

В функции:

#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>

struct fruit {
    char *name;
    struct fruit *next;
    int number;
    /* Other members */
};

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;
}

/* Example code */

int main(int argc, char *argv[]) {
    int i;
    struct fruit *f;
    if (argc < 2) {
        fprintf(stderr, "Usage: fruits fruit-name [...]\n");
        exit(1);
    }
    for (i = 1; i < argc; i++) {
        if ((f = get_fruit(argv[i]))) {
            printf("Fruit %s: number %d\n", argv[i], f->number);
        }
    }
    return 0;
}

Вместо этого использование одного исходного файла позволяет разделить состояние между несколькими функциями, при этом скрывая его от несвязанных функций.

фрукты.ч:

#ifndef _FRUIT_INCLUDED_
#define _FRUIT_INCLUDED_

struct fruit {
    char *name;
    struct fruit *next;
    int number;
    /* Other members */
};

struct fruit *get_fruit(char *name);
void print_fruit_list(FILE *file);

#endif /* _FRUIT_INCLUDED_ */

фрукты.с:

#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);
}

основной.с:

#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, "Usage: fruits fruit-name [...]\n");
        exit(1);
    }
    for (i = 1; i < argc; i++) {
        if ((f = get_fruit(argv[i]))) {
            printf("Fruit %s: number %d\n", argv[i], f->number);
        }
    }
    printf("The following fruits have been generated:\n");
    print_fruit_list(stdout);
    return 0;
}

В .NET Framework 4.0 Microsoft включила Lazy класс, который можно использовать для отложенной загрузки. Ниже приведен фиктивный код, который выполняет ленивую загрузку класса. Fruit

var lazyFruit = new Lazy<Fruit>();
Fruit fruit = lazyFruit.Value;

Вот фиктивный пример на C# .

The Fruit сам класс здесь ничего не делает. Переменная класса _typesDictionary это словарь/карта, используемая для хранения Fruit экземпляры по typeName.

using System;
using System.Collections;
using System.Collections.Generic;

public class Fruit
{
    private string _typeName;
    private static IDictionary<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))
        {
            // Lazy initialization
            fruit = new Fruit(type);

            _typesDictionary.Add(type, fruit);
        }

        return fruit;
    }

    public static void ShowAll()
    {
        if (_typesDictionary.Count > 0)
        {
            Console.WriteLine("Number of instances made = {0}", _typesDictionary.Count);
            
            foreach (KeyValuePair<string, Fruit> kvp in _typesDictionary)
            {
                Console.WriteLine(kvp.Key);
            }
            
            Console.WriteLine();
        }
    }
    
    public Fruit()
    {
        // required so the sample compiles
    }
}

class Program
{
    static void Main(string[] args)
    {
        Fruit.GetFruitByTypeName("Banana");
        Fruit.ShowAll();

        Fruit.GetFruitByTypeName("Apple");
        Fruit.ShowAll();

        // returns pre-existing instance from first 
        // time Fruit with "Banana" was created
        Fruit.GetFruitByTypeName("Banana");
        Fruit.ShowAll();

        Console.ReadLine();
    }
}

Достаточно простой пример шаблона проектирования «Отложенная инициализация» с «заполнением пробелов», за исключением того, что здесь используется перечисление для типа

namespace DesignPatterns.LazyInitialization;

public class LazyFactoryObject
{
    // internal collection of items
    // IDictionary makes sure they are unique
    private IDictionary<LazyObjectSize, LazyObject> _LazyObjectList =
        new Dictionary<LazyObjectSize, LazyObject>();

    // enum for passing name of size required
    // avoids passing strings and is part of LazyObject ahead
    public enum LazyObjectSize
    {
        None,
        Small,
        Big,
        Bigger,
        Huge
    }

    // standard type of object that will be constructed
    public struct LazyObject
    {
        public LazyObjectSize Size;
        public IList<int> Result;
    }

    // takes size and create 'expensive' list
    private IList<int> Result(LazyObjectSize size)
    {
        IList<int> result = null;

        switch (size)
        {
            case LazyObjectSize.Small:
                result = CreateSomeExpensiveList(1, 100);
                break;
            case LazyObjectSize.Big:
                result = CreateSomeExpensiveList(1, 1000);
                break;
            case LazyObjectSize.Bigger:
                result = CreateSomeExpensiveList(1, 10000);
                break;
            case LazyObjectSize.Huge:
                result = CreateSomeExpensiveList(1, 100000);
                break;
            case LazyObjectSize.None:
                result = null;
                break;
            default:
                result = null;
                break;
        }

        return result;
    }

    // not an expensive item to create, but you get the point
    // delays creation of some expensive object until needed
    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()
    {
        // empty constructor
    }

    public LazyObject GetLazyFactoryObject(LazyObjectSize size)
    {
        // yes, i know it is illiterate and inaccurate
        LazyObject noGoodSomeOne;

        // retrieves LazyObjectSize from list via out, else creates one and adds it to list
        if (!_LazyObjectList.TryGetValue(size, out noGoodSomeOne))
        {
            noGoodSomeOne = new LazyObject();
            noGoodSomeOne.Size = size;
            noGoodSomeOne.Result = this.Result(size);

            _LazyObjectList.Add(size, noGoodSomeOne);
        }

        return noGoodSomeOne;
    }
}

Вот пример на C++ .

#include <iostream>
#include <map>
#include <string>

class Fruit {
 public:
  static Fruit* GetFruit(const std::string& type);
  static void PrintCurrentTypes();

 private:
  // Note: constructor private forcing one to use static |GetFruit|.
  Fruit(const std::string& type) : type_(type) {}

  static std::map<std::string, Fruit*> types;

  std::string type_;
};

// static
std::map<std::string, Fruit*> Fruit::types;

// Lazy Factory method, gets the |Fruit| instance associated with a certain
// |type|.  Creates new ones as needed.
Fruit* Fruit::GetFruit(const std::string& type) {
  auto [it, inserted] = types.emplace(type, nullptr);
  if (inserted) {
    it->second = new Fruit(type);
  }
  return it->second;
}

// For example purposes to see pattern in action.
void Fruit::PrintCurrentTypes() {
  std::cout << "Number of instances made = " << types.size() << std::endl;
  for (const auto& [type, fruit] : types) {
    std::cout << type << std::endl;
  }
  std::cout << std::endl;
}

int main() {
  Fruit::GetFruit("Banana");
  Fruit::PrintCurrentTypes();

  Fruit::GetFruit("Apple");
  Fruit::PrintCurrentTypes();

  // Returns pre-existing instance from first time |Fruit| with "Banana" was
  // created.
  Fruit::GetFruit("Banana");
  Fruit::PrintCurrentTypes();
}

// OUTPUT:
//
// Number of instances made = 1
// Banana
//
// Number of instances made = 2
// Apple
// Banana
//
// Number of instances made = 2
// Apple
// Banana
//

Кристалл

[ редактировать ]
class Fruit
  private getter type : String
  @@types = {} of String => Fruit

  def initialize(@type)
  end

  def self.get_fruit_by_type(type : String)
    @@types[type] ||= Fruit.new(type)
  end

  def self.show_all
    puts "Number of instances made: #{@@types.size}"
    @@types.each do |type, fruit|
      puts "#{type}"
    end
    puts
  end

  def self.size
    @@types.size
  end
end

Fruit.get_fruit_by_type("Banana")
Fruit.show_all

Fruit.get_fruit_by_type("Apple")
Fruit.show_all

Fruit.get_fruit_by_type("Banana")
Fruit.show_all

Выход:

Number of instances made: 1
Banana

Number of instances made: 2
Banana
Apple

Number of instances made: 2
Banana
Apple

Смешанный

[ редактировать ]

Вот пример в Haxe [1]

class Fruit {
  private static var _instances = new Map<String, Fruit>();

  public var name(default, null):String;

  public function new(name:String) {
    this.name = name;
  }

  public static function getFruitByName(name:String):Fruit {
    if (!_instances.exists(name)) {
      _instances.set(name, new Fruit(name));
    }
    return _instances.get(name);
  }

  public static function printAllTypes() {
    trace([for(key in _instances.keys()) key]);
  }
}

Использование

class Test {
  public static function main () {
    var banana = Fruit.getFruitByName("Banana");
    var apple = Fruit.getFruitByName("Apple");
    var banana2 = Fruit.getFruitByName("Banana");
    
    trace(banana == banana2); // true. same banana
    
    Fruit.printAllTypes(); // ["Banana","Apple"]
  }
}

Вот пример на Java .

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<>();
    
    /**
     * Using a private constructor to force the use of the factory method.
     * @param type
     */
    private Fruit(FruitType type) {
    }
    
    /**
     * Lazy Factory method, gets the Fruit instance associated with a certain
     * type. Instantiates new ones as needed.
     * @param type Any allowed fruit type, e.g. APPLE
     * @return The Fruit instance associated with that type.
     */
    public static Fruit getFruitByTypeName(FruitType type) {
        Fruit fruit;
                // This has concurrency issues.  Here the read to types is not synchronized, 
                // so types.put and types.containsKey might be called at the same time.
                // Don't be surprised if the data is corrupted.
        if (!types.containsKey(type)) {
            // Lazy initialisation
            fruit = new Fruit(type);
            types.put(type, fruit);
        } else {
            // OK, it's available currently
            fruit = types.get(type);
        }
        
        return fruit;
    }
    
    /**
     * Lazy Factory method, gets the Fruit instance associated with a certain
     * type. Instantiates new ones as needed. Uses double-checked locking 
     * pattern for using in highly concurrent environments.
     * @param type Any allowed fruit type, e.g. APPLE
     * @return The Fruit instance associated with that type.
     */
    public static Fruit getFruitByTypeNameHighConcurrentVersion(FruitType type) {
        if (!types.containsKey(type)) {
            synchronized (types) {
                // Check again, after having acquired the lock to make sure
                // the instance was not created meanwhile by another thread
                if (!types.containsKey(type)) {
                    // Lazy initialisation
                    types.put(type, new Fruit(type));
                }
            }
        }
        
        return types.get(type);
    }
    
    /**
     * Displays all entered fruits.
     */
    public static void showAll() {
        if (types.size() > 0) {
 
           System.out.println("Number of instances made = " + types.size());
            
            for (Entry<FruitType, Fruit> entry : types.entrySet()) {
                String fruit = entry.getKey().toString();
                fruit = Character.toUpperCase(fruit.charAt(0)) + fruit.substring(1);
                System.out.println(fruit);
            }
            
            System.out.println();
        }
    }
}

Выход

Number of instances made = 1
Banana

Number of instances made = 2
Banana
Apple

Number of instances made = 2
Banana
Apple

Вот пример на JavaScript .

var Fruit = (function() {
  var types = {};
  function Fruit() {};

  // count own properties in object
  function count(obj) {
    return Object.keys(obj).length;
  }

  var _static = {
    getFruit: function(type) {
      if (typeof types[type] == 'undefined') {
        types[type] = new Fruit;
      }
      return types[type];
    },
    printCurrentTypes: function () {
      console.log('Number of instances made: ' + count(types));
      for (var type in types) {
        console.log(type);
      }
    }
  };

  return _static;

})();

Fruit.getFruit('Apple');
Fruit.printCurrentTypes();
Fruit.getFruit('Banana');
Fruit.printCurrentTypes();
Fruit.getFruit('Apple');
Fruit.printCurrentTypes();

Выход

Number of instances made: 1
Apple

Number of instances made: 2
Apple
Banana

Number of instances made: 2
Apple
Banana

Вот пример ленивой инициализации в PHP 7.4:

<?php
header('Content-Type: text/plain; charset=utf-8');

class Fruit
{
    private string $type;
    private static array $types = array();

    private function __construct(string $type)
    {
        $this->type = $type;
    }

    public static function getFruit(string $type)
    {
        // Lazy initialization takes place here
        if (!isset(self::$types[$type])) {
            self::$types[$type] = new Fruit($type);
        }

        return self::$types[$type];
    }

    public static function printCurrentTypes(): void
    {
        echo 'Number of instances made: ' . count(self::$types) . "\n";
        foreach (array_keys(self::$types) as $key) {
            echo "$key\n";
        }
        echo "\n";
    }
}

Fruit::getFruit('Apple');
Fruit::printCurrentTypes();

Fruit::getFruit('Banana');
Fruit::printCurrentTypes();

Fruit::getFruit('Apple');
Fruit::printCurrentTypes();

/*
OUTPUT:

Number of instances made: 1
Apple

Number of instances made: 2
Apple
Banana

Number of instances made: 2
Apple
Banana
*/

Вот пример на Python .

class Fruit:
    def __init__(self, item: str):
        self.item = item
    
class FruitCollection:
    def __init__(self):
        self.items = {}
    
    def get_fruit(self, item: str) -> Fruit:
        if item not in self.items:
            self.items[item] = Fruit(item)
        
        return self.items[item]

if __name__ == "__main__":
    fruits = FruitCollection()
    print(fruits.get_fruit("Apple"))
    print(fruits.get_fruit("Lime"))

Вот пример на Ruby ленивой инициализации токена аутентификации из удаленной службы, такой как Google. Способ кэширования @auth_token также является примером мемоизации .

require 'net/http'
class 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 and params are defined later in the class
end

b = Blogger.new
b.instance_variable_get(:@auth_token) # returns nil
b.auth_token # returns token
b.instance_variable_get(:@auth_token) # returns token

Scala имеет встроенную поддержку ленивой инициализации переменных. [2]

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

Вот пример на Smalltalk типичного метода доступа для возврата значения переменной с использованием ленивой инициализации.

    height
        ^height ifNil: [height := 2.0].

«Неленивая» альтернатива — использовать метод инициализации, который запускается при создании объекта, а затем использовать более простой метод доступа для получения значения.

    initialize
        height := 2.0

    height
        ^height

Обратите внимание, что ленивая инициализация также может использоваться в необъектно -ориентированных языках .

Теоретическая информатика

[ редактировать ]

В области информатики теоретической ленивая инициализация [3] (также называемый ленивым массивом ) — это метод проектирования структур данных , которые могут работать с памятью, не требующей инициализации. В частности, предположим, что у нас есть доступ к таблице T из n неинициализированных ячеек памяти (с номерами от до n ) и мы хотим назначить m ячеек этого массива, например, мы хотим назначить T [ k i ] := vi 1 для пары ( k 1 , v 1 ), ..., ( km ) , , v m где все k i различны. Техника ленивой инициализации позволяет нам сделать это всего за O( m ) операций, вместо того, чтобы тратить O( m + n ) операций на первую инициализацию всех ячеек массива. Техника состоит в том, чтобы просто выделить таблицу V, пары ( ki , хранящую vi ) в некотором произвольном порядке, и записать для каждого i в ячейке T [ ki ] позицию в V ключ k i , где хранится , оставив остальные ячейки T неинициализируются. Это можно использовать для обработки запросов следующим образом: когда мы ищем в ячейке T [ k ] некоторый k , мы можем проверить, находится ли k в диапазоне {1, ..., m }: если это не так, то T [ k ] не инициализирован. В противном случае мы проверяем V [ T [ k ]] и проверяем, что первый компонент этой пары равен к . Если это не так, то T [ k ] не инициализирован (и просто случайно попал в диапазон {1,..., m }). В противном случае мы знаем, что T [ k ] действительно является одной из инициализированных ячеек, а соответствующее значение — вторым компонентом пары.

См. также

[ редактировать ]
  1. ^ «Ленивая инициализация — Шаблоны проектирования — Рецепты языка программирования Haxe» . 11 января 2018 г. Проверено 9 ноября 2018 г.
  2. ^ Поллак, Дэвид (25 мая 2009 г.). Начало Скалы . ISBN  9781430219897 .
  3. ^ Море, BME; Шапиро, HD (1991). Алгоритмы от P до NP, Том 1: Проектирование и эффективность . Издательская компания Бенджамина/Каммингса. стр. 191–192. ISBN  0-8053-8008-6 .
[ редактировать ]
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: 89ebe203c798f45ab01748aac08dd0b6__1711008060
URL1:https://arc.ask3.ru/arc/aa/89/b6/89ebe203c798f45ab01748aac08dd0b6.html
Заголовок, (Title) документа по адресу, URL1:
Lazy initialization - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)