惰性初始化

在程式設計中，惰性初始化（），是一種拖延戰術。在第一次需求出現以前，先延遲創建物件、計算值或其它昂貴程序。這通常是以一個旗號來實現，用旗號來標示是否完成其程式。每次請求對象時，會先測試此旗號。如果已完成，直接傳回，否則當場執行。

對於此想法更一般的論述，可見惰性求值。對指令式語言，這個模式可能潛藏著危險，尤其是使用共享狀態的程式習慣。

 惰性工廠 
《設計模式》書中的工厂方法模式的早于C++98的实现，由于这里的工厂方法CreateProduct()总是虚函数并且经常是纯虚函数，Creator在其构造子中只将具体产品初始化是为0，转而通过其访问子来返回这个产品；访问子GetProduct()在需要的时候创建这个产品，《设计模式》书中将这种技术称为“惰性初始化”：
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;
}
這種“惰性工廠”結合了二個構想：
 工廠方法：使用工廠方法，来獲得類的實例。
 惰性初始化技術：使用惰性初始化，實例化物件於其第一次被要求之時。
在軟件設計實踐中經常還會結合上第三個構想：
 ：將實例存儲在一個映射中，在以“相同”的參數要求一個實例之時，返回“同一個”單例，有人將這種參數化或為單例註冊類型名字的“惰性工廠”稱為惰性初始化模式。

理论计算机科学
在理论计算机科学领域中，惰性初始化（也叫做惰性数组），是设计数据结构的技术，使其可以工作于不需要被初始化的内存。尤其是假定了要访问n个（编号从 1到n）未初始化内存单元的一个表格T，并希望赋值这个数组的m个单元，比如赋值T[ki] := vi，它针对键值对(k1, v1), ..., (km, vm)，并具有所有的ki都是不同的。惰性初始化技术允许只用O(m)次运算来完成它，而非耗费O(m+n)次运算来首先初始化所有数组单元。这项技术简单的分配一个表格V以任意次序存储键值对(ki, vi)，并为每个i在单元T[ki]中写入键ki存储在V中的位置，保留T的其他单元不初始化。这可以用来处理下列方式的查询：在针对某个k查看单元T[k]的时候，可以在{1, ..., m}范围内检查T[k]；如果不在其中，则T[k]是未初始化的。否则检查V[T[k]]，并验证这个键值对的第一个组件等于k；如果不等于，则T[k]是未初始化的（它只是意外落入范围{1, ..., m}中）；否则确知了T[k]是初始化的单元之一，而对应的值是这个键值对的第二个组件。

 示例 
 C++ 
C++的例子：
import std;

template <typename K, typename V>
using HashMap = std::unordered_map<K, V>;
template <typename T>
using SharedPtr = std::shared_ptr<T>;
using String = std::string;

class Fruit {
private:
    static HashMap<String, SharedPtr<Fruit>> types = {};
    String type;

    // Note: constructor private forcing one to use static getFruit.
    explicit Fruit(const String& type): 
        type{type} {}
public:
    // Lazy Factory method, gets the Fruit instance associated with a certain type.
    // Creates new ones as needed.
    static SharedPtr<Fruit> getFruit(const String& type) {
        auto [it, inserted] = types.emplace(type, nullptr);
        if (inserted) {
            it->second = std::make_shared<Fruit>(type);
        }
        return it->second;
    }

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

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

程序输出为：

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<String,Fruit> types = new HashMap<String,Fruit>();
    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#例子中，Fruit 類別本身在這裡不做任何事。_typesDictionary 變數則是一個存 Fruit 實例的 Dictionary 或 Map ，其透過typeName來存取。

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

public class Fruit {
    private string _typeName;
    private static Dictionary<string, Fruit> _typesDictionary = new Dictionary<string, Fruit>();

    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 > 0) {
            Console.WriteLine("Number of instances made = {0}", _typesDictionary.Count);
            foreach (KeyValuePair<string, Fruit> 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 weakref import WeakSet
import functools

class invoke_from_class():
    def __init__(self, fn):
        self.fn = fn
        functools.update_wrapper(self, fn)
    def __get__(self, obj, objtype=None):
        self.from_class = True if obj is None else False
        return self
    def __call__(self, *args, **kwargs):
        if self.from_class is True:
            return self.fn(*args, **kwargs)

class Fruit():
    delay_set = WeakSet()
    def __new__(cls, name, **kwargs):
        obj = super().__new__(cls)
        obj.name = name
        obj.kwargs = kwargs
        cls.delay_set.add(obj)
        return obj
    @invoke_from_class
    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            self.__dict__[key] = value
    def __call__(self):
        if self in type(self).delay_set:
            type(self).delay_set.remove(self)
            kwargs = self.kwargs
            del self.kwargs
            type(self).__init__(self, **kwargs)
    @classmethod
    def info(cls):
        print(f'Instances deferred: {len(cls.delay_set)}')
若要防止非预期的访问到未初始化实例特性，可以在定义类中设置值为None的同名类特性，这里的实现略过了可附加的。下面是其执行：
>>> fruit1 = Fruit('Banana')
>>> fruit2 = Fruit('Apple')
>>> Fruit.info()
Instances deferred: 2
>>> fruit1()
>>> fruit2()
>>> Fruit.info()
Instances deferred: 0
Python的对象特性惰性初始化例子：
import functools

class Employee():
    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            self.__dict__[key] = value
    @functools.cached_property
    def report(self):
        #Spending a lot of time and effort to get a result
        result = "Concise report: small is beautiful."
        return result
其执行：
>>> employee = Employee(name="Taciturn Man")
>>> employee.__dict__
{'name': 'Taciturn Man'}
>>> employee.report
'Concise report: small is beautiful.'
>>> employee.__dict__
{'name': 'Taciturn Man', 'report': 'Concise report: small is beautiful.'}

Smalltalk
下面的Smalltalk例子，具有典型的访问子方法，它返回使用惰性初始化的一个变量的值。

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

非惰性的替代者，使用的初始化方法是在对象被创建时运行的，并且使用简单的访问子方法来取回这个值。

initialize
    height := 2.0

height
    ^height

注意惰性初始化也可以用在非面向对象编程语言中。

Ruby
下面的Ruby例子，具有惰性初始化的来自远程服务的身份验证令牌。 @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

 另見 
 單例模式
 享元模式
 代理模式
 惰性載入
 双重检查锁定模式

引用

 外部連結 
 Article "Java Tip 67: Lazy instantiation - Balancing performance and resource usage" by Philip Bishop and Nigel Warren
 Java code examples 
 Use Lazy Initialization to Conserve Resources 
 Description from the Portland Pattern Repository 
 Lazy Initialization of Application Server Services
 Lazy Inheritance in JavaScript 

Category:軟體設計模式