Como novos de objetos ao usar DI

votos
1

Eu estava assistindo a um curso de injeção de dependência em Pluralsight que esclareceu algumas coisas para mim. Mas, apesar do fato de que ele vai mais algumas camadas, também não houve informações sobre o que exatamente é o caminho a percorrer quando você só tem que novos até objetos em seu codde .

Esta é a última peça do quebra-cabeça que eu estou lutando para entender. Eu sei sobre compisition Raiz e cerca de como usá-lo. Eu sei para evitar ServiceLocator e para evitar passar para baixo o contêiner IoC através de todos os níveis do código, definindo-o como um parâmetro ctor em praticamente todas as classes na base de código (que eu tanto ter visto lado a lado em uma empresa que eu trabalhava para).

Vamos considerar dois exemplos abaixo onde eu acredito que é impraticável impossível mão em tudo.

I pode injetar dependências, mas não posso possivelmente injetar tudo, ele não é chamado de injeção de objeto depois de tudo, então o problema é como criar dinamicamente objetos Doee dentro de um loop:

public class MyClass
{
    private IVerifyer _verifyer;
    public MyClass(IVerifyer verifyer)
    {
        _verifyer = verifyer;
    }

    public IList<Doer> PrepareDoingSomething(IList<IDoees> doees)
    {
        var doers = new List<Doer>();
        foreach (var doee in doees)
        {
            if (!Verifyer.Verify(doee)) throw new Exception(Blablabla...);
            doers.Add(new Doer(doee));
        }
        return doers;
    }
}

Agora, em alguns casos, menos dinâmicas, o problema é que, para algumas classes IO não há nem uma interface nem uma classe base abstrata disponível, o que significa que eles são difíceis lidar com no código de teste, e também eu nem sequer sei como para lidar com eles com um contêiner IoC, como a classe de processo:

public class Processor
{
    public Process ProcessSomething(IProcessee processee)
    {
        // do some pre-processing stuff here
        // static Start() returns a new Process instance
        return Process.Start(C:\MyApp.exe, $-option {processee.Option1});
    }
}

O que eu fiz até agora é introduzir em ambos os casos uma classe abstrata fábrica que eu possa injetar e que me dá o que eu preciso quando precisar dele (ou seja, de forma dinâmica em um loop). Eu tenho uma implementação para uso em produção. No código de teste que pode implementá-lo ou apenas zombar dele (já que é abstrato). Para a classe Process eu apresentei uma classe proxy com a interface (I) ProcessProxy que passa através das chamadas para a classe de processo real. Eu também pode zombar facilmente que se eu precisar. Os dois exemplos em seguida, ligue para o que eu listei abaixo.

Minha pergunta é, é que o caminho certo para ir nesses dois casos (que são minhas principais preocupações)? Eu sei que eu poderia desencadear respostas opinativo, mas eu só estou tentando descobrir se essa é a maneira recomendada no espírito de uma injeção de dependência e CompositionRoot implementação livro-como texto para a frente limpa e reta. Se isso não é a forma preferida para ir, então o que é?

exemplo de loop após refatoração e DI habilitado:

public class MyClass
{
    private IVerifyer _verifyer;
    private AbstractDoerFactory _doerFactory;
    public MyClass(IVerifyer verifyer, AbstractDoerFactory doerFactory)
    {
        _verifyer = verifyer;
        _doerFactory = doerFactory;
    }

    public IList<Doer> PrepareDoingSomething(IList<IDoees> doees)
    {
        var doers = new List<Doer>();
        foreach (var doee in doees)
        {
            if (!_verifyer.Verify(doee)) throw new Exception(Blablabla...);
            doers.Add(_doerFactory.GetNewDoer(doee));
        }
        return doers;
    }
}

exemplo processo depois de refatoração e DI habilitado:

public interface IProcessProxy : IDisposable
{
    TextReader StandardOutput { get; }
    TextReader StandardError { get; }
    int ExitCode { get; }
    Start(string fileName, string arguments);
    void Kill();
}

public class Processor
{
    private AbstractProcessProxyFactory _processProxyFactory;
    public Processor(AbstractProcessProxyFactory processProxyFactory)
    {
        _processProxyFactory = processProxyFactory;
    }

    public IProcessProxy ProcessSomething(IProcessee processee)
    {
        // do some pre-processing stuff here
        var processProxy = _processProxyFactory.GetProxyFactory();
        return processProxy.Start(C:\MyApp.exe, $-option {processee.Option1});
    }
}
Publicado 20/10/2018 em 13:58
fonte usuário
Em outras línguas...                            


2 respostas

votos
1

Injeção de dependência é uma ferramenta incrivelmente poderosa para organizar aplicações polimórficos complexos. Eu sempre olhar para as oportunidades de usá-lo - mas não é ideal para lidar com todos os detalhes de sua aplicação.

Embora possa ter sido apenas para fins de exemplo, eu não teria geralmente pensam de usar DI para remover a sua dependência de um serviço de sistema padrão como System.Process a menos que você tinha um bom motivo para querer trocá-lo com uma classe de processo personalizado . Este seria um caso de uso excessivo de injeção de dependência.

Dito isto, quando eu precisava para construir dinamicamente instâncias de tipos bem após o processo de bootstrapping eu encontrei fábricas para ser muito útil. Para a maior flexibilidade, não dependem de uma única instância de fábrica. Em vez disso, fornecer a capacidade de registrar fábricas para diferentes tipos de forma independente.

public interface IFactory
{ 
    Type FactoryType;
    object CreateInstance(); 
}

namespace Generic {
    public interface IFactory<T> : IFactory
    {
        new T CreateInstance();
    }
}

public class DoThisFactory : IFactory<DoThis> { ... }
public class DoThatFactory : IFactory<DoThat> { ... }


public class MyClass
{
    // use property injection
    public DoThisFactory DoThisFactory { get; set; }       
    public DoThatFactory DoThatFactory { get; set; }

    private DoThis _doThis = null;
    private DoThat _doThat = null;

    public DoThis DoThis
    {
        get {
            if(_doThis == null) _doThis = DoThisFactory.CreateInstance();
            return _doThis;
        }
    }

    public DoThat DoThat
    {
        get {
            if(_doThat == null) _doThat = DoThatFactory.CreateInstance();
            return _doThat;
        }
    }
}

No exemplo acima, MyClass conhece os diferentes tipos de coisas que ele precisa fazer - as dependências são explícitas. Isso me permite fornecer diferente (ou a mesma) classe de fábrica para diferentes tipos de IDoees.

Se você não sabe os tipos específicos de implementação que você acabará por estar trabalhando com, então você vai precisar para injetar uma matriz de instâncias IFactory e usar sua propriedade FactoryType registrá-los em um dicionário para pesquisa mais tarde.

public class MyClass
{
    private Dictionary<Type, IFactory> _registerations = new Dictionary<Type, IFactory>();

    public MyClass(IFactory[] factories)
    {
        for(int i = 0; i < factories.Count -1; i++)
        {
            _registrations.Add(factories(i).FactoryType, factories);
        }
    }

    public IEnumeruable<IDoer> GetDoers(Type[] types)
    {
        List<IDoer> doers = new List<IDoer>();
        for(int i = 0; i < types.Count - 1; i++)
        {
            doers.Add(_registerations(types(i)).CreateInstance());
        }
        return doers;
    }
}
Respondeu 21/10/2018 em 12:08
fonte usuário

votos
0

Você pode resolver o primeiro problema com uma fábrica, conforme descrito no OP, embora a questão é se você deve. A partir do OP, não está claro o que IDoeesé, mas geralmente, os objetos que são passados como argumentos de método, ou devolvidos como valores de retorno, tendem a ser newables em vez de injetáveis . A questão é, então, se você deve mesmo injetar uma fábrica. Ele pode ser mais apropriado para simplesmente new-se Doer, como também mostrado no OP.

Quando se trata de algo como Process, enquanto você pode tecnicamente extrair uma interface de uma classe concreta, e, em seguida, fazer um adaptador , isso geralmente não é a melhor maneira de usar Dependency Injection.

De acordo com o Princípio de inversão de dependência , abstrações não deve depender de detalhes; detalhes devem depender de abstrações. O que isso normalmente significa é que os clientes devem definir a abstração que eles precisam, e depois implementações devem corresponder abstração.

Assim, se um cliente tem que executar um processo, em vez de chamá-lo RunProcess, nomeá-lo de acordo com o problema que aborda. Se, por exemplo, você deseja executar um programa chamado encode.exe, por exemplo, para codificar alguns arquivos de áudio, o modelo de dependência de acordo com esse objetivo:

public class Foo
{
    private readonly IEncoder encoder;

    public Foo(IEncoder encoder)
    {
        this.encoder = encoder;
    }

    public Bar DoSomething(Baz baz)
    {
        // Maybe do something else first...

        this.encoder.Encode(/*...*/);

        // Maybe do something else after...
    }
}

Você pode então implementar IEncoderusando o ProcessAPI, se você precisa:

public class Encoder : IEncoder
{
    public void Encode(/*...*/)
    {
        // Start encode.exe here...
    }
}

Se a operação for long-running, pode voltar a Taskpartir Encode, em vez de void; Se você precisa ser capaz de cancelar a tarefa, passar um CancellationTokencomo um argumento de método para o Encodemétodo; e assim por diante. Modelar a dependência em torno dos requisitos do cliente (s), não os detalhes de implementação.

Respondeu 01/11/2018 em 07:21
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more