惰性初始模式
在程式設計中, 惰性初始是一種拖延戰術。在第一次需求出現以前,先延遲創建物件、計算值或其它昂貴程序。
這通常是以一個旗號來實現,用旗號來標示是否完成其程式。每次請求對象時,會先測試此旗號。如果已完成,直接傳回,否則當場執行。
對於此想法更一般的論述,可見惰性求值。
對指令式語言,這個模式可能潛藏著危險,尤其是使用共享狀態的程式習慣。
"惰性工廠"
示例
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 (頁面存檔備份,存於網際網路檔案館)