Приспособленец. Удаление неиспользуемых экземпляров из пула

При описании шаблона Приспособленец для упрощения примера не был рассмотрен механизм удаления неиспользуемых экземпляров. Рассмотрим вариант реализации этой функциональности на C#.

Сразу оговорим, что в качестве основы используем код примера из описания шаблона. Далее будет приведен только новый класс Пула приспособленцев и код для демонстрации его работы.

Кроме того, код будет приводиться по мере его разработки. Поэтому private метод появится до public. В рабочем варианте, разумеется, необходимо отсортировать методы в правильной последовательности.

Основная задача – это определить неиспользуемые экземпляры. Во многих языках программирования для этого пришлось бы ввести подсчет ссылок. Но в C# это можно сделать проще, используя слабые ссылки (WeakReference). Ссылка на экземпляр, хранимая в этом классе, не учитывается при определении мусорных объектов. Поэтому, любой экземпляр Приспособленца, не используемый клиентами, будет уничтожен при очередной сборке мусора.

Перейдем к реализации нового MapComponentFactory. Часть, реализующая шаблон Одиночка, останется не тронутой. Изменения начнутся с замены типа объектов в хранилищах на WeakReference.

public class MapComponentFactory
{
    private static readonly Lazy<MapComponentFactory> _instance
                = new Lazy<MapComponentFactory>(() => new MapComponentFactory());

    public enum Trees { Oak, Spruce, Pine, Birch, Aspen };
    public enum Roads { Direct, TurnLeft, TurnRight }

    private ConcurrentDictionary<Trees, WeakReference> _trees
        = new ConcurrentDictionary<Trees, WeakReference>();

    private ConcurrentDictionary<Roads, WeakReference> _roads
        = new ConcurrentDictionary<Roads, WeakReference>();

    private MapComponentFactory() { }

    public static MapComponentFactory Instance
    {
        get { return MapComponentFactory._instance.Value; }
    }

Теперь разработаем метод GetFromPoolOrCreate(), предназначенный для получения объекта из хранилища. Его параметрами будут хранилище (pool), указание требуемого варианта Приспособленца (keyValue) и метод, вызываемый при необходимости порождения экземпляра объекта (valueFactory).

    private IMapComponent GetFromPoolOrCreate<TKey>(
        ConcurrentDictionary<TKey, WeakReference> pool,
        TKey keyValue,
        Func<TKey, IMapComponent> valueFactory)
    {
        IMapComponent component = null;

        WeakReference componentRef = pool.GetOrAdd(
            keyValue,
            (key) => {
                component = valueFactory(key);
                return new WeakReference(component);
            });

        component = componentRef.Target as IMapComponent;
        if (component == null) {

            Console.WriteLine("RECREATE: {0}", keyValue.ToString());

            pool.TryRemove(keyValue, out componentRef);
            return this.GetFromPoolOrCreate<TKey>(pool, keyValue, valueFactory);
        }

        return component;
    }

Рассмотрим код метода подробнее. Вначале попытаемся получить слабую ссылку на требуемый экземпляр непосредственно из хранилища. Если такой нет, то создадим сам объект и слабую ссылку на него. Все это происходит в вызове GetOrAdd(). При обнаружении уже существующей слабой ссылки проверяем, не был ли сам экземпляр удален сборщиком (componentRef.Target as IMapComponent). Если необходимо – порождаем объект заново. Для этого убираем слабую ссылку из хранилища и вызываем GetFromPoolOrCreate() повторно.

Чтобы в демонстрации можно было зам��тить повторное создание, добавим вывод сообщения об этом событии на консоль (строка 18).

Остается только написать методы для получения нужных Приспособленцев.

    public IMapComponent CreateTree(Trees treeType)
    {
        return this.GetFromPoolOrCreate<Trees>(
            this._trees,
            treeType,
            key => new MapTreeFlyweight() { Title = key.ToString() });
    }

    public IMapComponent CreateRoad(Roads roadType)
    {
        return this.GetFromPoolOrCreate<Roads>(
            this._roads,
            roadType,
            key => new MapRoadFlyweight() { Title = key.ToString() });
    }

    public IMapComponent CreateHouse(string title)
    {
        return new MapHouse() { Title = title };
    }

Перейдем к демонстрации. В классе Demo перепишем код метода Execute(). В нем создадим карту города и удалим ссылку на нее. После этого вызовем сборку мусора, используя метод GC.Collect().

public static void Execute()
{
    IMapComponent city = BuildCity(MapComponentFactory.Instance);
    city = null;

    GC.Collect();

    city = BuildCity(MapComponentFactory.Instance);
}

Если запустить полученный пример, то из сообщений будет видно, что второй вызов BuildCity() приведет к повторному созданию экземпляров в Пуле приспособленцев.

Pingbacks and trackbacks (1)+

Добавить комментарий