Modelando funções... Herança?

4 respostas
Danilo_Barboza

Saudações!!

Estou com uma dúvida em modelar funções dentro do sistema... Por enquanto tenho apenas duas funções (Aluno e Porfessor). Eu deveria utilizar herança para modelar isso? Ex:

abstract class Usuario {
	
	abstract public boolean isProfessor();
	
	//outros comportamentos e atributos de usuario
}


class Professor extends Usuario {
	
	@Override
	public boolean isProfessor() {
		return true;
	}
}

class Aluno extends Usuario{
	
	@Override
	public boolean isProfessor() {
		return false;
	}
}

Ou então com uma única classe representando Usuário e um ENUM indicando a função dele? ex:

enum Funcao {
     ALUNO, PROFESSOR;
}

class Usuario {
	
	private Funcao funcao;
	
	public Usuario(Funcao funcao) {
		this.funcao = funcao;
	}
	
	public Funcao getFuncao() {
		return funcao;
	}
	
	//outros comportamentos e atributos de usuario
}

O ponto aqui é que o professor terá acesso a alguns lugares que o aluno não terá. Então em alguns pontos do sistema eu tenho que verificar se o usuario que esta tentando acessar é um Aluno ou um Professor.

Como o Aluno tem um comportamento diferente do Professor imaginei que ambos fossem classes diferentes... Mas não faz muito sentido eu perguntar pro usuário algo do tipo:

Usuario usuario = //recupera o usuario de algum lugar
        if(!usuario.podeAcessar(lugarOndeApenasAlgunsAlunosPodemAcessar))
              throw new VoceNaoPodeAcessarException();

O lugarOndeApenasAlgunsAlunosPodemAcessar saberia que apenas alguns alunos específicos podem acessar (talvez com uma lista de ids ou algo do tipo). Dessa forma o meu objeto usuário teria que chamar um método e saber de um atributo (estado) interno do objeto que quero acessar... Já li em alguns lugares: "Se quer saber algo sobre os atributos do objeto, pergunte pra ele!". ex:

class Usuario {
	//...
	public boolean podeAcessar(LugarAcessivel lugarAcessivel){
		if(lugarAcessivel.getListaDeUsuariosQuePodemAcessar().contains(this))
			return true;
		else if(funcao.equals(Funcao.PROFESSOR)) //Se for professor, pode acessar (com herança se chamaria isProfessor())
			return true;
		else
			return false;	
	}
}

Então pensei no seguinte:

Usuario usuario = //recupera o usuario de algum lugar
        if(!lugarOndeApenasAlgunsAlunosPodemAcessar.podeSerAcessadoPor(usuario))
              throw new VoceNaoPodeAcessarException();

Só que nesse caso também teríamos que saber de um atributo (estado) do objeto Usuario para fazer alguma coisa... O que também não faz sentido... Pois, se quero saber algo do objeto, pergunte pra ele! ex:

class LugarAcessivel {
	
	private List<Usuario> usuariosQuePodemAcessar;
	//...
	public boolean podeSerAcessadoPor(Usuario usuario) {
		if(usuariosQuePodemAcessar.contains(usuario))
			return true;
		else if(usuario.getFuncao().equals(Funcao.PROFESSOR)) //Se for professor, pode acessar (com herança se chamaria isProfessor())
			return true;
		else return false;
	}
}

Note que nesse segundo exemplo o isProfessor faria mais sentido... (nao seria necessário pegar a funçao do usuario e compará-la... basta perguntar pro usuario)

Enfim, o que vocês acham dessa modelagem? Acha que existe uma melhor forma de modelar algo desse tipo?

Gostaria de ouvir (ler) a opinião de vocês!

Até!

4 Respostas

T

Olá Danilo,

acredito que o melhor cenário seria utilizar herança e pensar na possibilidade de ao invés de checar por isProfessor, comparar os tipos da classe. Dessa maneira você economiza em métodos e usa o recurso nativo da linguagem.

Outra sugestão seria a de implementar um serviço para verificar acesso, pois não faz muito sentido essa responsabilidade ser do próprio usuário. Acredito que deveria ter um serviço do tipo porteiro, por exemplo, e o usuário em questão apenas apresentaria suas credenciais e o serviço seria responsável por decidir se o usuário pode ou não acessar.

Soçarba

Danilo_Barboza

Então eu deveria utilizar herança e fazer um instanceof pra saber se é aluno ou professor? Acho estranho, mas é uma alternativa… Como professores e alunos tem apenas esse tipo de comportamento diferente (acesso) acho que faz sentido tê-los em classes diferentes, mas, uma vez que não vou utilizar dos benefícios da herança pra isso, o melhor não seria ter um atributo em usuário falando se ele é aluno ou professor? Não sei…

Sim. Concordo que é função de um serviço tratar esse tipo de coisa. Entretanto se eu tiver um Arquivo, por exemplo, e esse arquivo só pode ser acessado por alguem que seja professor ou pelos alunos X e Y. Quem sabe disso é o Arquivo e não o serviço. Acho que o serviço só serviria como fachada… É isso que você quis dizer?

Acho que a melhor abordagem seria a segunda que mostrei:

Usuario usuario = //recupera o usuario de algum lugar
        if(!lugarOndeApenasAlgunsAlunosPodemAcessar.podeSerAcessadoPor(usuario))
              throw new VoceNaoPodeAcessarException();

Embora minha dúvida com relação ao princípio de “Se quer saber algo sobre o objeto, pergunte pra ele!” continua…

Obrigado pela resposta!

thinet

Você poderia usar uma interface ou fazer uma composição de usuário com professor e aluno, assim cada acesso teria sua responsabilidade bem dividida.

Desculpe se escrevi alguma besteira

alarangeiras

Caro Danilo,

O intuito da herança é proporcionar o polimorfismo e a reutilização de código. A herança só deve ser usada quando você estiver criando uma classe de comportamento mais específico. Uma maneira fácil de se entender a herança é usando o conceito de É-UM. Basta ler desta maneira: ProfessorDeMatematica É-UM Professor. Aluno É-UM Professor (Semânticamente Errado).
Quando utilizamos interfaces estamos dispostos a especificar um comportamente específico geralmente um padrão em classes de hierarquias diferentes.
Aluno não é um Professor porém também pode se autenticar no sistema. Por Exemplo:

public class Professor {

        public void leciona(Object disciplina) {
                 //codigo
        }

}

public class ProfessorDeMatematica extends Professor implements Autenticavel {

        public void leciona(Object disciplina) {
               //codigo
        }

        public void autenticarNoSistema() {
               //codigo
        }

}

public class Aluno implements Autenticavel {

        public void autenticarNoSistema() {
               //codigo
        }

}

public interface Autenticavel {

    void autenticarNoSistema();

}

Uma fez usado o implements você terá assinado o contrato com a interface e terá que implementar todos os métodos da mesma…
Essa abordagem é eficaz e intensamente recomendada até mesma pela Sun.

Bons códigos para ti.

Criado 31 de outubro de 2008
Ultima resposta 2 de nov. de 2008
Respostas 4
Participantes 4