Ліниве завантажування (шаблон проєктування)
Ліниве завантажування (англ. 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; // ліниве завантаження
- Lazy Load [Архівовано 18 серпня 2020 у Wayback Machine.]