Princípio de Inversão de Dependência (Swift) - Aplicável também sem polimorfismo? (Abstração: genéricos restritos)

5

Existem vários artigos / blogs explicando o Princípio da Inversão de Dependência (DIP) usando o Swift; Para citar alguns (top hits do Google):

Agora, o Swift é amplamente descrito (mesmo pela Apple) como uma linguagem de programação orientada a protocolos, e não uma linguagem OOP; Naturalmente, esses artigos realizam as abstrações no DIP usando protocolos em vez de herança, no entanto protocolos especificamente heterogêneos para permitir que a camada de política use o polimorfismo para invocar camadas inferiores sem precisar conhecer detalhes de implementação. Por exemplo:

// Example A
protocol Service {
    func work()
}

final class Policy {
    // Use heterogeneous protocol polymorphically.
    private let service: Service

    init(service: Service) { self.service = service }

    func doWork() { service.work() }
}

final class SpecificService: Service {
    func work() { /* ... */ print("Specific work ...") }
}

let policy = Policy(service: SpecificService())

// Resolves to doWork() of SpecificService at runtime
policy.doWork() // Specific work ...

Eu percebo que o polimorfismo é um dos principais conceitos do DIP (a menos que eu esteja enganado), mas de uma perspectiva de implementação do Swift, prefiro ver o DIP aplicado usando genéricos restritos por protocolo em vez de polimorfismo de tempo de execução. Por exemplo:

// Example B
protocol Service {
    func work()
}

final class Policy<PolicyService: Service> {
    private let service: PolicyService

    init(service: PolicyService) { self.service = service }

    func doWork() { service.work() }
}

final class SpecificService: Service {
    func work() { /* ... */ print("Specific work ...") }
}

let policy = Policy(service: SpecificService())

// policy.service specialized as SpecificService instance at compile time
policy.doWork() // Specific work ...

Eu não vi ninguém usar genéricos no contexto de DIP e Swift, então eu sou provavelmente o que está no escuro aqui, daí a questão.

Como princípio design , acredito que B acima atinja o mesmo objetivo que A , w.r.t. MERGULHO; e quando aplicado em uma linguagem de programação orientada a protocolo estaticamente tipada que geralmente prefere composição sobre herança, especificamente (afaik) protocolos e genéricos sobre protocolos e polimorfismo, eu preferiria usar B . Isso está naturalmente sob a restrição de que usamos apenas um único Policy especializado de cada vez e recorremos ao DIP para facilitar as alterações nos detalhes de baixo nível por meio da inversão / inversão de dependência.

Pergunta: O Exemplo B acima será considerado uma aplicação válida do DIP, mesmo que um Policy especializado "saiba" sobre o concreto Service em tempo de compilação (devido a genéricos; ainda desacoplado por abstrações aplicadas como restrições ao marcador genérico)?

    
por dfri 14.01.2018 / 20:52
fonte

1 resposta

4

Disclaimer: Eu não sei Swift. Com isso fora do caminho - se os genéricos Swift são parecidos com os genéricos C #, e parece que eles são um pouco, eu não diria que a política "sabe" sobre o serviço concreto, porque você não pode realmente chamar qualquer método específico para o serviço concreto - você tem que trabalhar com o que está exposto através do protocolo de serviço. O fato de o compilador saber como gerar uma versão que pode funcionar com o tipo concreto não afeta realmente o acoplamento de seu código. Você ainda pode alterar os detalhes de implementação de qualquer serviço concreto sem afetar a Política, desde que ela esteja de acordo com o protocolo do Serviço. Então isso está de acordo com o DIP - é só que os mecanismos envolvidos são ligeiramente diferentes.

Dito isso, não acho que você ganhe muito aqui (a menos que exista algo específico para Swift de que eu não tenha conhecimento). Do ponto de vista do código do cliente, as duas variantes são praticamente as mesmas. Do ponto de vista da manutenção de código, parece um pouco confuso (IMO). E você não necessariamente obterá melhor desempenho, porque, até onde eu sei, o compilador provavelmente irá gerar vtables baseado nas restrições genéricas, e usar o despacho dinâmico para chamar polimorficamente o método correto no tipo correto (generics don ' t funcionam da mesma forma que os modelos C ++). Claro, o compilador pode ser capaz de otimizar alguns desses itens em certas situações, mas mesmo assim, presumindo que isso equivaleria a uma otimização prematura de sua parte (e, além disso, você deve fazer medições para determinar se há algum desempenho real ganhos, em vez de assumir que haverá).

P.S. O DIP não precisa necessariamente envolver os mecanismos de linguagem comumente usados para empregá-lo, como o polimorfismo baseado em herança. Ele afirma que um componente de alto nível não deve depender diretamente de um componente de baixo nível, mas que ambos devem depender de uma abstração. Essa abstração deve ser "de propriedade" (definida por, ou prescrito por) o componente de nível superior. Conceitualmente faz parte do componente de nível superior, se você quiser. O componente de nível inferior deve então aderir ao "contrato" imposto por essa abstração, e a inversão ocorre por meio do componente de baixo nível ser dependente dos outros dois. Assim, embora o papel da abstração seja frequentemente realizado por um protocolo ou uma classe base, você também pode fazer algo bastante estranho, como fazer com que o componente de alto nível grave comandos personalizados em um arquivo em um local conhecido, que seria então lido e interpretado pelo componente de baixo nível. Aqui, a abstração é a combinação do mecanismo de comunicação, o local do arquivo e o conjunto de comandos personalizados, e novamente, tudo isso é levado para ser definido e pertencente ao componente de alto nível, e o componente de baixo nível deve aderir a esses requisitos . É incomum, mas ainda confirma para o DIP. Não é sobre a implementação exata, é sobre como os vários elementos interagem para controlar o acoplamento entre diferentes partes do sistema.

    
por 15.01.2018 / 01:07
fonte