Duvida sobre Orientação a Objetos

Suponha uma classe chamada turma

public class Turma {
    private List<Aluno> alunos;
}

e outra classe chamada aluno

public class Aluno {
    private Turma turmaAtual;
}

Onde eu coloco a regra de negócio que trata a transferência de aluno de turma?

Eu vejo que o mais óbvio seria colocar na classe Turma, adicionando um método add(Aluno) e um remove(Aluno), mas para atualizar o campo “turmaAtual” da classe aluno, deveria existir um método “setTurmaAtual(Turma)” publico dentro de Aluno, o que possibilitaria qualquer trecho de código chamar esse método, e isso não é o ideal pois para trocar de turma deve obrigatoriamente passar pelo metodo add da classe Turma.

Alguem tem outra idéia?

Isso ficou complicado.

Aluno realmente precisa do atributo turmaAtual?

Se você fizer da maneira que sugeriu, tudo vai funcionar, mas você vai ter um alto acoplamento entre as classes (Turma sabe detalhes de implementação de Aluno), e isso é uma péssima prática.

Uma opção seria:

public class Turma {

    private Set<Aluno> alunosMatriculados;

    public void matricula(Aluno novoAluno) {
        alunosMatriculados.add(novoAluno);
    }

    //...

}

public class Aluno {

    private Turma turmaAtual;

    public void transfere(Turma novaTurma) {
        turmaAtual = novaTurma;
        novaTurma.matricula(this);
    }

    //...

}

Se eu tirar turmaAtual de aluno, como eu recuperaria essa informação de forma eficiente?
Pesquisar nas turmas não é legal…

Mas não seria responsabilidade da turma receber os alunos?
Digamos que a turma tenha um limite máximo de alunos, ou seja uma turma com uma faixa etaria definida, como eu faria essa validação?

[quote=bob_sponja]Uma opção seria:

[code]
public class Turma {

private Set<Aluno> alunosMatriculados;

public void matricula(Aluno novoAluno) {
    alunosMatriculados.add(novoAluno);
}

//...

}

public class Aluno {

private Turma turmaAtual;

public void transfere(Turma novaTurma) {
    turmaAtual = novaTurma;
    novaTurma.matricula(this);
}

//...

}
[/code][/quote]

Então eu imagino uma camada acima de tudo (pode não ser a melhor solução)

Algo como Escola, que possui um método matricularAluno(Aluno, Turma)

Eu acho que a matrícula é responsabilidade da escola. Vc não matricula seu filho numa turma. Vc matricula seu filho numa escola e a escola coloca em determinada turma de acordo com algum critério (número de alunos, vagas, idade, desempenho, etc.)

Porém, acho legal a Turma ser uma classe. Fica mais fácil e acessível para você informações como média da turma, quantidade de alunos, etc. Também não vejo problema em ter a informação da turma atual na classe Aluno, mas somente como consulta (somente getter). Não é certo alterar um atributo da turma ou acessar um comportamento através de Aluno.

Gostei da idéia da classe Escola, realmente a divisão das resposabilidades fica melhor, pois a escola poderia escolher outra turma automaticamente.

Mas ainda fica a duvida: Como atualizar a turmaAtual dentro do aluno?
Seria uma boa idéia eu colocar tudo no mesmo pacote e colocar o setter como protected?

[quote=Arthur F. Ferreira]Eu acho que a matrícula é responsabilidade da escola. Vc não matricula seu filho numa turma. Vc matricula seu filho numa escola e a escola coloca em determinada turma de acordo com algum critério (número de alunos, vagas, idade, desempenho, etc.)

Porém, acho legal a Turma ser uma classe. Fica mais fácil e acessível para você informações como média da turma, quantidade de alunos, etc. Também não vejo problema em ter a informação da turma atual na classe Aluno, mas somente como consulta (somente getter). Não é certo alterar um atributo da turma ou acessar um comportamento através de Aluno.[/quote]

Essa dúvida é em relação às entidades (objetos) ou ao banco de dados (tabelas)?

Objetos

Pode deixar o setter como protected se preferir.

Agora uns pontos que você precisa pensar.

  • Se matricular um aluno em uma turma, como vai garantir que ele não se matricule em outra também? Ou isso é permitido?
  • O que acontece se matricular o aluno duas vezes?

As classes de dominio nao devem ter regras de negocio… devem ser o mais simples possivel…
Essas regras nao deve ficar em nem uma das classes…

Essas logicas tem que ficar em uma camada mais acima…
se quiser fazer um turma.addAluno(aluno), terá que ser em uma classe Controladora da Turma, e nao nas classes de dominio…

[quote=igor_ks]As classes de dominio nao devem ter regras de negocio… devem ser o mais simples possivel…
Essas regras nao deve ficar em nem uma das classes…

Essas logicas tem que ficar em uma camada mais acima…
se quiser fazer um turma.addAluno(aluno), terá que ser em uma classe Controladora da Turma, e nao nas classes de dominio…[/quote]
Isso depende da sua modelagem.

O desenvolvimento orientado a domínio é interessante, e aproveita muito da orientação a objetos.

Essa ideia de que as classes de domínio devem ser um espelho da entidade (ou um saco de getters e setters), teve uma influência grande da especificação Java EE, e é chamado de modelo anêmico.

Então se o projeto estiver sendo desenvolvido com DDD, ele está fazendo da maneira correta.

Isso depende da abordagem de orientação a objetos que se esta adotando.

Aqui na empresa que trabalho procuramos seguir, sempre que possível, as boas práticas do DDD (Domain Driven Design), que diz que sim, podemos ter regras de negócio em nossas entidades.

[quote=igor_ks]As classes de dominio nao devem ter regras de negocio… devem ser o mais simples possivel…
Essas regras nao deve ficar em nem uma das classes…

Essas logicas tem que ficar em uma camada mais acima…
se quiser fazer um turma.addAluno(aluno), terá que ser em uma classe Controladora da Turma, e nao nas classes de dominio…[/quote]

[quote=Rodrigo Sasaki]Pode deixar o setter como protected se preferir.

Agora uns pontos que você precisa pensar.

  • Se matricular um aluno em uma turma, como vai garantir que ele não se matricule em outra também? Ou isso é permitido?
  • O que acontece se matricular o aluno duas vezes?[/quote]

O atributo “turmaAtual” de aluno possilita de forma fácil identificar qual a turma que ele se encontra, para remove-lo de lá.
O meu exemplo é sobre alunos/turma, para utilizar uma idéia mais comum no dia a dia, e não tem relação com o sistema que estou desenvolvendo.
:slight_smile:

[quote=igor_ks]As classes de dominio nao devem ter regras de negocio… devem ser o mais simples possivel…
Essas regras nao deve ficar em nem uma das classes…

Essas logicas tem que ficar em uma camada mais acima…
se quiser fazer um turma.addAluno(aluno), terá que ser em uma classe Controladora da Turma, e nao nas classes de dominio…[/quote]

Exatamente, ai você aproveita e assina um certificado de evangelista do modelo anêmico e utilizador fake de OOP. Classe controladora deve ser responsável por intermediar View e Model. Modelos devem conter a logica de negócios e não controllers.

[quote=ezidio]Gostei da idéia da classe Escola, realmente a divisão das resposabilidades fica melhor, pois a escola poderia escolher outra turma automaticamente.

Mas ainda fica a duvida: Como atualizar a turmaAtual dentro do aluno?
Seria uma boa idéia eu colocar tudo no mesmo pacote e colocar o setter como protected?

[quote=Arthur F. Ferreira]Eu acho que a matrícula é responsabilidade da escola. Vc não matricula seu filho numa turma. Vc matricula seu filho numa escola e a escola coloca em determinada turma de acordo com algum critério (número de alunos, vagas, idade, desempenho, etc.)

Porém, acho legal a Turma ser uma classe. Fica mais fácil e acessível para você informações como média da turma, quantidade de alunos, etc. Também não vejo problema em ter a informação da turma atual na classe Aluno, mas somente como consulta (somente getter). Não é certo alterar um atributo da turma ou acessar um comportamento através de Aluno.[/quote][/quote]

Basicamente, o que você quer seria algo desse tipo aqui:

Escola.java

public class Escola {

    private List<Turma> turmas;
    
    public Escola() {
        this.turmas = new ArrayList<>();
    }
    
    public void matricula(Aluno aluno) {
        Turma turma = turmas.get(turmas.size() - 1);
        if (!turma.adiciona(aluno)) {
            turma = new Turma();
            turma.adiciona(aluno);
        }
        
        System.out.println("Aluno " + aluno.getNome() + " matriculado na turma " + turma.getNumero() + " com sucesso!");
    }
    
    public void mudaAlunoDeTurma(Aluno aluno, Turma novaTurma) {
        Turma turmaAnterior = aluno.getTurma();
        turmaAnterior.remove(aluno);
        novaTurma.adiciona(aluno);
    }
    
}

Turma.java

public class Turma {

    private static final short MAXIMO_DE_ALUNOS = 50;
    
    private static int contadorNumero = 0;
    
    private int numero;
    
    private List<Aluno> alunos;
    
    public Turma() {
        this.alunos = new ArrayList<>();
        this.numero = contadorNumero++;
    }
    
    public boolean adiciona(Aluno aluno) {
        if (MAXIMO_DE_ALUNOS > alunos.size()) {
            this.alunos.add(aluno);
            aluno.setTurma(this);
            return true;
        } else {
            return false;
        }
    }

    public int quantidadeDeAlunos() {
        return this.alunos.size();
    }

    public int getNumero() {
        return this.numero;
    }

    public void remove(Aluno aluno) {
        this.alunos.remove(aluno);
    }
    
}

Aluno.java

public class Aluno {

    private String nome;

    private float nota;

    private Turma turma;

    public Aluno(String nome) {
        this.nome = nome;
    }

    public String getNome() {
        return this.nome;
    }

    public Turma getTurma() {
        return this.turma;
    }
    
    public void setTurma(Turma turma) {
        this.turma = turma;
    }

    public float getNota() {
        return this.nota;
    }

    public void setNota(float nota) {
        this.nota = nota;
    }

}

Porém, como o Sasaki falou, tem questões a serem resolvidas além do que você quer, como as que ele citou. Além disso, a pergunta do drsmachado é pertinente se você estiver pensando em trabalhar com banco de dados. Eu imagino que você esteja estudando com exemplos básicos para orientação objeto ainda, certo? Se for isso, esqueça (por enquanto) as questões de entidades, bancos de dados, tabelas, domino, controlador, etc. Foque no conceito e aprendizado da orientação a objeto. Depois que vc pegar isso, vc vai partir para essa questão de banco de dados, normalização, camadas, etc.

Posso estar errado, mas tenho a impressão que vocês estão achando que estou pensando em banco de dados devido ao fato de querer colocar o “turmaAtual” na classe aluno. Mas eu apenas estou considerando esse item para facilitar recuperar as informações de turma através do aluno.

A remoção do “classeAtual” resolveria meu problema tambem, porem criaria outros… Se alguem tiver uma idéia melhor para este caso, por favor, me informem.

Não sou novato em OO, mas tambem não me considero um expert. Um dia chego la…

[quote=Arthur F. Ferreira][quote=ezidio]Gostei da idéia da classe Escola, realmente a divisão das resposabilidades fica melhor, pois a escola poderia escolher outra turma automaticamente.

Mas ainda fica a duvida: Como atualizar a turmaAtual dentro do aluno?
Seria uma boa idéia eu colocar tudo no mesmo pacote e colocar o setter como protected?

[quote=Arthur F. Ferreira]Eu acho que a matrícula é responsabilidade da escola. Vc não matricula seu filho numa turma. Vc matricula seu filho numa escola e a escola coloca em determinada turma de acordo com algum critério (número de alunos, vagas, idade, desempenho, etc.)

Porém, acho legal a Turma ser uma classe. Fica mais fácil e acessível para você informações como média da turma, quantidade de alunos, etc. Também não vejo problema em ter a informação da turma atual na classe Aluno, mas somente como consulta (somente getter). Não é certo alterar um atributo da turma ou acessar um comportamento através de Aluno.[/quote][/quote]

Basicamente, o que você quer seria algo desse tipo aqui:

Escola.java

public class Escola {

    private List<Turma> turmas;
    
    public Escola() {
        this.turmas = new ArrayList<>();
    }
    
    public void matricula(Aluno aluno) {
        Turma turma = turmas.get(turmas.size() - 1);
        if (!turma.adiciona(aluno)) {
            turma = new Turma();
            turma.adiciona(aluno);
        }
        
        System.out.println("Aluno " + aluno.getNome() + " matriculado na turma " + turma.getNumero() + " com sucesso!");
    }
    
    public void mudaAlunoDeTurma(Aluno aluno, Turma novaTurma) {
        Turma turmaAnterior = aluno.getTurma();
        turmaAnterior.remove(aluno);
        novaTurma.adiciona(aluno);
    }
    
}

Turma.java

public class Turma {

    private static final short MAXIMO_DE_ALUNOS = 50;
    
    private static int contadorNumero = 0;
    
    private int numero;
    
    private List<Aluno> alunos;
    
    public Turma() {
        this.alunos = new ArrayList<>();
        this.numero = contadorNumero++;
    }
    
    public boolean adiciona(Aluno aluno) {
        if (MAXIMO_DE_ALUNOS > alunos.size()) {
            this.alunos.add(aluno);
            aluno.setTurma(this);
            return true;
        } else {
            return false;
        }
    }

    public int quantidadeDeAlunos() {
        return this.alunos.size();
    }

    public int getNumero() {
        return this.numero;
    }

    public void remove(Aluno aluno) {
        this.alunos.remove(aluno);
    }
    
}

Aluno.java

public class Aluno {

    private String nome;

    private float nota;

    private Turma turma;

    public Aluno(String nome) {
        this.nome = nome;
    }

    public String getNome() {
        return this.nome;
    }

    public Turma getTurma() {
        return this.turma;
    }
    
    public void setTurma(Turma turma) {
        this.turma = turma;
    }

    public float getNota() {
        return this.nota;
    }

    public void setNota(float nota) {
        this.nota = nota;
    }

}

Porém, como o Sasaki falou, tem questões a serem resolvidas além do que você quer, como as que ele citou. Além disso, a pergunta do drsmachado é pertinente se você estiver pensando em trabalhar com banco de dados. Eu imagino que você esteja estudando com exemplos básicos para orientação objeto ainda, certo? Se for isso, esqueça (por enquanto) as questões de entidades, bancos de dados, tabelas, domino, controlador, etc. Foque no conceito e aprendizado da orientação a objeto. Depois que vc pegar isso, vc vai partir para essa questão de banco de dados, normalização, camadas, etc.[/quote]

eu não gostei do aluno.setTurma(this); pois mantém um acoplamento alto entre as classes.

Agora respondendo sua pergunta, sim, eu entendi que você não está falando de banco de dados, e acho que a sua solução pode ser interessante, mas você deve pensar nos casos que eu te falei, senão pode ter alunos duplicados em turmas e/ou alunos em turmas diversas, e com certeza você não quer isso.

Ao matricular um aluno, como garantir que ele não está matriculado em outra turma? Mais cedo ou mais tarde você terá que varrer as turmas.

[quote=ezidio]Posso estar errado, mas tenho a impressão que vocês estão achando que estou pensando em banco de dados devido ao fato de querer colocar o “turmaAtual” na classe aluno. Mas eu apenas estou considerando esse item para facilitar recuperar as informações de turma através do aluno.

A remoção do “classeAtual” resolveria meu problema tambem, porem criaria outros… Se alguem tiver uma idéia melhor para este caso, por favor, me informem.

Não sou novato em OO, mas tambem não me considero um expert. Um dia chego la…[/quote]

Por mim não tem problemas em nenhum cenário. Tanto que eu te dei um código (simples) funcional com a situação de você ter a turma atual do aluno na classe.

Sim. Vc tem um ganho de não precisar consultar ou varrer uma lista para ter as informações da turma do aluno, mas você acopla o código. Não tem como fugir disso. Mas aí é colocar na balança se o acoplamento vai ser mais vantajoso que o outro cenário.