مقداردهی اولیه کاهلانه

در برنامه نویسی کامپیوتری، مقداردهی اولیهٔ کاهلانه (به انگلیسی: Lazy Initialization) یک الگو برای به تأخیر انداختن ساخت یک شیء می‌باشد، به طوری که محاسبه و ایجاد شیء مورد نظر تا زمانی که به آن شیء نیازی نباشد معوق می‌شود. این الگو زمانی استفاده می‌شود که ساخت شیء از نظر هزینهٔ محاسبه‌گران می‌باشد و اجرای برنامه در گرو ساخته شدن آن شیء نمی‌باشد. بدین ترتیب برنامه روال عادی اجرای خود را با سرعت بیشتری طی می‌کند و تنها وقتی به ساختن آن شیء می‌پردازد که به‌طور مستقیم به آن نیازمند بشود. برای مثال تا وقتی که در برنامه نیاز به ارتباط به پایگاه داده‌ها نداریم شیء اتصال دهنده به پایگاه داده‌ها را نمی‌سازیم و از هزینه‌های محاسباتی ساخت چنین شیء ای اجتناب می‌کنیم، ولی به محض اینکه در این برنامه خواستیم به پایگاه داده‌ها متصل بشویم، برنامهٔ ما شیء اتصال را ساخته و از آن استفاده می‌کند.

معمولاً برای پیاده‌سازی این الگو از یک متغیر کمکی استفاده می‌کنند که می‌تواند ۲ حالت به خود بگیرد. حالت صفر به این معنا می‌باشد که پروسهٔ ساخت شیء هنوز رخ نداده و حالت یک یعنی این که این پروسه اجرا شده و شیء قبلاً ساخته شده. هر بار که برنامه به شیء مورد نطر ارجاع می‌دهد، با چک کردن این متغیر کمکی می‌فهمیم که آیا شیء تا آلان ساخته شده یا نه. اگر شیء ساخته شده بود که می‌توان از همان برای ادامهٔ اجرای برنامه استفاده کنیم، در غیر این صورت بایستی ابتدا شی را ساخته و مقدار دهی کرده و سپس مقدار متغیر کمکی را برابر یک قرار دهیم.

بایستی توجه داشته باشیم که در یک کد چندریسمانی دسترسی به این متغیر کمکی بایستی به صورت سنکرون باشد تا حالت مسابقه پیش نیاید.

کارخانهٔ ساخت شی به صورت کاهلانه ویرایش

در مبحث الگوهای طراحی مهندسی نرم‌افزار، مقداردهی کاهلانه معمولاً به کمک الگوی متد کارخانه انجام می‌شود.

که به ترکیب سه ایده می‌پردازد:

  1. استفاده از الگوی متد کارخانه برای ایجاد یک نمونه از یک کلاس
  2. ذخیره کردن نمونه‌های ساخته شده از آن کلاس در یک آرایه انجمنی (map). بدین ترتیب هر وقت که آن شیء را بخواهیم بتوانیم به کمک مشخصاتش از map آن را بدست آوریم.
  3. استفاده از تکنیک مقدار دهی اولیه کاهلانه برای ساخت شی برای اولین بار.

مثال ویرایش

Java ویرایش

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

public enum FRUIT_TYPE {
NONE,
APPLE,
BANANA,
}

class Program {

/**
 * @param args
 */
public static void main(String[] args) {
Fruit.getFruitByTypeName(FRUIT_TYPE.BANANA);
Fruit.showAll();
Fruit.getFruitByTypeName(FRUIT_TYPE.APPLE);
Fruit.showAll();
Fruit.getFruitByTypeName(FRUIT_TYPE.BANANA);
Fruit.showAll();
}
}

public class Fruit {
private static Map<FRUIT_TYPE, Fruit> types = new HashMap<>();

/**
 * Using a private constructor to force the use of the factory method.
 * @param type
 */
private Fruit(FRUIT_TYPE type) {
}

/**
 * Lazy Factory method, gets the Fruit instance associated with a certain
 * type. Instantiates new ones as needed.
 * @param type Any allowed fruit type, e.g. APPLE
 * @return The Fruit instance associated with that type.
 */
public static Fruit getFruitByTypeName(FRUIT_TYPE type) {
Fruit fruit;
                // This has concurrency issues.  Here the read to types is not synchronized,
                // so types.put and types.containsKey might be called at the same time.
                // Don't be surprised if the data is corrupted.
if (!types.containsKey(type)) {
// Lazy initialisation
fruit = new Fruit(type);
types.put(type, fruit);
} else {
// OK, it's available currently
fruit = types.get(type);
}

return fruit;
}

/**
 * Lazy Factory method, gets the Fruit instance associated with a certain
 	 * type. Instantiates new ones as needed. Uses double-checked locking
 * pattern for using in highly concurrent environments.
 * @param type Any allowed fruit type, e.g. APPLE
 * @return The Fruit instance associated with that type.
 */
public static Fruit getFruitByTypeNameHighConcurrentVersion(FRUIT_TYPE type) {
if (!types.containsKey(type)) {
synchronized (types) {
// Check again, after having acquired the lock to make sure
// the instance was not created meanwhile by another thread
if (!types.containsKey(type)) {
// Lazy initialisation
types.put(type, new Fruit(type));
}
}
}

return types.get(type);
}

/**
 * Displays all entered fruits.
 */
public static void showAll() {
if (types.size() > 0) {
System.out.println("Number of instances made = " + types.size());

for (Entry<FRUIT_TYPE, Fruit> entry : types.entrySet()) {
System.out.println(
Constants.firstLetterToUpper(entry.getKey().toString()));
}

System.out.println();
}
}
}

خروجی

Number of instances made = 1
Banana
Number of instances made = 2
Banana
Apple
Number of instances made = 2
Banana
Apple