Value holder

Value holder

The roles in this pattern are:

  • ValueHolder– uses the IValueLoader through a Strategy Pattern to load the value when it is first accessed
  • IValueLoader
  • Factory

Good news. The ValueHolder, and the IValueLoader are reusable, so you don’t have to create these for every different object that you’re going to load lazily.

Value holder vs Lazy<T> type

ValueHolder approach is actually very similar to the Lazy<T> object. You can think of the Lazy<T> type as being the ValueHolder. The IValueLoader type is not used by the Lazy<T> as it uses a lambda expression instead of a separate type.

We can implement the Value Holder pattern using Lazy<T>, rather than using a ValueHolder and a separate IValueLoader interface that implements the Load method. For .NET 3.5 – or earlier – the ValueHolder technique is valuable, but with .NET 4 – or later – you’re probably better off to simply use the Lazy<T> type.

If you are still curious, here is an example.

The the property that we want to load lazily is a list of OrderItems that are related to the particular Order. We’re going to reference this list through the public Items property but its backing field for the property is of type ValueHolder<List>.

  public class OrderVH
    {
        public int Id { get; set; }

        public OrderVH(int id)
        {
            Id = id;
        }

        private ValueHolder<List<OrderItem>> _items;

        public List<OrderItem> Items
        {
            get { return _items.Value; }
        }

        internal void SetItems(ValueHolder<List<OrderItem>> valueHolder)
        {
            _items = valueHolder;
        }
    }

ValueHolder type is generic. It works for any given type, and it knows about an IValueLoader of that type.  The IValueLoader is injected through the constructor. It simply has a Load method that is able to Load the T type.

ValueHolder has a Value property of type T which checks whether the backing T type field is null. That value gets loaded using the loader that was injected only on the first access.

ValueHolder is not thread-safe.

  public class ValueHolder<T>
    {
        private T _value;
        private readonly IValueLoader<T> _loader;

        public ValueHolder(IValueLoader<T> loader)
        {
            _loader = loader;
        }

        public T Value
        {
            get
            {
                if(_value == null)
                {
                    _value = _loader.Load(); //not thread-safe
                }
                return _value;
            }
        }
    }

OrderItemLoader is the implementation of the IValueLoader. It has the Load method fetching the OrderItems from the database.

 public interface IValueLoader<T>
    {
        T Load();
    }

 public class OrderItemLoader : IValueLoader<List<OrderItem>>
    {
        private readonly int _orderId;

        public OrderItemLoader(int orderId)
        {
            _orderId = orderId;
        }

        public List<OrderItem> Load()
        {
            // fetch items from database by _orderId
            Debug.Print("Fetching OrderItems from Database");
            return new List<OrderItem>();
        }
    }

At the end we are setting the way that the ValueHolder is going to work in the factory.

 public class OrderFactory
    {
        public OrderVH CreateFromId(int id)
        {
            var order = new OrderVH(id);
            order.SetItems(new ValueHolder<List<OrderItem>>(new
            OrderItemLoader(id)));
            return order;
        }
    }

Loosing some encapsulation

With value holder we loose some encapsulation as the client knows about the lazy loading functionality. The calling code is working with a Value Holder type, it’s not actually working with the underlying type that it expects.

References

https://app.pluralsight.com/library/courses/patterns-library

https://martinfowler.com/eaaCatalog/lazyLoad.html

Leave a comment