热门问题
时间线
聊天
视角

惰性初始化

来自维基百科,自由的百科全书

Remove ads

程式设计中,惰性初始化(英语:lazy initialization),是一种拖延战术。在第一次需求出现以前,先延迟创建物件、计算或其它昂贵程序。这通常是以一个旗号来实现,用旗号来标示是否完成其程式。每次请求对象时,会先测试此旗号。如果已完成,直接传回,否则当场执行。

对于此想法更一般的论述,可见惰性求值。对指令式语言,这个模式可能潜藏著危险,尤其是使用共享状态的程式习惯。

惰性工厂

设计模式》书中的工厂方法模式的早于C++98的实现[1],由于这里的工厂方法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;
}

这种“惰性工厂”结合了二个构想:

  • 工厂方法:使用工厂方法,来获得实例
  • 惰性初始化技术:使用惰性初始化,实例化物件于其第一次被要求之时。

在软件设计实践中经常还会结合上第三个构想:

  • 多例模式英语Multiton pattern:将实例存储在一个映射[2],在以“相同”的参数要求一个实例之时,返回“同一个”单例,有人将这种参数化或为单例注册类型名字的“惰性工厂”称为惰性初始化模式。
Remove ads

理论计算机科学

理论计算机科学领域中,惰性初始化(也叫做惰性数组)[3],是设计数据结构的技术,使其可以工作于不需要被初始化的内存。尤其是假定了要访问n个(编号从 1n)未初始化内存单元的一个表格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]是初始化的单元之一,而对应的值是这个键值对的第二个组件。

Remove ads

示例

C++

C++的例子:

import std;

class Fruit {
private:
    static std::map<std::string, Fruit*> types;
    std::string type_;

    // Note: constructor private forcing one to use static |GetFruit|.
    Fruit(const std::string& type): 
        type_{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& 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.
    static void printCurrentTypes() {
        std::println("Number of instances made = {}", types.size());
        for (const auto& [type, fruit] : types) {
            std::println({}, type);
        }
        std::println();
    }
};

// static
std::map<std::string, Fruit*> 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
//
Remove ads

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);
    }
}
Remove ads

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

Python

Python的对象惰性实例化例子:

from abc import ABCMeta
from weakref import WeakSet

class Fruit(metaclass=ABCMeta):
    def __init__(self, name, **kwargs):
        self.name = name
        for key, value in kwargs.items():
            self.__dict__[key] = value

class FruitFuture():
    future_set = WeakSet()
    def __init__(self, name, **kwargs):
        self.name = name
        self.kwargs = kwargs
        type(self).future_set.add(self)
    def __call__(self):
        if self in type(self).future_set:
            fruit = Fruit(self.name, **self.kwargs)
            type(self).future_set.remove(self)
        else:
            fruit = None
        return fruit
    @classmethod
    def info(cls):
        print(f'Instances deferred: {len(cls.future_set)}')

Fruit.register(FruitFuture)

这里实现了创建产品的Future,采用同于解弱引用的形式获得结果,略过了可附加的多例模式英语Multiton pattern。它与享元模式中用Python写的类似例子的区别在于,它的目标是推延而非共享。下面是其执行:

>>> fruit1 = FruitFuture('Banana')
>>> assert isinstance(fruit1, Fruit)
>>> fruit2 = FruitFuture('Apple')
>>> FruitFuture.info()
Instances deferred: 2
>>> fruit1 = fruit1()
>>> fruit2 = fruit2()
>>> FruitFuture.info()
Instances deferred: 0
>>> fruit2 = FruitFuture('Banana')()
>>> FruitFuture.info()
Instances deferred: 0

Python的对象惰性初始化例子[4]

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

其执行:

>>> employee = Employee("Taciturn Man", "Large Institution")
>>> employee.report
'Voluminous document: 100,000 words omitted here.'
>>> employee.report = "Concise documentation: small is beautiful."
>>> employee.report
'Concise documentation: small is beautiful.'
Remove ads

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

另见

引用

外部链接

Loading related searches...

Wikiwand - on

Seamless Wikipedia browsing. On steroids.

Remove ads