( Para o propósito desta pergunta, quando digo 'interface', quero dizer a construção da linguageminterface
, e não uma 'interface' no outro sentido da palavra, ou seja, os métodos públicos que uma classe oferece ao mundo externo para se comunicar com e manipulá-lo. )
O acoplamento frouxo pode ser obtido fazendo com que um objeto dependa de uma abstração em vez de um tipo concreto.
Isso permite um acoplamento flexível por duas razões principais: 1 - as abstrações têm menos probabilidade de mudar do que os tipos concretos, o que significa que o código dependente tem menos probabilidade de quebrar. 2- tipos diferentes de concreto podem ser usados em tempo de execução, porque todos se encaixam na abstração. Novos tipos de concreto também podem ser adicionados posteriormente, sem a necessidade de alterar o código dependente existente.
Por exemplo, considere uma classe Car
e duas subclasses Volvo
e Mazda
.
Se o seu código depende de a Car
, ele pode usar a Volvo
ou a Mazda
durante o tempo de execução. Mais tarde, subclasses adicionais podem ser adicionadas sem a necessidade de alterar o código dependente.
Além disso, Car
- que é uma abstração - é menos provável que mude do que Volvo
ou Mazda
. Os carros geralmente são os mesmos há algum tempo, mas Volvos e Mazdas são muito mais propensos a mudar. Ou seja, as abstrações são mais estáveis que os tipos concretos.
Tudo isso foi para mostrar que eu entendo o que é o acoplamento solto e como ele é alcançado, dependendo das abstrações e não das concreções. (Se eu escrevi algo impreciso, diga isso).
O que eu não entendo é o seguinte:
Abstrações podem ser superclasses ou interfaces.
Em caso afirmativo, por que as interfaces são elogiadas especificamente por sua capacidade de permitir acoplamentos soltos? Não vejo como é diferente do que usar uma superclasse.
As únicas diferenças que vejo são: 1- As interfaces não são limitadas por herança única, mas isso não tem muito a ver com o tópico de acoplamento flexível. 2- As interfaces são mais "abstratas", pois não possuem lógica de implementação. Ainda assim, não vejo por que isso faz uma diferença tão grande.
Por favor, explique-me por que as interfaces são ótimas para permitir acoplamentos soltos, enquanto superclasses simples não são.
fonte
interfaces are essential for single-inheritance languages like Java and C# because that's the only way in which you can aggregate different behaviors into a single class
(o que me leva à comparação com C ++, onde interfaces são apenas classes com funções virtuais puras).Respostas:
Terminologia: vou me referir à construção da linguagem
interface
como interface e à interface de um tipo ou objeto como superfície (por falta de um termo melhor).Corrigir.
Não está correto. Os idiomas atuais geralmente não antecipam que uma abstração seja alterada (embora existam alguns padrões de design para lidar com isso). Separar detalhes de coisas gerais é abstração. Isso geralmente é feito por alguma camada de abstração . Essa camada pode ser alterada para outras especificidades sem quebrar o código que se baseia nessa abstração - o acoplamento flexível é alcançado. Exemplo fora da OOP: Uma
sort
rotina pode ser alterada do Quicksort na versão 1 para Tim Sort na versão 2. O código que depende apenas do resultado que está sendo classificado (ou seja, é baseado nasort
abstração) é, portanto, desacoplado da implementação de classificação real.O que chamei de superfície acima é a parte geral de uma abstração. Agora acontece no OOP que um objeto às vezes deve suportar várias abstrações. Um exemplo não muito ideal: o Java's
java.util.LinkedList
suporta tanto oList
interface que trata da abstração "coleção ordenada e indexável" quanto aQueue
interface que (em termos gerais) trata da abstração "FIFO".Como um objeto pode suportar múltiplas abstrações?
O C ++ não possui interfaces, mas possui várias heranças, métodos virtuais e classes abstratas. Uma abstração pode então ser definida como uma classe abstrata (ou seja, uma classe que não pode ser instanciada imediatamente) que declara, mas não define métodos virtuais. Classes que implementam as especificidades de uma abstração podem herdar dessa classe abstrata e implementar os métodos virtuais necessários.
O problema aqui é que a herança múltipla pode levar ao problema do diamante , onde a ordem na qual as classes são pesquisadas para uma implementação de método (MRO: ordem de resolução do método) pode levar a "contradições". Existem duas respostas para isso:
Defina uma ordem sensata e rejeite as ordens que não podem ser sensivelmente linearizadas. O C3 MRO é bastante sensível e funciona bem. Foi publicado em 1996.
Siga o caminho mais fácil e rejeite a herança múltipla.
Java pegou a última opção e escolheu uma herança comportamental única. No entanto, ainda precisamos da capacidade de um objeto para suportar múltiplas abstrações. Portanto, é necessário usar interfaces que não suportam definições de métodos, apenas declarações.
O resultado é que o MRO é óbvio (basta olhar para cada superclasse em ordem) e que nosso objeto pode ter várias superfícies para qualquer número de abstrações.
Isso acaba sendo bastante insatisfatório, porque muitas vezes um pouco de comportamento faz parte da superfície. Considere uma
Comparable
interface:Isso é muito fácil de usar (uma API agradável com muitos métodos convenientes), mas tedioso de implementar. Gostaríamos que a interface incluísse apenas
cmp
e implementasse os outros métodos automaticamente em termos desse método necessário. Mixins , mas mais importante Características [ 1 ], [ 2 ] resolvem esse problema sem cair nas armadilhas da herança múltipla.Isso é feito definindo uma composição de características para que elas não participem do MRO - em vez disso, os métodos definidos são compostos na classe de implementação.
A
Comparable
interface pode ser expressa em Scala comoQuando uma classe usa essa característica, os outros métodos são adicionados à definição da classe:
Então
Inty(4) cmp Inty(6)
seria-2
eInty(4) lt Inty(6)
seriatrue
.Muitos idiomas têm algum suporte para características, e qualquer linguagem que tenha um "Metaobject Protocol (MOP)" pode ter características adicionadas a ele. A atualização recente do Java 8 adicionou métodos padrão que são semelhantes aos traços (os métodos nas interfaces podem ter implementações de fallback, sendo opcional para a implementação de classes implementar esses métodos).
Infelizmente, os traços são uma invenção bastante recente (2002) e, portanto, são bastante raros nas grandes línguas tradicionais.
fonte
Primeiro, subtipagem e abstração são duas coisas diferentes. Subtipagem significa apenas que eu posso substituir valores de um tipo por valores de outro tipo - nenhum dos tipos precisa ser abstrato.
Mais importante, as subclasses dependem diretamente dos detalhes de implementação de sua superclasse. Esse é o tipo mais forte de acoplamento que existe. De fato, se a classe base não for projetada com herança em mente, alterações na classe base que não mudam seu comportamento ainda podem quebrar subclasses, e não há como saber a priori se ocorrerá uma quebra. Isso é conhecido como o frágil problema da classe base .
A implementação de uma interface não o acopla a nada, exceto a própria interface, que não contém comportamento.
fonte
you want an object named A to depend on an abstraction named B instead of a concrete implementation of that abstraction named C
que está assumindo que as aulas não são abstratas. Uma abstração é qualquer coisa que oculte detalhes da implementação; portanto, uma classe com campos privados é tão abstrata quanto uma interface com os mesmos métodos públicos.Há um acoplamento entre as classes pai e filho, uma vez que o filho depende do pai.
Digamos que temos uma classe A e a classe B herda dela. Se formos para a classe A e mudarmos as coisas, a classe B também será alterada.
Digamos que temos uma interface I e a classe B a implementa. Se mudarmos a interface I, embora a classe B não possa mais implementá-la, a classe B permanece inalterada.
fonte