Ліниве завантажування (шаблон проєктування)

Матеріал з Вікіпедії — вільної енциклопедії.
Перейти до навігації Перейти до пошуку

Ліниве завантажування (англ. Lazy Load) — шаблон проєктування, який покликаний покращити продуктивність шляхом завантаження даних, не одразу, а на вимогу.

При завантаженні даних зі сховища у пам'ять необхідно користуватись не тільки даними об'єкту, але і пов'язаних із ним сутностей. Якщо завантажувати усі об'єкти це погано вплине на продуктивність аплікації, особливо у випадках коли ці дані не потрібні.

Даний шаблон пропонує не завантажувати додаткові дані, коли в них немає потреби. Замість цього встановлюється маркер, що дані відсутні та при потребі можуть бути завантажені.

Протилежністю лінивому завантаженню являється жадібне.

Реалізація

[ред. | ред. код]

Є чотири основних способи реалізації лінивого завантаження: лінива ініціалізація, віртуальний проксі, контейнер значення та привид. Кожний із цих підходів має свої переваги та недоліки.

Лінива ініціалізація

[ред. | ред. код]

Даний підхід використовує спеціальний маркер для перевірки наявності даних. Часто у якості маркера може виступати нульовий вказівник — null. При звернені до даних перевіряється їх наявність та в разі відсутності відбувається завантаження даних зі сховища.

class LazyInitialization
{
	private Data _data;
	
	public Data GetData()
	{
		if (_data == null)
		{
			_data = _db.GetData();
		}
		
		return _data;
	}
}

// використання
var obj = new LazyInitialization();
obj.GetData(); // ліниве завантаження

Віртуальний проксі

[ред. | ред. код]

Даний підхід вимагає, щоб об'єкт приховувався за інтерфейсом. При звернені до об'єкта насправді відбуватиметься звернення до проксі, який завантажує дані та перенаправляє запит реальному об'єкту.

Цей підхід використовує фреймворк EntityFramework.

class IUserConfiguration
{
	bool IsSoundEnabled { get; set; }
}

class UserConfiguration : IUserConfiguration
{
	public bool IsSoundEnabled { get; set; }
}

class UserConfigurationProxy : IUserConfiguration
{
	private UserConfiguration _realUserConfiguration;

	public bool IsSoundEnabled 
	{
		get
		{
			LoadObjectIfEmpty();
			
			return _realUserConfiguration.IsSoundEnabled;
		}
		set
		{
			LoadObjectIfEmpty();
			
			_realUserConfiguration.IsSoundEnabled = value;
		}
	}
	
	private void LoadObjectIfEmpty()
	{
		if (realUserConfiguration == null)
		{
			_realUserConfiguration = _db.GetConfiguration();
		}
	}
}

class User 
{
	public string Name { get; set; }
	
	// властивість буде відсутня та завантажена у пам'ять при першому звернені
	public IUserConfiguration Configuration { get; set; } = new UserConfigurationProxy();
}

// використання
var obj = new User();
var config = obj.Configuration; // ліниве завантаження

Контейнер значення

[ред. | ред. код]

Необхідно створити обгортку для об'єкта, яка вміє завантажувати дані у пам'ять.

У мові C# даний шаблон реалізує клас Lazy.

public class ValueHolder<T>
{
	private T _value;
	private readonly Func<object, T> _factory;

	public ValueHolder(Func<object, T> factory)
	{
		_factory = factory;
	}

	public T GetValue(object parameter)
	{
		if (_value == null)
		{		
			_value = factory(parameter);
		}
		
		return _value;
	}
}

class UserConfiguration
{
	public bool IsSoundEnabled { get; set; }
}

class User 
{
	public string Name { get; set; }
	
	// властивість буде відсутня та завантажена у пам'ять при першому звернені
	public ValueHolder<UserConfiguration> Configuration { get; set; } = new ValueHolder<UserConfiguration>((params) => _db.LoadConfiguration());
}

// використання
var obj = new User();
var config = obj.Configuration.GetValue(params); // ліниве завантаження

Привид

[ред. | ред. код]

Об'єкт не містить даних (або містить частково), а при першому звернені до нього завантажує себе зі сховища.

class UserConfiguration
{
	private bool _isLoaded;
	private bool _isSoundEnabled;
	
	public bool IsSoundEnabled 
	{
		get
		{
			LoadObjectIfEmpty();
			
			return _isSoundEnabled;
		}
		set
		{
			LoadObjectIfEmpty();
			
			_isSoundEnabled = value;
		}
	}
	
	private void LoadObjectIfEmpty()
	{
		if (!_isLoaded)
		{
			var record = _db.Load();
			
			_isSoundEnabled = record.IsSoundEnabled;
					. . .
			
			_isLoaded = true;
		}
	}
}

class User 
{
	public string Name { get; set; }
	
	// властивість буде відсутня та завантажена у пам'ять при першому звернені
	public UserConfiguration Configuration { get; set; } = new UserConfiguration();
}

// використання
var obj = new User();
var config = obj.Configuration; // ліниве завантаження

Див. також

[ред. | ред. код]

Джерела

[ред. | ред. код]