热门问题
时间线
聊天
视角

惰性初始化模式

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

Remove ads

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

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

"惰性工廠"

以《設計模式》書中的觀點來說[1],惰性初始化通常會和工廠方法模式合作,這結合了三種構想:

  • 使用一個工廠去得到一個類別的實例(工廠方法模式)。
  • 將實例存在一個集合中,所以下次要求一個實例卻有「相同」參數時,可以得到「同一個」實例(可和單例模式來做比較)。
  • 在第一次時,使用惰性初始化來實例化物件(惰性初始化模式)。

示例

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

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++的例子:

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

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
Remove ads

Python

Python的例子:

class Fruit():
    def __init__(self, type_name):
        self.type_name = type_name
    def __str__(self):
        return f'Fruit: {self.type_name}'
    
class Fruits():
    def __init__(self, *args, **kwargs):
        self.type_dict = {}
    def get_fruit(self, type_name):
        if type_name not in self.type_dict:
            self.type_dict[type_name] = Fruit(type_name)
        return self.type_dict[type_name]

其執行:

>>> fruits = Fruits()
>>> print(fruits.get_fruit('Apple'))
Fruit: Apple
>>> print(fruits.get_fruit('Lime'))
Fruit: Lime
Remove ads

PHP

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): 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
*/

另見

引用

外部連結

Loading related searches...

Wikiwand - on

Seamless Wikipedia browsing. On steroids.

Remove ads