Ola,
De que forma poderíamos ter um exemplo sobre a afirmativa abaixo para melhor entendimento?
Com composições e interfaces teremos mais flexibilidade com nosso código, já que não nos prenderemos ao acoplamento que a herança propõe.
Obrigado
Ola,
De que forma poderíamos ter um exemplo sobre a afirmativa abaixo para melhor entendimento?
Com composições e interfaces teremos mais flexibilidade com nosso código, já que não nos prenderemos ao acoplamento que a herança propõe.
Obrigado
Olá John, tudo bem?
Vamos imaginar que temos uma classe Pagamento
, essa classe tem alguns atributos, como valor, e um método que realiza o pagamento:
public class Pagamento {
protected BigDecimal valor;
public void realizaPagamento() {
// implementação do método
}
// construtor e outros métodos omitidos
}
Podemos ter vários tipos de pagamentos, por cartão de crédito, débito, boleto, entre outros. Seguindo essa ideia, podemos ter uma classe para cada tipo de pagamento que herda da classe Pagamento
:
public class PagamentoCartaoDeCredito extends Pagamento {
@Override
public void realizaPagamento() {
// implementação do método por cartão de crédito
}
// construtor e outros métodos omitidos
}
Como o atributo valor
está como protected
, as classes filhas podem visualizá-lo. Mas em um momento do código, mudamos seu encapsulamento para private
. O que vai acontecer com as classes filhas?
Essas classes podem utilizar esse atributo, mas agora como mudamos seu encapsulamento, elas não tem mais acesso a ele. Ou seja, se alterarmos a classe mãe, talvez, teremos que alterar as classes filhas para evitar efeitos colaterais.
Agora utilizando composição, poderíamos ter uma interface MetodoDePagamento
com um único método pagar
:
public interface MetodoDePagamento {
void pagar(BigDecimal valor);
}
Agora, cada meio de pagamento, cartão, boleto, basta implementar essa interface. Podemos utilizar o polimorfismo para ter essa interface como um atributo e receber as diversas implementações dela.
Se alguma dessas implementações mudar, não tem problema, pois estamos utilizando a interface, não estamos amarrados a implementação.
Vamos explicar por conceito, que tal?
Uma interface nada mais é que uma forma de dizer "é um(a)..."
vamos ver a implementacao da ArrayList:
ela usa as interfaces Serializable
, Cloneable
, Iterable<E>
, Collection<E>
, List<E>
, RandomAccess
.
Ou seja: na hora de criarmos uma ArrayList eu posso chamar ela de qualquer um dessas interfaces.
Então uma ArrayList é uma... List
, Iterable
, Collection
...
logo, eu posso fazer...
List<String> lista = new ArrayList<>();
E mudar depois para
List<String> lista = new LinkedList<>();
Sem gerar erros de compilação (neste ponto) porque LinkedList
implementa List
. Ambas são listas. Isso é a composição!
Então eu posso ler o código que invoca o objeto partir de uma interface como "Tem uma..."
olha só:
List<String> lista = new ArrayList<>();
Uma lista de String tem uma... ArrayList.
List<String> lista = new LinkedList<>();
Uma lista de String tem uma... LinkedList.
Um ponto a ser reforçado: Ao usar o tipo da interface implementada, você só terá acesso aos métodos declarados daquela interface.
Onde então estaria a segurança? O baixo acoplamento diz que eu posso mudar a qualquer momento a implementação de arraylist para linkedlist sem quebrar o código. Com herança, isso fica impossível, já que ao herdar os métodos da classe mãe, você também estará preso a sua implementação. Se você sobrescrever um método da classe mãe, e então ele futuramente é removido, quando o projeto for atualizado ele quebra.
Um exemplo de mal uso de herança ao invés de interface é a classe Stack
do Java, que herda de Vector
.
Ola pessoal, obrigado pelas orientações.
@Gabriel, gostaria que vc me desse um exemplo deste trecho, pois achei interessante e entendo que tanto Superclasse de Herança quanto de Interface precisa ter seus metodos declarados. Ou estou errado?
E, se eu tiver herança e quiser ter metodos únicos nas subclasses, ou seja, que não existam na superclasse, seria possível?
... Com herança, isso fica impossível, já que ao herdar os métodos da classe mãe, você também estará preso a sua implementação. Se você sobrescrever um método da classe mãe, e então ele futuramente é removido, quando o projeto for atualizado ele quebra.
Obrigado
John, ao usarmos a anotação @Override
avisamos ao compilador para olhar a classe mãe ou a interface por aquele método. Uma vez que o método não exista mais ou a assinatura do seu método é diferente do da classe mãe, o compilador te traz a mensagem:
The method metodo(String, int) of type B must override a superclass method