← Back to POO/1. Composição, Agregação, Classes

Composição

Situação em que uma classe seja composta por outros objectos e:

  • faça a gestão do ciclo de vida dos mesmos
  • faça a criação dos objectos internamente
  • não os receba por parâmetros já criados

2025-03-19_01:46:03.png

public class Turma {
	private String designação;
	private Aluno[] alunos; // Composição
	private int capacidade;

	//variaveis internas para controlo do numero de alunos
	private int ocupacao;

	//se não for especificado o tamanho da turma usa-se esta constante
	private static final int capacidade_inicial = 20;

}
  • getAlunos é declarado como auxiliar e privado

/**
* Método privado(auxiliar)
* possível problema de encapsulamento ao partilhar o endereço do array
* 
* @return Array com os objectos do tipo Aluno
*/
prrivate Aluno[] getAlunos(){
	return this.alunos
}

  • Quem cria a instância de Aluno é a classe Turma

/**
* Este método assume que se vreifica perviamente se ainda existe espaço para
* mais um aluno na turma.
* Em futuras versões desta classe poderemos fazer internamente a gestão das 
* situações de erro. Neste momento assume-se que a pré condição é verdadeira. 
*/
public void insereAluno(String numer, int nota, String nome, string cruso){
	this.alunos[this.ocupaçao] = new Aluno(numero, nota, nome, curso); 
	this.ocupacao++;
}

/**
* Método que insere um aluno na turma, mas recebe já uma isntãncia da classe
* Aluno 
* Como forma de garantir o encapsulamento cria-se uma cópia do objecto recebido
* 
*/
public void insereAluno(Aluno umAluno){
	this.alunos[this.ocupaçao] = new Aluno(umAluno); 
	this.ocupacao++;
}

  • Como foi decidido, na fase de concepção, que a estratégia de associação previa uma composição então é necessário explicitamente clonar o objecto.

2025-03-18_21:45:50.png

Clone

  • Criação de uma cópia do objecto a quem é enviado
  • x.clone() != x
  • x.clone().getClass() == x.getClass()
  • A utilização de clone() permite que seja possível preservarmos o encapsulamento dos objectos, desde que:
    ->seja feita uma cópia dos objectos à entrada dos métodos
    ->seja devolvida uma cópia dos objectos e não o apontador para os mesmos

/**
* Implementação do método de cloangem de um Aluno
*
* @return objecto de tipo Aluno
*/
public ALuno clone(){
	return Aluno(this);
}

  • Duas abordagens:
    -> Shallow Clone: cópia parcial que deixa endereços partilhados (cria as estruturas de dados mas partilha os conteúdos)
    -> Deep Clone: cópia em que nenhum objecto partilha endereços com outro

Objetos Imutáveis que não precisam ser clonados: -> String -> Integer -> Float -> (...)

  • O método clone() existente nas classes Java é sempre shallow, e devolve sempre um Object (se usado, é necessário fazer cast) consistente

Equals

  • alunos[i] == a -> compara apontadores se a clone de alunos[i] então dá false
  • (alunos[i]).getNumero() == a.getNumero() -> assume demasiado sobre como se comparam alunos.
  • Dois objectos são iguais se forem o mesmo, se tiverem o mesmo apontador ou as mesmas variáveis de instância.

Regras para usar equals: -> reflexivo -> x.equals(x) == true -> simétrico -> Se x.equals(y) == true então y.equals(x) == true -> transitiva -> x.equals(y) == true && y.equals(z) == true então x.equals(z) == true -> consistente -> sucessivas repetições de x.equals(y) ou y.equals(x) dá sempre o mesmo valor

  • Para valores nulos, a comparação com x, não nulo, dá como resultado false.

Caso não se implemente o método equals por omissão o java faz:

public boolean equals(Object object){
	return this == object;
}

Template típico de um método equals:

public boolean equals(Object o){

	if (this == 0)
		return true;
	if((o == null) || (this.getClass() != o.getClass()))
		return false;

	<CLASSE> m = (<CLASSE>) o;
	return (<condições de igualdade>);

}

Exemplo classe Aluno:

/**
* Implementação do método de igualdade entre dois Aluno
* Redefinição do método equals de Object
* 
* @param umAluno aluno é comparado com receptor
* @return booleano true ou false
*/
public boolean equals(Object o){

	if (this == 0)
		return true;
	if((o == null) || (this.getClass() != o.getClass()))
		return false;

	Aluno umAluno = (Aluno) o;
	return (this.nome.equals(umAluno.getNome()) 
			&& this.nota == umAluno.getNota()
			&& this.numero.equals(umAluno.getNumero())
			&& this.curso.equals(umAluno.getCurso())
			);
}

toString

  • Caso não seja implementado a resposta é: -> getClass().getName() + ‘@’ + Integer.toHexString(hashCode())

  • Todas as classes devem implementar este método

/**
* Implementação do método toString
* comum na maioria das classes Java
* 
* @return uma string com a informação textual do objecto aluno
*/
public String toString(){
	return("Numero" + this.numero + "Numero" + this.nome + "Nota:" this.nota);
}

  • Strings são objectos imutáveis, logo não crescem, o que as torna muito ineficientes
  • Para tornar a construção de Strings mais simples (e legível) pode recorrer-se à utilização da classe StringBuilder
/**
* Implementação do método toString
* comum na maioria das classes Java
* 
* @return uma string com a informação textual do objecto aluno
*/
public String toString(){
	StringBuilder sb = new StringBuilder();
	
	sb.append("Numero: ");
	sb.append(this.numero+"\n");
	sb.append("Nome: ");
	sb.append(this.nome+"\n");
	sb.append("Nota: ");
	sb.append(this.nota+"\n");

	return sb.toString();
}

Agregação

  • Neste Caso as instâncias de Aluno já não são internas à Turma - partilha-se o apontador

2025-03-19_01:46:10.png

Sumário

  • Composição: -> faz-se cópia (clone) dos objectos quando são guardados internamente -> devolve-se sempre uma cópia dos objectos e,caso seja necessário, da estrutura de dados que os guarda

  • Agregação: -> guarda-se internamente o apontador dos objectos passados como parâmetro -> devolve-se sempre o apontador dos objectos e, caso seja solicitado, uma cópia da estrutura da dados que os guarda.

2025-03-19_01:45:51.png

  • Quando o diagrama de classes não explicitar se a associação é de composição

Variáveis e Métodos de Classe

  • as variáveis de classe servem para guardar informação global a todas as instâncias
  • podem também ser utilizadas para guardar constantes que são utilizadas pelos diversos objectos instância
  • os métodos de classe fazem o acesso às variáveis de classe
  • os métodos de classe são sempre acessíveis às instâncias, mas métodos de classe não tem acesso aos métodos de instância
  • se uma classe possui variáveis de classe o acesso a essas variáveis deverá ser feito através dos métodos de classe
  • declaram métodos e variáveis de classe -> utilizando o prefixo static
  • seja uma classe Factura, que modela de forma muito simples uma factura
    -> queremos representar a informação que permitirá ao criar uma factura ajustar o valor ao imposto que na altura está em vigor.
    ->terá de ser uma definição global a todas as instâncias
public class Factura{
	//variáveis de instância
	private String nomeEmpresa;
	private String nif;
	private String descricaoDespesa;
	private LocalDate dataFact;
	private double valorFact;
	private double valorApgar; //pelo cliente, depois de aplicando o imposto

	// a taxa de imposto é definida globalmente para todos
	// as facturas e na altura da emissão deve utilizar-se
	// a taxa de imposto actual.

	poublic static double taxaImposto;
	
	public static double getTaxaImposto(){
		return taxaImposto;
	}
	
	public static void setTaxaImposto(double tx){
		taxaImposto = tx;
	}

}