Date Published: October 09, 2020
The Lazy Load Pattern can be used to boost performance in an application that works with a database or other external data store. It is related to the Proxy, Strategy, and Template Method Patterns.
Pluralsight's Design Patterns Library course has a module on the Lazy Load Pattern from Steve Smith.
The Lazy Load Pattern aims to reduce unnecessary work in an application that loads objects from a database, while avoiding loading aspects of the object that the application does not use. Depending on the specific implementation, the client may or may not know lazy loading is being used (though it is usually best if the client does not know).
Smith quotes Martin Fowler: "If you're lazy about doing things, you'll win when it turns out you don't need to do them at all."
The Lazy Load Pattern should be used when fetching an object requires multiple calls for its data, and some of that data is never used. This pattern is used for optimization and should be used only in specific cases - applying it indiscriminately can actually hurt performance. "Premature optimization is the root of all evil."
As of .NET 4, a
Lazy<T> type can be used for the backing field of
Beware of ripple loading. Ripple loading can happen, for example, when iterating through a collection of objects; a call to the database is made to fetch the first object, then another call is made to fetch the second object, then another for the third, and on and on. This is very inefficient. You would be better off to use a single request for all the objects. This problem can be mitigated by treating collections differently when implementing lazy loading, e.g. by fetching a whole collection or chunk of that collection when a request is made for an object in that collection.
Lazy Initialization - very simple: every property just checks if its backing field has already been populated, then fetches the value if necessary. This violates the DRY principle.
Virtual Proxy - Proxy looks like the full object. When a request is made for any of its properties, it goes to the data store to fetch data as needed. This is, as you might expect, related to the Proxy Pattern. This variant of the Lazy Load Pattern can cause identity problems, because the proxy will not be seen as an equivalent to the actual object; you can fix this by overriding the
Equals() method. The Virtual Proxy is implemented by inheriting from the same interface as the object it represents. To use a proxy, it is usually necessary to implement a factory to create the proxy in place of the object it represents. The Virtual Proxy is a good choice when working with collections.
Value Holder - provides lazy loading, but encapsulation is not implemented; the client knows it is working with a value holder. Very similar to
Lazy<T> type built into .NET 4. This implementation makes use of the Strategy Pattern.
Ghost - "a real object in a partial state." Initially, the ghost knows only its Id, but when any property is accessed, the ghost loads all of its state from the database. "The object is its own virtual proxy" (but this violates SRP). Ghost objects allow you to gather your data from the database in chunks, rather than making a new call to the database for every little piece of information in the object as it is needed. Each ghost knows whether it has been loaded or not. When ghost objects inherit from a superclass, the Template Method Pattern can be implemented for methods like
Thanks for reading! I hope you find this and other articles here at ilyanaDev helpful! Be sure to follow me on Twitter @ilyanaDev.