19 de outubro de 2016



Herança e composição são duas formas possíveis que uma classe possui para construir a estrutura de seus elementos a partir de outra classe. No uso da herança, uma classe filha herda todos os atributos e métodos de uma superclasse. O uso da herança é ilustrado na Figura 1:

 FIG. 1 Exemplo de herança. Fonte: Medeiros.

Na implementação da Figura 1, a classe Animal é a classe generalizada ou superclasse e pode conter atributos como raca e peso. Os métodos da classe Animal poderiam ser locomover e comer. A classe Cachorro, por sua vez, é uma classe especializada ou subclasse e herda todos os atributos e métodos da classe Animal. Essa classe especializada deve realizar todas as atividades implementadas pela classe Animal e direcioná-las de maneira específica para um cachorro. Da mesma forma ocorre com a outra classe especializada Gato. Esse tipo de implementação realiza a relação que chamamos de é-um, podemos dizer que cachorro é um animal e o gato também.

A implementação da Figura 1:

public class Animal { … }
public class Cachorro extends Animal { … }
public class Gato extends Animal { … }

Na implementação de uma composição em vez de existir uma relação é-um, existe a relação tem-um. Nesse tipo de implementação, um componente é incluído na classe compositora que fará reúso da classe que modelou o objeto a ser utilizado. Os métodos reutilizados serão acionados a partir desse componente. O uso da composição é ilustrado na Figura 2:

FIG. 2 Exemplo de composição. Fonte: Medeiros

Na implementação da Figura 2, Sistema, que é a classe compositora, tem um campo do tipo Pessoa. A implementação ficaria da seguinte forma:

public class Pessoa {... }
public class Sistema { Pessoa pessoa = new Pessoa(); ... }

Tanto a herança quanto a composição podem implementar reúso caixa-preta (não mostra como são feitas as implementações e protege a encapsulação dos dados). Esse reúso é obtido com o uso do modificador de acesso private. Composição e herança também podem implementar reúso caixa-branca (mostra como são feitas as implementações e não protege a encapsulação dos dados). Esse outro tipo de reúso é obtido com o uso do modificador de acesso public e é uma prática não recomendada.

A conveniência do uso da herança ou da composição deve ser verificada caso a caso. De acordo com Coad (1999), no uso de herança a classe derivada somente deve especializar um papel (ex: papel de pessoa), uma transação (ex: venda ou troca) ou um dispositivo/entidade/coisa (ex: veículo ou casa). Quando a nova classe não é uma especialização de papel, transação ou coisa, é melhor usar composição. Devemos respeitar também a condição é-um e não “exerce um papel de”. No exemplo acima, Cachorro é um Animal. Nesse caso o uso de herança foi aplicado corretamente. Veja outro exemplo: em um sistema com os componentes Carro e Veículo, a classe Carro pode ser uma especialização da classe Veículo, porque todo carro é um tipo de veículo.

Para o uso de composição, devemos respeitar a condição tem-um e não “é um tipo especial de”. Por exemplo: um carro tem uma roda, mas, isso não significa que uma roda é um tipo de carro. Na implementação de um sistema com esses componentes, caso a classe Carro seja uma subclasse da classe Roda, ela estará ferindo o princípio necessário para a implementação de uma herança uma vez que um carro não é uma roda. O correto seria a classe Carro ter um campo do tipo Roda, implementação obtida por meio de composição.

Vamos verificar um exemplo de Bigonha (2015) que mostra um caso no qual o uso de composição seria melhor que herança. No modelo da Figura 3, o objeto Passanger é um Person, mas, em algum momento ele poderia exercer o papel Agent. Como o modelo é implementado com herança e possui a hierarquia apresentada, seria inviável realizar essa troca de papéis.

FIG. 3 Herança x Composição. Fonte: Bigonha. 

Para resolver o problema, poderíamos criar um novo papel e apresentar esse novo modelo com o uso de herança múltipla, como ilustra a Figura 4.

FIG. 4 Herança x Composição. Fonte: Bigonha.

Esse novo modelo supostamente resolveria o problema, mas, em herança as subclasses deviam expressar a relação “é um tipo especial de’’, e não a de “pode exercer o papel de”. Herança não foi a melhor solução para essa implementação. Vejamos então o uso de composição para essa mesma situação, Figura 5.

FIG. 6 Herança x Composição. Fonte: Bigonha.
 
Agora ao papel de Person pode mudar sempre que necessário e as condições podem ser satisfeitas. Poderíamos em uma nova solução, utilizar composição e herança para implementar esse caso. Verifique o modelo da implementação, ilustrado na Figura 6.

FIG. 6 Herança x Composição. Fonte: Bigonha.

No modelo da nova implementação, Passenger e Agent herdam de PersonRole que é um papel exercido por Person. Nessas condições, novos papéis podem ser criados para satisfazer os critérios necessários.

Como verificamos com a apresentação de todos os exemplos utilizados, o importante é que as implementações de herança e composição sejam aplicadas de forma coerente em cada caso. Para tal, devemos fazer uma análise conforme indicado nos exemplos acima para verificar as situações nas quais se aplicam a composição e outras nas quais será aplicável a herança.

Referências:

BIGONHA, R.S.; BIGONHA, M. Programação Modular. Notas de Aula – DCC UFMG. Belo Horizonte: 2015.

COAD, P.; MAYFIELD, M.; KERN, J. Java Design: Building Better Apps and Applets. Yourdon Press Computing Series. 2. ed. New Jersey: Prentice Hall, 1999.


0 comentários:

Postar um comentário

Comentários:

Perfil

Formada em Sistemas de Informação e pós-graduada em Engenharia de Software.

Facebook

Views