Olá pessoal!
No segundo post da série UML na Prática – Diagrama de Classe, vamos falar sobre os relacionamentos entre as entidades. Os relacionamentos básicos possíveis são as associações, generalizações e dependências.
1. Associações
As associações são relacionamentos entre as classes e podem definir seus comportamentos. Estas relações fazem com que as classes tenham conexões fisicamente ou conceitualmente, e alterações refletem na programação diretamente.
Quando construímos as associações estamos definindo os conceitos que irão definir como nosso código será estruturado e interligado. É importante saber que as associações se comportam de uma forma na teoria, mas na prática podem se comportar de outra forma se o programador não tomar cuidado para respeitar o UML.
1.1. Agregação( é parte de , todo/parte , consiste em, contém, é contida)
Tipo de associação onde um objeto faz parte de outro objeto maior( o todo ), sendo que esta parte não é totalmente dependente do todo, isto é, a parte sobrevive sem a existência do todo. Na literatura você pode encontrar tipos de agregação, mas estes tipos mantém a idéia básica, eles apenas especificam comportamentos especiais/específicos.
Na agregação o todo pode ser uma classe que representa um objeto composto por outros objetos, como uma vila pode ser composta de construções.

Agregação
#include<iostream>
#include<Modelo.h>
...
void Vila::adicionar(Construcao* obj) {
...
construcoes.add(obj);
...
}
...
1.2. Composição(está contida, faz parte, pertence)
Este relacionamento é similar ao anterior, só que mais forte. Nesse caso, a parte só existe e faz sentido com o todo. A parte é criada e destruida com o todo, como por exemplo um objeto 3D pode ser formado por um conjunto de Nós. Cada nó, pertencente ao objeto, só existe se o objeto existir.

Composição
#include<iostream>
#include<Modelo.h>
...
void Modelo::carregar() {
...
No* no = new No();
...
nodos.add(no);
...
}
...
2. Generalizações
as generalizações são formas de permitir que certos objetos sejam mais específicos. No caso, podemos ter uma classe Objeto que possuí os métodos básicos de um objeto da cena, mas podemos criar um objeto mais específico como ObjetoVoador onde podemos definir novas características.
Desta forma é possível manter compatibilidade na adição de novas classes mais especializadas e permite um controle maior do universo, como no exemplo anterior onde poderíamos definir limites diferentes de deslocamento para objetos voadores.

Generalização
#include<iostream>
#include<Modelo.h>
...
class Objeto {
...
};
class ObjetoAquatico: public Objeto {
...
};
class ObjetoAereo: public Objeto {
...
};
class ObjetoTerrestre: public Objeto {
...
};
...
As generalizações são as famosas heranças, podendo ser simples ou composta. A herança simples acontece quando um objeto herda características de apenas um outro objeto, já na composta as características são herdadas de dois ou mais objetos.
3. Dependências
As dependências são relacionamentos utilizados para informar e alertar o desenvolvedor. Esse tipo de relação alerta que a alteração de uma classe ou módulo pode afetar o funcionamento de outra. Ela informa porque diz ao desenvolvedor que uma classe faz uso de alguns serviços fornecidos por outra, deixando claro a necessidade de algum teste da existência da classe que fornece os serviços.

Dependência
No caso acima o pacote Gráfico utiliza serviços do pacote Utils, estes serviços podem ser manipulação de arquivos, plugins e etc. Caso o pacote Utils não exista, o pacote Gráfico não poderá funcionar 100%, nesse caso seu funcionamento pode ficar comprometido ou limitado. O tratamento de erros e validações pode variar conforme o caso, de um simples if até simplesmente o não funcionamento do pacote.
Para exemplificar melhor, vamos supor que o pacote Gráfico utilize o pacote Utils para fazer leitura de arquivos dos objetos 3D. Caso o pacote Utils não exista pode acontecer os seguintes casos:
- Os objetos 3D nos arquivos não são carregados e apenas objetos já em formato de código são carregados, o programa continua sendo executado.
- É exibido um erro e o programa não continua sua execução.
Perceba que no caso acima a falta do pacote Utils pode gerar dois resultados, onde o programa continua sua execução no primeiro caso ou termina sua execução no segundo. Por este motivo este tipo de relacionamento deve ser especificado durante o planejamento de sua arquitetura, o que acontece quando a relação de dependência não é satisfeita? Essa pergunta deve ser respondida antes da implementação.

