Relacionamentos entre Classes
Projeto de Sistemas
Relacionamentos entre classes
• Para o iniciante, a gama muito variada de possíveis opções de relacionamentos entre classes costuma mais confundir do que ajudar
• Classificação: mecanismo primário para compreender o mundo a nosso redor
• Herança: adiciona ao encapsulamento a habilidade de expressar relacionamentos entre classes
• Relacionamento entre superconjunto e subconjunto (em POO: superclasse e subclasse)
• Superclasses e subclasses podem formar hierarquias
Tipos de relacionamento de herança
• Extensão: novas classes preservam a classe original e adicionam membros
• Especificação: responsabilidades são atribuídas a uma classe, a qual deve implementá-las
• Herança polimórfica: combinação de extensão e especificação
Relacionamento de extensão
• uma subclasse extende uma superclasse
• todos os membros (atributos e métodos) da superclasse são preservados
• novos atributos ou métodos são adicionados • Também chamada de subtipo estrito
• Herança da interface (ou métodos públicos) da superclasse e também da implementação
Relacionamento de especificação
• Uma superclasse especifica um conjunto de responsabilidades
• A subclasse deve cumprir estas responsabilidades
• A superclasse não implementa nada, este trabalho é deixado para a subclasse
• Herança apenas da interface (declaração dos métodos públicos)
Herança polimórfica
• Combinação entre extensão e especificação • A subclasse herda:
– a interface da superclasse
– uma implementação default ou, pelo menos, de alguns dos métodos da superclasse
• A subclasse pode reescrever (sobrepor, sobrescrever, override) um ou mais métodos herdados para prover comportamento
específico
• Pode-se exigir que a subclasse providencie a implementação de um método particular
• Herança polimórfica: prover comportamento especializado em resposta às mesmas mensagens
Descobrindo relacionamentos de
herança
• Evolução da hierarquia de herança
– Processo iterativo de especialização top-down e generalização bottom-up
• Partindo do problema, você pode descobrir diferentes situações para o uso de herança
– Especialização – Subtipo estrito – Generalização – Especificação – Especificação pura – Especificação e especialização
Especialização
• Associação é-um-tipo-de ou é-um (is-a). A subclasse é um tipo especial da classe pai
• A subclasse pode herdar dados e métodos da superclasse
• Ex.: Uma MaquinaDeVenderCafé é uma forma especializada de uma MaquinaDeVender
– Atributos e métodos de MaquinaDeVender são herdados na classe MaquinaDeVenderCafé
– Novos atributos e métodos são adicionados na classe MaquinaDeVenderCafé
Substituibilidade e Subtipos
• Uma classe é um subtipo se atende ao princípio de substituibilidade de Liskov
• O princípio faz o seguinte teste:
– Um objeto da subclasse mais especializada pode ser usado em qualquer lugar em que um objeto da
superclasse seria usado?
• O princípio é semântico
– Embora a linguagem OO não proíba alteração do
significado dos métodos reescritos, o princípio de Liskov não o permite
Exemplo que não atende o princípio
de Liskov
public class SuperClass {
public int add(int a, int b) { return a + b; }
public int subtract(int a, int b) { return a - b; } }
public class SubClass extends SuperClass {
public int add(int a, int b) { return a - b; }
public int subtract(int a, int b) { return a + b; } }
Subtipos estritos
• Um subtipo é estrito quando:
– atende ao princípio de Liskov (semântica)
– preserva inalterados todos os métodos públicos da superclasse
• Um subtipo estrito pode, por outro lado, adicionar novos atributos e métodos
Generalização
• Numa hierarquia de classe, pode-se notar que certas classes têm dados e métodos em comum
– Esta situação abre possibilidades de generalização
• Generalização (bottom-up) é o espelho da especialização (top-down)
– A diferença está no modo como o relacionamento foi descoberto
• Especialização e generalização permitem compartilhar interface e implementação
Especificação
• Em alguns casos, o programador sabe o que uma classe deve fazer, mas não sabe como ela deve fazê-lo
• Especifique o que a subclasse deve fazer e deixe o compilador cuidar que cada subclasse o faça • Especificação é também chamada de herança
de interface, pois apenas a interface pública da superclasse é herdada
Especificação pura
• Útil quando se quer um comportamento comum para classes não-relacionadas
• A subclasse não herda nenhuma implementação de método e nenhum campo variável
• Classes que implementam uma especificação pura não precisam ser substituíveis no sentido de Liskov, exceto em relação à interface que implementam
• Em Java, usa-se o mecanismo de interface para implementar especificações puras
Especialização e Especificação
• Às vezes, é necessário:
– implementar algum comportamento padrão para ser usado pelas subclasses
– especificar um outro comportamento que uma subclasse deve realizar por conta própria
• A solução é a combinação da especialização com a especificação
Contração (herança de implementação)
• Consiste em:
– Aproveitar algum comportamento existente na superclasse
– Reescrever todos os outros métodos que não interessam como métodos vazios
• Herança que se deve evitar, por três motivos:
– Viola o princípio da substituibilidade
– Dá mais trabalho que fazer a coisa certa. Simplesmente, use composição que se resolve o problema.
Equívocos da contração
• Exemplo 1:– Você tem uma classe Cadillac, que possui um sistema de som estéreo – Você resolve criar um novo sistema de som, SoundSystem, e usa o
Cadillac como base
– Você substitui os métodos dirigir() e acelerar() por métodos vazios – A composição de SoundSystem com um Cadillac dentro pode ser
esdrúxulo, mas é melhor que a contração
• Exemplo 2:
– Todos os pássaros podem voar. – Pinguins são pássaros.
Mecanismos de herança em Java
• Herança com extensão usando extends • Herança com especificação usando
implements
Mecanismo de extensão em Java
(
extends)
• Use a palavra-chave extends para criar uma subclasse
– Atributos e métodos serão herdados
– Novos atributos e métodos podem ser adicionados
• Uma extensão simples supõe que:
– mais funcionalidade pode ser adicionada
– a implementação existente deve permanecer invariante
Extensão simples
• Deseja-se estender a classe existente, mas sem modificar a implementação da classe base
• Métodos herdados não podem ser modificados • use a palavra-chave final antes de todos os
métodos públicos da classe-mãe
• Extensão simples é a herança de uma interface obrigatória bem como de uma implementação obrigatória
Mecanismo de especificação em
Java
(
implements)
• Especificações puras em Java são realizadas através do
mecanismo interface/implements
• Interfaces definem os métodos que devem ser implementados (todos) pela subclasse
• Se algum método na interface não é implementado, o compilador percebe esta omissão e aponta um erro • Quaisquer atributos numa interface são
implicitamente public final, ou seja, constantes
Mecanismo de especificação em
Java
(
implements)
• Interfaces promovem herança de uma interface obrigatória sem nenhuma implementação
• Em Java, uma classe pode implementar várias interfaces
– Este atribuito serve para simular herança múltipla
• Classes que implementam uma interface não
precisam ser substituíveis no sentido de Liskov, exceto em relação à interface que implementam
– Ex.: Uma classe que implementa a interface Runnable é-um objeto Runnable e pode ser usada em qualquer lugar onde um objeto
Herança polimórfica em Java
• Polimorfismo: prover comportamento
especializado em resposta às mesmas mensagens • Combinação de extensão e especificação
• Tipo de herança mais comum • Herda-se, neste caso:
– interface (assinatura dos métodos públicos) da superclasse
– implementação default de, pelo menos, alguns dos métodos da superclasse
Herança polimórfica em Java
• Usam-se as palavras-chave final,
abstract e extends para definir que partes da classe estendida...
– ... não podem ser modificadas (interface e implementação obrigatórias)
– ... devem ser modificadas (interface obrigatória sem implementação default)
– ... podem ser modificadas (interface obrigatória e implementação opcional)
... não podem ser modificadas (interface obrigatória e implementação obrigatória)
• Alguns métodos podem ser invariantes, como na extensão simples
• Estes métodos devem ser marcados como final
... devem ser modificadas (interface
obrigatória sem implementação default)
• Alguns métodos podem ser especificados na
superclasse embora não tenha sua implementação definida lá
• Estes métodos devem ser começar com a palavra-chave abstract na superclasse e terminar com um ponto-e-vírgula (sem corpo)
• Na subclasse, o método abstrato deve, obrigatoriamente, ser implementado
... podem ser modificadas (interface obrigatória e implementação opcional)
• Alguns métodos da superclasse podem ser
sobrescritos (sobrepostos, overriden), permitindo às subclasses se comportarem de maneira especializada • Estes métodos não devem ser marcados como
final, para permitir que as subclasses os redefinam
• A subclasse não precisa redefinir estes métodos, trata-se de uma implementação opcional
Uma checklist de herança
• Que mecanismo usar?
– Classes finais – Métodos finais
– Métodos abstratos – Métodos sobrescritos
– Classes puramente abstratas – Interfaces
Use classes finais quando...
• ... sua classe não deve ser estendida
– Impede-se a extensão marcando a classe inteira como final
– Se a classe não é final, você está dizendo que ela pode ser estendida
Use métodos finais quando...
• ... você quer modelar um relacionamento de subtipo estrito entre duas classes
– Marcar um método como final faz o método ser invariante para todas as subclasses
– Se o método não é final, é porque você quer o contrário: que o comportamento possa ser variável para alguma(s) subclasse(s)
• ... Um método realiza operações de um modo que deve ser invariante mesmo quando novas classes são criadas
Use métodos abstratos quando...
• ... todos os objetos das subclasses devemrealizar uma operação, mas a superclasse não tem como prover uma implementação default • ... você quer escrever código em termos da
superclasse, mas, de fato, vai usar objetos de subclasse
• ... você quer forçar uma subclasse a
implementar um certo comportamento, (e.g., inicialização)
Use métodos sobrescritos quando...
• ... uma superclasse pode definir um razoável comportamento default para um método
• ... mais do que uma subclasse pode usar o comportamento default
– Se apenas uma subclasse usa o comportamento default, o método da superclasse deveria ser abstrato e a subclasse o implementaria
• ... objetos de subclasse devem realizar a mesma operação de diferentes modos, embora façam uso de algum comportamento comum para fazê-la
– Se mais de uma subclasse usa a operação default e pelo menos uma faz diferente, use um método sobrescrito
– Se todas as subclasses precisam adicionar algo ao método default, coloque o código comum num método helper protegido e faça o método a ser
Use classes puramente abstratas quando...
• ... você pode definir um membro de dadoscomum na superclasse, ainda que não possa implementar os métodos
• ... você está projetando uma hierarquia de classes que pretende modelar classes
Use interfaces quando...
• ... você tiver classes que têm umcomportamento comum, mas não são relacionadas de outra maneira
• ... não existe implementação comum a ser
herdada, mas você quer impor uma interface obrigatória
• ... você precisa combinar vários