Como desacoplar a implementação simples de fábrica e padrão?

5

Eu tenho uma classe de fábrica simples ( FileResources ) com métodos de fábrica estática, fornecendo uma implementação padrão ( DefaultFileResource ).

public final class FileResources {
    private FileResources() {}
    public static FileResource get(Path path) {
        return new DefaultFileResource(path);
    }
}

O problema com essa classe é que ela é strongmente acoplada à implementação padrão ( DefaultFileResource ).

Dado um novo requisito de que classes parte da API ( FileResources ) não podem depender diretamente de classes que não fazem parte da API ( DefaultFileResource ), concluí que a única solução é inversão de controle, injeção de dependência ou a < s localizador de serviços do provedor de serviços .

É possível separar FileResources da implementação padrão ( DefaultFileResource )?

Até onde eu sei, isso só é possível injetando a dependência em FileResources através de um método construtor, campo ou setter.
O principal problema é que o construtor de FileResources é privado, além disso, ele só possui métodos estáticos de fábrica.

A alternativa é o padrão do método de fábrica. Haveria uma fábrica abstrata (uma classe API) e haveria fábricas concretas (classes não-API) que poderiam ser injetadas para separar as classes API e não-API.

De qualquer forma, um contêiner de dependência é necessário para lidar com a fiação.

O que você acha? Estou pensando nisso?

    
por Vorzard 11.03.2014 / 18:38
fonte

2 respostas

6

Não, um contêiner IOC não é necessário para dissociar as classes API e não API. Porque eles não estão realmente acoplados.

FileResources é acoplado ao Path e ao FileResource, mas não ao DefaultFileResource. Ou seja, se você alterar Path ou FileResource, haverá um risco de quebra de FileResources. Embora não seja grande. Essas são camadas bem finas; isso é um acoplamento aceitável.

No entanto, se você alterar DefaultFileResource, contanto que ainda esteja em conformidade com FileResource , não será possível quebrar FileResources.

Você nem mesmo quebraria nenhum teste para o FileResorces. Eles testam apenas que um DefaultFileResource é retornado, sob as condições padrão, e tudo bem.

Ainda mais importante, o seu código de chamada não é acoplado a qualquer implementação específica do FileResource. Isso é bom. Na verdade, é o ponto principal de um método de fábrica. (E este é um método de fábrica. Não há distinção de classe de fábrica, no contexto que você usou; não importa se o método de fábrica existe em outra classe.)

No entanto, seu código de chamada é acoplado a FileResources. Mais notavelmente, se você alterar a implementação de FileResources, há todas as chances de que você quebre qualquer teste de unidade que esteja testando seu código de chamada, porque o método get não pode ser apagado.

Essa é uma preocupação muito maior do que qualquer conhecimento que o FileResources tem sobre o DefaultFileResource.

A única razão pela qual você usaria um contêiner IOC para a fábrica é se você deseja criar DefaultFileResource com suas dependências. Eu evitaria que, a menos que você precisasse e, se precisasse, eu desse uma olhada no padrão Service Locator e por que deveria ser evitado, antes de decidir pelo uso desse contêiner IOC.

    
por 11.03.2014 / 19:20
fonte
1

O que fazer realmente depende de quão longe você deseja ir nas abstrações. Uma maneira simples de usar é adicionar um método .getInstance(Path path) estático à classe abstrata FileResource (se for uma interface, você a transformaria em uma classe abstrata), o que retornaria uma instância padrão. Isso pode não atender exatamente às suas necessidades ( FileResource agora seria dependente da implementação padrão) e seu aplicativo pode ter problemas de herança se você alterar uma interface para uma classe abstrata.

A segunda opção é criar um FileResourceFactory . Pode ser uma classe ou uma interface com a implementação de classes. Uma única classe de implementação é mais simples, mas cria dependências em implementações, enquanto uma interface significa mais complexidade, mas potencialmente menos acoplamento.

Como esta é uma classe estática, a injeção de dependência através do construtor não é possível, mas você pode criar um método setter para um FileResourceFactory estático e injetá-lo quando precisar. Dois avisos surgem aqui, porém:

  • Você não pode garantir que a fábrica estática foi definida antes de seu método get ser chamado
  • Você está vagando perigosamente perto do território de variáveis globais com um membro estático em uma classe como esta

Eu também quero salientar que, em algum momento, alguém eventualmente precisa pegar algo concreto. Com a abordagem de injeção de dependência, você pode levar isso muito longe, mas eventualmente alguém dependerá de uma instância concreta em algum lugar.

    
por 11.03.2014 / 18:55
fonte