Composição, agregação e associação em Java

Composição, agregação e associação em Java

1. Introdução

Os objetos têm relações entre eles, tanto na vida real quanto na programação. Às vezes, é difícil entender ou implementar essas relações.

Neste tutorial, vamos nos concentrar na abordagem do Java em três tipos de relacionamento às vezes facilmente confundidos: composição, agregação e associação.

2. Composição

Composition é um tipo de relacionamento “pertence a”. Isso significa que um dos objetos é uma estrutura logicamente maior, que contém o outro objeto. Em outras palavras, é parte ou membro do outro objeto.

Alternativamente,we often call it a “has-a” relationship (em oposição a um relacionamento “é um”, que éinheritance).

Por exemplo, uma sala pertence a um prédio ou, em outras palavras, um prédio tem uma sala. Então, basicamente, se nós o chamamos de "pertence a" ou "tem-a" é apenas uma questão de ponto de vista.

A composição é um tipo forte de relacionamento "tem um", porque o objeto que o contém é o proprietário. Portanto,the objects' lifecycles are tied. It means that if we destroy the owner object, its members also will be destroyed with it. Por exemplo, a sala é destruída com a construção em nosso exemplo anterior.

Observe que isso não significa que o objeto que o contém não pode existir sem nenhuma de suas partes. Por exemplo, podemos derrubar todas as paredes dentro de um prédio e, portanto, destruir as salas. Mas o prédio ainda existirá.

Em termos de cardinalidade, um objeto que contém pode ter quantas partes quisermos. No entanto,all of the parts need to have exactly one container.

2.1. UML

Na UML, indicamos composição com o seguinte símbolo:

image

Observe que o diamante está no objeto que a contém e é a base da linha, não uma ponta de seta. Por uma questão de clareza, também costumamos desenhar a ponta da seta:

image

Portanto, podemos usar essa construção UML para o exemplo da sala de construção:

image

2.2. Código fonte

Em Java, podemos modelar isso com uma classe interna não estática:

class Building {
    class Room {}
}

Como alternativa, também podemos declarar essa classe no corpo de um método. Não importa se é uma classe nomeada, uma classe anônima ou um lambda:

class Building {
    Room createAnonymousRoom() {
        return new Room() {
            @Override
            void doInRoom() {}
        };
    }

    Room createInlineRoom() {
        class InlineRoom implements Room {
            @Override
            void doInRoom() {}
        }
        return new InlineRoom();
    }

    Room createLambdaRoom() {
        return () -> {};
    }

    interface Room {
        void doInRoom();
    }
}

Observe que é essencial que nossa classe interna seja não estática, uma vez que vincula todas as suas instâncias à classe contida.

Geralmente, o objeto que contém deseja acessar seus membros. Portanto, devemos armazenar suas referências:

class Building {
    List rooms;
    class Room {}
}

Observe que todos os objetos da classe interna armazenam uma referência implícita ao objeto que os contém. Como resultado, não precisamos armazená-lo manualmente para acessá-lo:

class Building {
    String address;

    class Room {
        String getBuildingAddress() {
            return Building.this.address;
        }
    }
}

3. Agregação

A agregação também é um relacionamento "tem um". O que o distingue da composição, que não envolve possuir. Como resultado, os ciclos de vida dos objetos não são amarrados: cada um deles pode existir independentemente um do outro.

Por exemplo, um carro e suas rodas. We can take off the wheels, and they’ll still exist. Podemos montar outras rodas (preexistentes) ou instalá-las em outro carro e tudo funcionará perfeitamente.

Claro, um carro sem rodas ou com uma roda destacada não será tão útil quanto um carro com as rodas colocadas. Mas é por isso que essa relação existiu em primeiro lugar: paraassemble the parts to a bigger construct, which is capable of more things than its parts.

Como a agregação não envolve propriedade,a member doesn’t need to be tied to only one container. Por exemplo, um triângulo é feito de segmentos. Mas triângulos podem compartilhar segmentos como seus lados.

3.1. UML

A agregação é muito semelhante à composição. A única diferença lógica é que a agregação é um relacionamento mais fraco.

Portanto, as representações UML também são muito semelhantes. A única diferença é que o diamante está vazio:

image

Para carros e rodas, então, faríamos:

image

3.2. Código fonte

Em Java, podemos modelar a agregação com uma referência antiga simples:

class Wheel {}

class Car {
    List wheels;
}

O membro pode ser qualquer tipo de classe, exceto uma classe interna não estática.

No trecho de código acima, ambas as classes têm seu arquivo de origem separado. No entanto, também podemos usar uma classe interna estática:

class Car {
    List wheels;
    static class Wheel {}
}

Observe que Java criará uma referência implícita apenas em classes internas não estáticas. Por isso, temos que manter o relacionamento manualmente onde for necessário:

class Wheel {
    Car car;
}

class Car {
    List wheels;
}

4. Associação

Associação é o relacionamento mais fraco entre os três. It isn’t a “has-a” relationship, nenhum dos objetos são partes ou membros de outro.

Association only means that the objects “know” each other. Por exemplo, uma mãe e seu filho.

4.1. UML

Na UML, podemos marcar uma associação com uma seta:

image

Se a associação for bidirecional, podemos usar duas setas, uma seta com uma ponta de seta nas duas extremidades ou uma linha sem nenhuma ponta de seta:

image

Podemos representar uma mãe e seu filho na UML, então:

image

4.2. Código fonte

Em Java, podemos modelar a associação da mesma maneira que a agregação:

class Child {}

class Mother {
    List children;
}

Mas espere,how can we tell if a reference means aggregation or association?

Well, we can’t. A diferença é apenas lógica: se um dos objetos é parte do outro ou não.

Além disso, temos que manter as referências manualmente nas duas extremidades, como fizemos com a agregação:

class Child {
    Mother mother;
}

class Mother {
    List children;
}

5. Sidenote UML

Por uma questão de clareza, às vezes queremos definir a cardinalidade de um relacionamento em um diagrama UML. Podemos fazer isso escrevendo-o nas extremidades da seta:

image

Observe que não faz sentido escrever zero como cardinalidade, porque significa que não há relacionamento. A única exceção é quando queremos usar um intervalo para indicar um relacionamento opcional:

image

Observe também que, uma vez que na composição há exatamente um proprietário, não o indicamos nos diagramas.

6. Um exemplo complexo

Vamos ver um exemplo (um pouco) mais complexo!

Vamos modelar uma universidade, que tem seus departamentos. Os professores trabalham em cada departamento, que também tem amigos entre si.

Os departamentos existirão depois que fecharmos a universidade? Claro que não, portanto, é uma composição.

Mas os professores ainda existirão (espero). Temos que decidir o que é mais lógico: se considerarmos os professores como partes dos departamentos ou não. Alternativamente: eles são membros dos departamentos ou não? Sim, eles estão. Portanto, é uma agregação. Além disso, um professor pode trabalhar em vários departamentos.

A relação docente é associativa, porque não faz sentido dizer que um professor faz parte de outro.

Como resultado, podemos modelar este exemplo com o seguinte diagrama UML:

image

E o código Java fica assim:

class University {
    List department;
}

class Department {
    List professors;
}

class Professor {
    List department;
    List friends;
}

Observe que, se nósrely on the terms “has-a”, “belongs-to”, “member-of”, “part-of”, e assim por diante, podemos identificar mais facilmente as relações entre nossos objetos.

7. Conclusão

Neste artigo, vimos as propriedades e a representação da composição, agregação e associação. Também vimos como modelar esses relacionamentos em UML e Java.

Como de costume, os exemplos estão disponíveisover on GitHub.