惰性初始模式

在程式設計中，惰性初始是一種拖延戰術。在第一次需求出現以前，先延遲建立物件、計算值或其它昂貴程式。

這通常是以一個旗號來實現，用旗號來標示是否完成其程式。每次請求物件時，會先測試此旗號。如果已完成，直接傳回，否則當場執行。

對於此想法更一般的論述，可見惰性求值。

對指令式語言，這個模式可能潛藏著危險，尤其是使用共享狀態的程式習慣。

"惰性工廠"

以設計模式的觀點來說，惰性初始通常會和工廠方法模式合作，這結合了三種構想：

使用一個工廠去得到一個類別的例項（工廠方法模式）。
將例項存在一個集合中，所以下次要求一個例項卻有相同引數時，可以得到同一個例項（可和單例模式來做比較）。
在第一次時，使用惰性初始來例項化物件（惰性初始模式）。

示例

2.1 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 _typesDictionary = new Dictionary ;

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 = _typesDictionarytype;

return fruit;

}

public static void ShowAll

{if (_typesDictionary.Count> 0)

{Console.WriteLine ("Number of instances made = {0}", _typesDictionary.Count);

foreach (KeyValuePair 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 ;}

}

2.2 Java

import java.util.*;

public class Fruit

{private static final Map types = new HashMap ;

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

}

}

2.3 C++

include

using namespace std;

class Fruit {

public:

static Fruit* getFruit (const string& type);

static void printCurrentTypes ;

private:

static map 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 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 = typestype; //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 ::iterator iter = types.begin ; iter != types.end ; ++iter) {

cout

2.4 Smalltalk

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

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.

initialize

height := 2.0

height

^height

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

2.5 Ruby

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)) &&

gettokenfromhttpresponse (res)

end

gettokenfromhttpresponse, uri and params are defined later in the class

end

b = Blogger.new

b.instancevariableget (:@auth_token) # returns nil

b.auth_token # returns token

b.instancevariableget (:@auth_token) # returns token

2.6 Python

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.typestype = Fruit (type)

return self.typestype

if name == 'main':

fruits = Fruits

print fruits.get_fruit ('Apple')

print fruits.get_fruit ('Lime')

2.7 PHP

type = $type;

}

public static function getFruit ($type)

{

// Lazy initialization takes place here

if (!arraykeyexists ($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

/

?>