惰性初始模式
在程式设计中, 惰性初始是一种拖延战术。在第一次需求出现以前,先延迟创建物件、计算值或其它昂贵程序。
这通常是以一个旗号来实现,用旗号来标示是否完成其程式。每次请求对象时,会先测试此旗号。如果已完成,直接传回,否则当场执行。
对于此想法更一般的论述,可见惰性求值。
对指令式语言,这个模式可能潜藏著危险,尤其是使用共享状态的程式习惯。
"惰性工厂"
以设计模式的观点来说,惰性初始通常会和工厂方法模式合作,这结合了三种构想:
- 使用一个工厂去得到一个类别的实例(工厂方法模式)。
- 将实例存在一个集合中,所以下次要求一个实例却有相同参数时,可以得到同一个实例(可和单例模式来做比较)。
- 在第一次时,使用惰性初始来实例化物件(惰性初始模式)。
示例
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 (页面存档备份,存于互联网档案馆)