Como embrulhar um serviço para que fique mais simples

11

Temos uma dependência de um serviço de terceiros que expõe uma interface gigantesca da qual precisamos apenas de três métodos. Além disso, a interface muda frequentemente ...

Decidi agrupar a interface em uma classe em nosso projeto e apenas expor os métodos que precisamos.

Mas não tenho certeza de como devo lidar com os valores retornados ... A interface retorna um objeto do tipo Storage. Internamente, temos um tipo StorageModelque é nossa representação interna de a Storage.

O que você retornaria no mapeador: Storageou StorageModel? Temos um DataService StorageServiceque obtém uma dependência do wrapper injetado.

Atualmente eu estou fazendo isso basicamente assim:

public class StorageService 
{
    private readonly IExternalStorageWrapper externalStorageWrapper;

    public StorageService(IExternalStorageWrapper externalStorageWrapper)
    {
        this.externalStorageWrapper = externalStorageWrapper;
    }

    public StorageModel GetStorage(int storageId)
    {
        return this.externalStorageWrapper.GetStorage(storageId).ConvertToStorageModel();
    }
}

public class ExternalStorageWrapper : IExternalStorageWrapper
{
    public Storage GetStorage(int storageId)
    {
        using(var ext = new ExternalStorage())
        {
            return ext.GetStorage(storageId);
        }
    }
}

O que você diria:

  • É bom como acima, que o wrapper retorne o Storageobjeto externo e o interno StorageServiceretorne o interno StorageModel?
  • Ou você já retornaria um StorageModelno invólucro?
xerafim
fonte
2
Por que você o chama de invólucro? É melhor procurar o padrão de fachada, ponte e adaptador. Pelo que entendi, um wrapper forneceria todos os métodos do serviço de terceiros - mas não é isso que você deseja.
Tobias Otto
@TobiasOtpara um invólucro não precisar expor todo o comportamento do objeto embrulhado, consulte este artigo em "Um invólucro limitado".
precisa

Respostas:

11

Na minha opinião, o wrapper deve lidar com todas as coisas relacionadas à biblioteca externa. Isso significa que a interface pública do Wrapper não deve nomear nenhum tipo externo.

O mapeamento de tipos externos para os respectivos tipos de seu aplicativo faz parte das tarefas do wrapper. Se esta não é uma operação trivial, você pode usar as várias ferramentas disponíveis para decompor o problema, por exemplo, injetando um objeto tradutor. No entanto, o tradutor ainda deve fazer parte do seu módulo wrapper e nenhuma outra parte do seu aplicativo pode depender dele.

Dessa forma, o restante do seu aplicativo é completamente imune não apenas a alterações na biblioteca, mas também a substituições da biblioteca por outra.

doubleYou
fonte
3

Decidi agrupar a interface em uma classe em nosso projeto e apenas expor os métodos que precisamos.

Isso está ok. Isso também é conhecido como adaptador .

Você escolhe o padrão do adaptador , portanto, o objetivo aqui é transformar uma interface (modelo de biblioteca) em outra (modelo de domínio). Portanto, se algo do anterior atingir o modelo de domínio, o adaptador falhará em seu objetivo .

De acordo com os argumentos anteriores, o adaptador deve retornar o StorageModel.

Por fim, seu domínio "fala" um idioma específico, onde Storageé um estranho .

Mas não tenho certeza de como devo lidar com os valores de retorno ...

A chave aqui é saber por que motivo você está agrupando / adaptando a biblioteca .

Os padrões de adaptador, decorador e fachada podem ter semelhanças, mas são bastante diferentes. Tão diferentes quanto os problemas que eles resolvem.

Dito isto, você também pode estar interessado em:

Laiv
fonte
1

Você não pode efetivamente agrupar uma biblioteca duplicando-a.

O que você deve agrupar é o uso da biblioteca e isso significa não expor seus objetos, neste caso Armazenamento. Também não tente duplicá-los.

Use a biblioteca, mas mantenha-a contida. Portanto, no seu caso, supondo que você esteja usando o StorageService para armazenar coisas que você deve agrupar em repositórios

MyPocoObjectRepo
    MyPocoObject GetObject(string id);

onde MyPocoObject é inteiramente sua lógica de dados e negócios. Não é uma duplicação de armazenamento ou um DataReader ou qualquer coisa

Ewan
fonte
0

A resposta é que depende se você precisa ou não acessar Storagediretamente de uma classe que não é StorageModel.

Se você deseja agrupar a biblioteca, faz sentido também agrupar o objeto retornado por ela para permitir futuras alterações de prova feitas pela biblioteca no futuro. No entanto, se você precisar usar Storagediretamente, isso significa que pode haver necessidade de retornar de Storageacordo com a situação. Pode-se dizer um argumento para obrigar o Storageuso aqui a ser o StorageModelque você provavelmente deseja manter consistente durante todo o programa.

Eu recomendo fortemente que você envolva a interface e o objeto retornado, se ainda não o estiver fazendo, no entanto, isso só faz sentido se você estiver usando apenas StorageModelo programa e não o fizer Storage.

Neil
fonte