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

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

При описании шаблона Приспособленец для упрощения примера не был рассмотрен механизм удаления неиспользуемых экземпляров. Рассмотрим вариант реализации этой функциональности на 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)+

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