惰性初始化

在程式设计中, 惰性初始化（）是一种拖延战术。在第一次需求出现以前，先延迟创建物件、计算值或其它昂贵程序。这通常是以一个旗号来实现，用旗号来标示是否完成其程式。每次请求对象时，会先测试此旗号。如果已完成，直接传回，否则当场执行。
对于此想法更一般的论述，可见惰性求值。对指令式语言，这个模式可能潜藏著危险，尤其是使用共享状态的程式习惯。
惰性工厂.
《设计模式》书中的工厂方法模式的早于C++98的实现，由于这里的工厂方法codice_1总是虚函数并且经常是纯虚函数，codice_2在其构造子中只将具体产品初始化是为codice_3，转而通过其访问子来返回这个产品；访问子codice_4在需要的时候创建这个产品，《设计模式》书中将这种技术称为“惰性初始化”：
class Creator {
public:
 Creator();
 Product* GetProduct();
protected:
 virtual Product* CreateProduct();
private:
 Product* _product;
Creator::Creator() {
 _product = 0;
Product* Creator::GetProduct() {
 if (_product == 0) {
 _product = CreateProduct();
 return _product;
这种“惰性工厂”结合了二个构想：
在软件设计实践中经常还会结合上第三个构想：
理论计算机科学.
在理论计算机科学领域中，惰性初始化（也叫做惰性数组），是设计数据结构的技术，使其可以工作于不需要被初始化的内存。尤其是假定了要访问codice_5个（编号从 codice_6到codice_5）未初始化内存单元的一个表格codice_8，并希望赋值这个数组的codice_9个单元，比如赋值codice_10，它针对键值对codice_11，并具有所有的codice_12都是不同的。惰性初始化技术允许只用codice_13次运算来完成它，而非耗费codice_14次运算来首先初始化所有数组单元。这项技术简单的分配一个表格codice_15以任意次序存储键值对codice_16，并为每个codice_17在单元codice_18中写入键codice_12存储在codice_15中的位置，保留codice_8的其他单元不初始化。这可以用来处理下列方式的查询：在针对某个codice_22查看单元codice_23的时候，可以在codice_24范围内检查codice_23；如果不在其中，则codice_23是未初始化的。否则检查codice_27，并验证这个键值对的第一个组件等于codice_22；如果不等于，则codice_23是未初始化的（它只是意外落入范围codice_24中）；否则确知了codice_23是初始化的单元之一，而对应的值是这个键值对的第二个组件。
示例.
C++.
C++的例子：
import std;
class Fruit {
private:
 static std::map&lt;std::string, Fruit*&gt; types;
 std::string type_;
 // Note: constructor private forcing one to use static |GetFruit|.
 Fruit(const std::string&amp; type): 
public:
 // Lazy Factory method, gets the |Fruit| instance associated with a certain
 // |type|. Creates new ones as needed.
 static Fruit* getFruit(const std::string&amp; type) {
 auto [it, inserted] = types.emplace(type, nullptr);
 if (inserted) {
 it-&gt;second = new Fruit(type);
 return it-&gt;second;
 // For example purposes to see pattern in action.
 static void printCurrentTypes() {
 std::println("Number of instances made = {}", types.size());
 for (const auto&amp; [type, fruit] : types) {
 std::println({}, type);
 std::println();
};
// static
std::map&lt;std::string, Fruit*&gt; Fruit::types;
int main(int argc, char* argv[]) {
 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
Java.
Java例子：
import java.util.*;
public class Fruit {
 private static final Map&lt;String,Fruit&gt; types = new HashMap&lt;String,Fruit&gt;();
 private final String type;
 // using a private constructor to force use of the factory method.
 private Fruit(String type) {
 this.type = type;
 
 * Lazy Factory method, gets the Fruit instance associated with a
 * certain type. Instantiates new ones as needed.
 * @param type Any string that describes a fruit type, e.g. "apple"
 * @return The Fruit instance associated with that type.
 public static synchronized Fruit getFruit(String type) {
 if(!types.containsKey(type))
 types.put(type, new Fruit(type)); // Lazy initialization
 return types.get(type);
C#.
在下面的C#例子中，codice_32 类别本身在这里不做任合事。codice_33 变数则是一个存 codice_32 实例的 Dictionary 或 Map ，其透过codice_35来存取。
using System;
using System.Collections;
using System.Collections.Generic;
public class Fruit {
 private string _typeName;
 private static Dictionary&lt;string, Fruit&gt; _typesDictionary = new Dictionary&lt;string, Fruit&gt;();
 private Fruit(String typeName) {
 this._typeName = typeName;
 public static Fruit GetFruitByTypeName(string type) {
 Fruit fruit;
 if (!_typesDictionary.ContainsKey(type)) {
 // 惰性初始
 fruit = new Fruit(type);
 _typesDictionary.Add(type, fruit);
 else
 fruit = _typesDictionary[type];
 return fruit;
 public static void ShowAll() {
 if (_typesDictionary.Count &gt; 0) {
 Console.WriteLine("Number of instances made = {0}", _typesDictionary.Count);
 foreach (KeyValuePair&lt;string, Fruit&gt; kvp in _typesDictionary) {
 Console.WriteLine(kvp.Key);
 Console.WriteLine();
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();
Python.
Python的对象惰性实例化例子：
from abc import ABCMeta
from weakref import WeakValueDictionary
class Fruit(metaclass=ABCMeta):
 def __init__(self, name, **kwargs):
 self.name = name
 for key, value in kwargs.items():
 self.__dict__[key] = value
class FruitFuture():
 weakref_dict = WeakValueDictionary()
 count = 0
 def __init__(self, name, **kwargs):
 self.name = name
 self.kwargs = kwargs
 type(self).count += 1
 def result(self):
 if id(self) in type(self).weakref_dict:
 fruit = type(self).weakref_dict[id(self)]
 else:
 fruit = Fruit(self.name, **self.kwargs)
 type(self).weakref_dict[id(self)] = fruit
 return fruit
 @classmethod
 def info(cls):
 print(f'Instances created: {cls.count}')
 print(f'Instances exist now: {len(cls.weakref_dict)}')
Fruit.register(FruitFuture)
这里实现了创建产品的Future，略过了可附加的，它与享元模式中用Python写的类似例子的区别在于，它的目标是推延而非共享，下面是其执行：
»&gt; fruit1 = FruitFuture('Banana')
»&gt; assert isinstance(fruit1, Fruit)
»&gt; fruit2 = FruitFuture('Apple')
»&gt; FruitFuture.info()
Instances created: 2
Instances exist now: 0
»&gt; fruit1 = fruit1.result()
»&gt; fruit2 = fruit2.result()
»&gt; FruitFuture.info()
Instances created: 2
Instances exist now: 2
»&gt; fruit2 = FruitFuture('Banana').result()
»&gt; FruitFuture.info()
Instances created: 3
Instances exist now: 2
Python的对象惰性初始化例子：
import functools
class lazyinitialize():
 def __init__(self, f):
 self.f = f
 functools.update_wrapper(self, f)
 def __get__(self, obj, objtype=None):
 if obj is None: 
 #Invocation from a class
 return self
 val = self.f(obj)
 obj.__dict__[self.f.__name__] = val
 return val
class Employee():
 def __init__(self, name, employer):
 self.name = name
 self.employer = employer
 @lazyinitialize
 def report(self):
 #Spending a lot of time and effort to get a result
 result = "Voluminous document: 100,000 words omitted here."
 return result
其执行：
»&gt; employee = Employee("Taciturn Man", "Large Institution")
»&gt; employee.report
'Voluminous document: 100,000 words omitted here.'
»&gt; employee.report = "Concise documentation: small is beautiful."
»&gt; employee.report
'Concise documentation: small is beautiful.'
Smalltalk.
下面的Smalltalk例子，具有典型的访问子方法，它返回使用惰性初始化的一个变量的值。
height
 ^height ifNil: [height := 2.0].
非惰性的替代者，使用的初始化方法是在对象被创建时运行的，并且使用简单的访问子方法来取回这个值。
initialize
 height := 2.0
height
 ^height
注意惰性初始化也可以用在非面向对象编程语言中。
Ruby.
下面的Ruby例子，具有惰性初始化的来自远程服务的身份验证令牌。 codice_36被缓存的方式也是记忆化的示例。
require 'net/http'
class Blogger
 def auth_token
 @auth_token ||=
 (res = Net::HTTP.post_form(uri, params)) &amp;&amp;
 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
PHP.
PHP 7.4的惰性初始化的例子：
&lt;?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-&gt;type = $type;
 public static function getFruit(string $type): Fruit {
 // 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