[Pesquisar este blog]

quinta-feira, 1 de março de 2018

POO::Plena-16-Associações-composição de objetos

POO-P-15-Associações POO-P-17-Associações:agregação
Um sistema OO contém vários objetos inter-relacionados, ou seja, instâncias de tipos diferentes associadas de uma maneira apropriada para produzir os resultados desejados. Assim, as diversas associações existentes entre os objetos de um sistema explicitam as relações lógicas ou de negócios que existem entre seus objetos e, exatamente por isso, devem ser adequadamente representadas.

As associações ou vínculos podem existir entre objetos diferentes, de um mesmo tipo ou de tipos diferentes [2]. Estes ligações constituem os relacionamentos específicos entre tais objetos, o que é muito importante, pois em geral efetivam um serviço ou materializam uma construção.

Por exemplo, uma biblioteca possui livros e usuários, que são entidades de tipos distintos. O empréstimo de um livro, que é o serviço prestado pela biblioteca, vincula um livro específico a um usuário particular, ou seja, cria uma associação livro-usuário.

De maneira semelhante, a construção de um avião corresponde a ligação de vários elementos, como fuselagem, asas, trens de pouso, motor, etc. Também existe uma associação necessária entre tais elementos para constituir uma aeronave.

No post anterior, que tratou das associações, foram vistas as situações gerais onde um objeto está associado a um ou mais objetos. A quantidade de objetos associados é dita cardinalidade ou multiplicidade da associação. Além disso, a maneira com que as associações são construídas permite a que navegação seja unidirecional ou bidirecional.

Também existem associações especiais, conhecidas como associações todo/parte, que relacionam as partes de um todo. Em função do tipo de relação existente entre o conjunto de todos os elementos e suas partes, existem dois tipos particulares de associações todo/parte que merecem um estudo mais detalhado porque ocorrem com muita frequência no desenvolvimento de sistemas OO. São as composições, assunto tratado a seguir; e as agregações, que serão abordadas no próximo post.

Composições

Numa composição (composite), o objeto composto não pode existir como um todo sem que todos os seus componentes estejam presentes, pois ele não estaria íntegro ou completo [2][6]. Considere, rapidamente, uma caneta esferográfica sem sua carga de tinta; uma bicicleta sem guidão; ou um telefone celular sem sua bateria; são exemplos de objetos incompletos que não podem ser utilizados, nem desempenhar seus papeis. Completando tais conjuntos, temos composições válidas.

Existe também outra questão envolvendo o tempo de vida do objeto composto (o todo) e dos objetos componentes (suas partes):
  • O tempo de vida de um objeto composto não pode ultrapassar o tempo de vida de seus componentes.
  • No entanto, o tempo de um, vários ou todos os objetos componentes (as partes) pode ser maior que a vida do objeto composto.
Considerando uma bicicleta, como um todo - completa, ela não pode existir sem uma ou duas de suas rodas, sem seu selim ou guidão. Mas as partes da bicicleta podem existir, mesmo que não montadas como uma bicicleta (ou seja, existem mesmo sem constituir um todo).

Além disso, a qualquer tempo, um objeto componente só pode fazer parte um objeto composto específico. O guidão, o selim e cada parte da bicicleta só podem estar em uma bicicleta em particular. Retirar a roda de uma bicicleta, faz que ela deixe de ser um todo; montando tal roda para completar outra bicicleta, surge outro objeto composto; mas a roda em questão é um objeto único, que só pode estar numa bicicleta ou nenhuma.

Também é muito comum que as composições sejam, geralmente, heterômeras ou heteromorfas, ou seja, objetos compostos cujas partes não são semelhantes, isto é, feitos de partes de tipos diferentes.

É comum que as composições sejam usadas para expressar o interrelacionamento sistêmico entre elementos que devem coexistir para funcionarem adequadamente como um sistema.

Num diagrama de classes UML adicionamos um diamante preenchido nas associações todo/parte do tipo composição, posicionando-o do lado do objeto composto. Por conta da própria heteromorfia, as multiplicidades de cada componente podem ser diferentes, como ilustrado a seguir.


As associações numa composição também podem ser navegáveis. Mas a escolha do desenho da navegabilidade entre o objeto composto e seus objetos componentes deve levar em conta:
  • O quão frequentemente a composição precisa ser navegada de parte a parte.
  • Se os objetos componentes serão reutilizados em outras composições (situação onde se recomenda que os componentes não façam referência ao objeto composto).
Outro aspecto bastante comum é que as mensagens enviadas ao objeto composto se propaguem para todos os seus componentes.

Exemplo de Composição

Considere as classes Java/C# que seguem, nas quais não foram incluídos quaisquer membros, exceto toString(), para simplificação do exemplo. Tais classes são os componentes de uma bicicleta.

public class Roda { 
   public String toString() {
      return getClass().getName();
   }
}
public class Quadro { 
   public String toString() {
      return getClass().getName();
   }
}
public class Selim { 
   public String toString() {
      return getClass().getName();
   }
}
public class Guidao { 
   public String toString() {
      return getClass().getName();
   }
}

A implementação da operação toString() de todos os tipos componentes da bicicleta é idêntica e produz uma String contendo o nome da classe.

Como uma bicicleta é um objeto composto, o qual só pode estar completo com a presença de todos os seus componentes, a classe Java/C# que segue representa uma possível implementação de Bicicleta.

public class Bicicleta {
   private Roda rodaDianteira, rodaTraseira;
   private Quadro quadro;
   private Selim selim;
   private Guidao guidao;

   public Bicicleta(Roda rd, Roda rt, Quadro q, Selim s, Guidao g) {
      if (rd == null || rt == null || rd == rt) {
         throw new RuntimeException("Bicicleta requer duas rodas.");
      }
      if (q == null) {
         throw new RuntimeException("Bicicleta requer um quadro.");
      }
      if (s == null) {
         throw new RuntimeException("Bicicleta requer um selim.");
      }
      if (g == null) {
         throw new RuntimeException("Bicicleta requer um guidao.");
      }
      rodaDianteira = rd; rodaTraseira = rt;
      quadro = q;
      selim = s;
      guidao = g;
   }

   public Roda getRodaDianteira() { return rodaDianteira; }
   public Roda getRodaTraseira() { return rodaTraseira; }
   public Quadro getQuadro() { return quadro; }
   public Selim getSelim() { return selim; }
   public Guidao getGuidao() { return guidao; }

   public String toString() {
      return
            String.format("%s:\n\t%s,\n\t%s,\n\t%s,\n\t%s,\n\t%s\n",
            getClass().getName(), rodaDianteira, rodaTraseira,
            quadro, selim, guidao);
   }  
}

Os membros privados rodaDianteira, rodaTraseira, quadro, selim e guidao são as associações requeridas pelo tipo composto Bicicleta. São declaradas privadas para garantir que não sejam substituídas por null, o que tornaria a composição inválida com a ausência de qualquer um de seus componentes.

O construtor toma como argumentos os componentes exigidos para a composição, validando e rejeitando componentes nulos. Apenas quando fornecidas duas instâncias distintas de Roda, uma instância de Quadro, de Selim e de Guidao, torna-se possível a instanciação de um objeto Bicicleta, como no fragmento que segue:
Bicicleta b = new Bicicleta(new Roda(), new Roda(),
      new Quadro(), new Selim(), new Guidao());
System.out.println(b);

Quando executado, este fragmento produz:
Bicicleta:
        Roda,
        Roda
        Quadro
        Selim
        Guidao

As operações de acesso getter permitem acessar e, portanto, navegar, unidirecionalmente, da instância de Bicicleta para as suas instâncias componentes.

O método toString() de Bicicleta produz uma String que concatena o nome da classe do tipo Bicicleta e, também, os nomes das classes de seus componentes, cujos métodos toString() são implicitamente acionados quando suas referências são usadas para suprir um argumento de tipo %s, que indica um tipo String. Este método mostra uma possível forma de propagação de mensagens entre o objeto composto e seus componentes.

Considerações Finais

As composições representam associações todo/parte, onde todas as partes (componentes), de tipos iguais ou diferentes, devem existir na quantidade adequada para que o todo possa ser considerado completo.

Composições são adequadas para expressar o inter-relacionamento sistêmico entre elementos, como mostrado no exemplo do tipo Bicicleta (composição) e seus componentes Roda, Quadro, Selim e Guidao.

É praticamente impossível construir sistemas onde não existam associações, sendo que as composições são frequentemente utilizadas.


POO-P-15-Associações POO-P-17-Associações:agregação

Referências Bibliográficas

[1] JAMSA, K.; KLANDER, L.. Programando em C/C++: a bíblia. São Paulo: Makron Books, 1999.
[2] PAGE_JONES, M.. Fundamentos do Desenho Orientado a Objeto com UML. São Paulo: Makron Books, 2001.
[3] SOMMERVILLE, I.. Software Engineering. 6th. Ed. Harlow: Pearson, 2001.
[4] DEITEL, H.M.; DEITEL, P.J.. Java: como programar. 6a. Ed. São Paulo: Pearson Prentice-Hall, 2005.
[5] SAVITCH, W.. C++ Absoluto. São Paulo: Pearson Addison-Wesley, 2004.
[6] JANDL JR., P. Introdução ao C++. São Paulo: Futura, 2003.
[7] JANDL JR., P.. Java - guia do programador. 3a. ed. São Paulo: Novatec, 2015.
[8] RUMBAUGH, J.; BLAHA, M.; PREMERLANI, W.; EDDY, F.; LORENSEN, W.. Object-oriented modeling and design. Englewoods Cliffs: Prentice-Hall, 1991.
[9] STROUSTRUP, B.. The C++ Programming Language. 3rd Ed. Reading: Addison-Wesley, 1997.
[10] LANGSAM, Y.; AUGENSTEIN, M. J.; TENENBAUM, A. M.. Data structures using C and C++. 2nd Ed. Upper Saddle River: Prentice-Hall, 1996.
[11] WATSON, K.; NAGEL, C.; PEDERSEN, J.H.; REID, J.D.; SKINNER, M.; WHITE, E.. Beginning Microsoft Visual C# 2008. Indianapolis: Wiley Publishing, 2008.

Nenhum comentário: