Decide decisão para memorizar instâncias / referências já construídas

5

Estou trabalhando no aplicativo WPF e precisando de uma decisão de design ideal para um cenário. Basicamente, eu estou lendo um assembly .net para extrair os tipos e propriedades e mapeá-lo para minhas próprias classes personalizadas criadas para armazenar esses dados para torná-los serializáveis. Durante a extração, exigi uma maneira de não ler o mesmo tipo novamente se ele for encontrado como parte de algum tipo de propriedade.

Problema

Eu quero uma maneira de lembrar todos os tipos extraídos e quando eles aparecerem novamente, use a mesma referência de algum armazenamento na memória.

Agora, posso pensar em uma solução usando o padrão de design Cache ou Fábrica. Mas precisa de um veredicto de especialistas para lidar com essa situação.

Atualmente, projetei abaixo da turma para guardar e memorizar as referências.

public abstract class Factory<S, T> : IFactory<S, T>
{
    private static readonly Dictionary<S, T> Cache = new Dictionary<S, T>();

    public T Create(S source)
    {
        if (!Cache.ContainsKey(source))
        {
            Cache.Add(source, CreateInstance(source));
        }
        return Cache[source];
    }

    protected abstract T CreateInstance(S source);
}
    
por Furqan Safdar 17.12.2016 / 14:53
fonte

1 resposta

1

Excluindo os descendentes da Fábrica que gravam no mesmo armazenamento (tornando o valor de retorno indeterminado), sua abordagem funcionaria bem para aplicativos de encadeamento único não cobertos com testes de Unidade e implementação de uma arquitetura de monolito.

Sugiro:

  • evite usar o estado global (Singleton ou membro estático)
  • injetar interface de fábrica em clientes para poder substituir a implementação (em particular, isso tornaria mais fácil desativar o memoization e testar clientes)
  • introduza segurança de thread
  • preocupações separadas de armazenamento em cache e criação
  • cubra o cache e as fábricas com testes unitários separados
  • teste o desempenho com e sem memoização

Por exemplo:

using System;
using System.Diagnostics;
using System.Collections.Concurrent;

/// Generic factory interface. Consider using Func<S,T> instead
public interface IFactory<S, T> {
  T Create(S source);
}

/// Memoization decorator
public sealed class MemoizedFactory<S, T> : IFactory<S, T> {
    private readonly IFactory<S, T> toDecorate;
    // Accessed from different threads
    private readonly ConcurrentDictionary<S, T> cache = new ConcurrentDictionary<S, T>();
    public MemoizedFactory(IFactory<S, T> toDecorate) {
       this.toDecorate = toDecorate;
    }
    public T Create(S source) {
       return cache.GetOrAdd(source, toDecorate.Create);
    }
}

public sealed class PrefixFactory: IFactory<String, String>  {
       public int invocationCount = 0;
       public String Create(String source) {
           invocationCount++;
           return "prefix" + source;
       }
}

class TestClass
    {
        static void Main(string[] args)
        {
            var prefixFactory = new PrefixFactory();
            var memoizedFactory = new MemoizedFactory<String, String>(prefixFactory);
            Trace.Assert(memoizedFactory.Create("test") == "prefixtest");
            Trace.Assert(prefixFactory.invocationCount == 1);
            Trace.Assert(memoizedFactory.Create("test") == "prefixtest");
            Trace.Assert(prefixFactory.invocationCount == 1);
            Trace.Assert(memoizedFactory.Create("test2") == "prefixtest2");
            Trace.Assert(prefixFactory.invocationCount == 2);
        }
    }
    
por 19.12.2016 / 18:06
fonte