跳转到内容

惰性初始模式

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

程式设计中, 惰性初始是一种拖延战术。在第一次需求出现以前,先延迟创建物件、计算值或其它昂贵程序。

这通常是以一个旗号来实现,用旗号来标示是否完成其程式。每次请求对象时,会先测试此旗号。如果已完成,直接传回,否则当场执行。

对于此想法更一般的论述,可见惰性求值

对指令式语言,这个模式可能潜藏著危险,尤其是使用共享状态的程式习惯。

"惰性工厂"

设计模式的观点来说,惰性初始通常会和工厂方法模式合作,这结合了三种构想:

  • 使用一个工厂去得到一个类别的实例(工厂方法模式)。
  • 将实例存在一个集合中,所以下次要求一个实例却有相同参数时,可以得到同一个实例(可和单例模式来做比较)。
  • 在第一次时,使用惰性初始来实例化物件(惰性初始模式)。

示例

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();
    }
}
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);
    }
}
#include <iostream>
#include <string>
#include <map>
 
using namespace std;
 
class Fruit {
    public:
        static Fruit* getFruit(const string& type);
        static void printCurrentTypes();

    private:
        static map<string,Fruit*> types;
        string type;

        // note: constructor private forcing one to use static getFruit()
        Fruit(const string& t) : type( t ) {}
};

//definition needed for using any static member variable
map<string,Fruit*> Fruit::types; 
 
/*
 * Lazy Factory method, gets the Fruit instance associated with a
 * certain type. Instantiates new ones as needed.
 * precondition: type. Any string that describes a fruit type, e.g. "apple"
 * postcondition: The Fruit instance associated with that type.
 */
Fruit* Fruit::getFruit(const string& type) {
    Fruit *& f = types[type];   // try to find a pre-existing instance, or std::map'll create one if not found
 
    if (!f) {                   // if it was created by map automatically, it'll be pointing to NULL
        // couldn't find one, so make a new instance
        f = new Fruit(type); // lazy initialization part
    }
    return f;
}

/*
 * For example purposes to see pattern in action
 */
void Fruit::printCurrentTypes() {
    if (!types.empty()) {
        cout << "Number of instances made = " << types.size() << endl;
        for (map<string,Fruit*>::iterator iter = types.begin(); iter != types.end(); ++iter) {
            cout << (*iter).first << endl;
        }
        cout << endl;
    }
}

int main(void) {
    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();
 
    return 0;
}

/*
輸出:
Number of instances made = 1
Banana
 
Number of instances made = 2
Apple
Banana
 
Number of instances made = 2
Apple
Banana
*/

The following is an example (in Smalltalk) of a typical accessor method to return the value of a variable using lazy initialization.

    height
        height ifNil: [height := 2.0].
        ^height
</source>
The 'non-lazy' alternative is to use an initialization method that is run when the object is created and then use a simpler accessor method to fetch the value.

<source lang="smalltalk">
    initialize
        height := 2.0

    height
        ^height

Note that lazy initialization can also be used in non-object-oriented languages.

The following is an example (in Ruby) of lazily initializing an authentication token from a remote service like Google. The way that @auth_token is cached is also an example of memoization.

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
class Fruit:
    def __init__(self, type):
        self.type = type
    
class Fruits:
    def __init__(self):
        self.types = {}
    
    def get_fruit(self, type):
        if type not in self.types:
            self.types[type] = Fruit(type)
        
        return self.types[type]

if __name__ == '__main__':
    fruits = Fruits()
    print fruits.get_fruit('Apple')
    print fruits.get_fruit('Lime')
<?php
header('Content-type:text/plain; charset=utf-8');

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

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

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

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

    public static function printCurrentTypes()
    {
        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
*/
?>

另见

外部链接