惰性初始模式 - 维基百科,自由的百科全书
在程式設計中, 惰性初始是一種拖延戰術。在第一次需求出現以前,先延遲創建物件、計算值或其它昂貴程序。
這通常是以一個旗號來實現,用旗號來標示是否完成其程式。每次請求對象時,會先測試此旗號。如果已完成,直接傳回,否則當場執行。
對於此想法更一般的論述,可見惰性求值。
對指令式語言,這個模式可能潛藏著危險,尤其是使用共享狀態的程式習慣。
"惰性工廠"
[编辑]以設計模式的觀點來說,惰性初始通常會和工廠方法模式合作,這結合了三種構想:
- 使用一個工廠去得到一個類別的實例(工廠方法模式)。
- 將實例存在一個集合中,所以下次要求一個實例卻有相同參數時,可以得到同一個實例(可和單例模式來做比較)。
- 在第一次時,使用惰性初始來實例化物件(惰性初始模式)。
示例
[编辑]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 */ ?>
另見
[编辑]外部連結
[编辑]- 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 (页面存档备份,存于互联网档案馆)