Pergunta

Ao querer obter algumas hands-on experiência de um bom design OO eu decidi tentar aplicar separação de interesses em um aplicativo legado.

Eu decidi que eu não estava confortável com essas chamadas que estão sendo espalhados por toda a base de código.

ConfigurationManager.AppSettings["key"]

Embora eu já abordou isso antes, escrevendo uma classe auxiliar para encapsular as chamadas para métodos estáticos eu pensei que poderia ser uma oportunidade para ir um pouco mais longe.

Eu percebo que em última análise, eu deveria ser destinado à injeção de uso dependência e sempre será 'codificação para as interfaces'. Mas eu não quero levar o que parece ser um passo muito grande. Nesse meio tempo eu gostaria de tomar pequenos passos para atingir esse objetivo final.

Alguém pode enumerar as medidas que recomendaria?

Aqui estão alguns que vêm à mente:

  • código de cliente tenha depender de uma interface não uma implementação concreta

  • manualmente dependências injetar em um interagir via construtor ou propriedade?

  • Antes de ir para o esforço de escolher e aplicar uma IoC recipiente como faço para manter o código correndo?

  • A fim de cumprir uma dependência do padrão construtor de qualquer classe que precisa de um valor de configuração pode usar uma fábrica (Com um método CreateObject () estático)?

Com certeza eu vou ainda têm uma dependência de concreto na Factory? ...

Eu mergulhada em Michael Feathers livro então eu sei que eu preciso para introduzir emendas, mas eu estou lutando para saber quando eu apresentei o suficiente ou muitos!

Atualizar

Imagine que chamadas de clientes métodos em WidgetLoader passando-as dependências necessárias (como uma IConfigReader)

WidgetLoader lê config para descobrir o que Widgets para carregar e pede WidgetFactory para criar um de cada vez

WidgetFactory lê config para saber o estado de colocar os Widgets para por padrão

delegados WidgetFactory para WidgetRepository para fazer o acesso aos dados, que lê config para decidir o diagnóstico ele deve registrar

Em cada caso acima deve o IConfigReader ser passado como uma batata quente entre cada membro da cadeia de telefonar?

É uma fábrica a resposta?

Para esclarecer seguir alguns comentários:

O meu principal objectivo é migrar gradualmente algumas configurações de aplicativos fora do arquivo de configuração e em alguma outra forma de persistência. Enquanto I compreender que com uma dependência injectado posso Extracto e Substituir para obter alguma bondade testes de unidade, a minha preocupação principal não está testando tanto a ponto do suficiente para começar a encapsular ser ignorante de onde as configurações de começar realmente persistiu.

Foi útil?

Solução

Quando refatoração um legado código-base que deseja de forma iterativa fazer pequenas mudanças ao longo do tempo. Aqui é uma abordagem:

  • Criar uma nova classe estática (ou seja MyConfigManager) com um método para obter a configuração de aplicativo (ou seja GetAppSettingString (key string)

  • Faça uma pesquisa global e substituir de "ConfigurationManager.AppSettings [" chave "] e substituir ocorrências com "MyConfigManager.GetAppSettingsString (" key") "

  • Test e check-in

Agora, sua dependência do ConfigurationManager é em um lugar. Você pode armazenar suas configurações em um banco de dados ou onde quer, sem ter que mudar de toneladas de código. A desvantagem é que você ainda tem uma dependência estática.

O próximo passo seria mudar MyConfigManager em uma classe instância regular e injetá-lo em classes onde é usado. Melhor abordagem aqui é fazê-lo de forma incremental.

  • Crie uma classe instância (e uma interface) ao lado da classe estática.

  • Agora que você tem ambos, você pode refazer as classes usando lentamente até que eles estão todos usando a classe instância. Injectar a instância para o construtor (utilizando a interface). Não tente para o big bang check-in, se existem muitos usos. Basta fazê-lo devagar e com cuidado ao longo do tempo.

  • Em seguida, basta excluir a classe estática.

Outras dicas

Normalmente é muito difícil de limpar um aplicativo herdado é passos pequenos, porque eles não são projetados para ser alterado desta forma. Se o código for completamente misturados e você não tem SoC é difícil mudança na coisa sem ser forçado a mudar tudo o mais ... Também muitas vezes é muito difícil de teste de unidade nada.

Mas, em geral, você tem que: 1) Encontre o mais simples (menor) classe não refeito ainda 2) Testes de gravação de unidade para esta classe para que você tenha confiança de que seu refatoração não quebrar nada 3) Faça a menor mudança possível (isso depende do projeto e seu senso comum) 4) Certifique-se de todos os testes passam 5) Commit e Goto 1

Gostaria de recomendar "refatoração", de Martin Fowler para lhe dar mais idéias: http://www.amazon.com/exec/obidos/ASIN/0201485672

Para o seu exemplo, a primeira coisa que eu faria é criar uma interface que irá expor a funcionalidade que você precisa ler configuração por exemplo.

public interface IConfigReader
{
    string GetAppSetting(string key);
    ...
}

e, em seguida, criar uma implementação que delega para a classe ConfigurationManager estática:

public class StaticConfigReader : IConfigReader
{
    public string Get(string key)
    {
        return ConfigurationManager.AppSetting[key];
    }
}

Em seguida, para uma determinada classe com uma dependência na configuração que você pode criar uma costura que inicialmente apenas retorna uma instância do leitor configuração estática:

public class ClassRequiringConfig
{
    public void MethodUsingConfig()
    {
        string setting = this.GetConfigReader().GetAppSetting("key");
    }
    protected virtual IConfigReader GetConfigReader()
    {
        return new StaticConfigReader();
    }
}

E substituir todas as referências a configmanager com usos de sua interface. Em seguida, para fins de teste você pode subclasse desta classe e substituir o método GetConfigReader a falsificações injetar, assim você não precisa de nenhum arquivo de configuração atual:

public class TestClassRequiringConfig : ClassRequiringConfig
{
    public IConfigReader ConfigReader { get; set; }
    protected override IConfigReader GetConfigReader()
    {
        return this.ConfigReader;
    }
}

[Test]
public void TestMethodUsingConfig()
{
    ClassRequiringConfig sut = new TestClassRequiringConfig { ConfigReader = fakeConfigReader };
    sut.MethodUsingConfig();

    //Assertions
}

Então, eventualmente, você será capaz de substituir este com injeção imóvel / construtor quando você adicionar um contêiner IoC.

EDIT: Se você não está feliz com a injeção casos em classes individuais como este (o que seria muito tedioso se muitas classes dependem da configuração), então você pode criar uma classe de configuração estática, e, em seguida, permitir que as alterações temporárias para o leitor de configuração para testar:

    public static class Configuration
{
    private static Func<IConfigReader> _configReaderFunc = () => new StaticConfigReader;

    public static Func<IConfigReader> GetConfiguration
    {
        get { return _configReaderFunc; }
    }

    public static IDisposable CreateConfigScope(IConfigReader reader)
    {
        return new ConfigReaderScope(() => reader);
    }

    private class ConfigReaderScope : IDisposable
    {
        private readonly Func<IConfigReader> _oldReaderFunc;

        public ConfigReaderScope(Func<IConfigReader> newReaderFunc)
        {
            this._oldReaderFunc = _configReaderFunc;
            _configReaderFunc = newReaderFunc;
        }

        public void Dispose()
        {
            _configReaderFunc = this._oldReaderFunc;
        }
    }
}

Em seguida, suas classes basta acessar a configuração através da classe estática:

public void MethodUsingConfig()
{
    string value = Configuration.GetConfigReader().GetAppSetting("key");
}

e os seus testes pode usar um falso com um espaço temporário:

[Test]
public void TestMethodUsingConfig()
{
    using(var scope = Configuration.CreateConfigScope(fakeReader))
    {
        new ClassUsingConfig().MethodUsingConfig();
        //Assertions
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top