Modelo rico ou gambiarra?

Pessoal,

sempre que construo um sistema de login, no meu controller LoginUser(por exemplo) eu acabo fazendo o seguinte:

if(User.getPerfil.equalsIgnoreCase("administrador"))
     return "administrador.html";
if(User.getPerfil.equalsIgnoreCase("convidado"))
     return "convidado.html";
if(User.getPerfil.equalsIgnoreCase("cliente"))
     return "cliente.html";

uma forma que pensei para contornar esses ifs seria criar uma interface para usuário e cada perfil de usuário saberia para que página retornar, ficaria mais ou menos assim:

public interface IUsuario {
    public String logar();
}

as implementações

public class Cliente implements IUsuario {
    private int id;
    private String nome;
   
    @Override
    public String logging() {
        return "cliente.html";

    }

}

public class Administrador implements IUsuario {
    private int id;
    private String nome;
   
    @Override
    public String logging() {
        return "administrador.html";
    }

}

dessa forma eu até evito de ter o atributo perfil, pq a própria classe é o perfil. e assim no meu controller eu teria algo como:

return user.logging(); //não precisando mais dos ifs.

só que fazendo isso eu estou colocando a regra de login dentro do meu modelo, o próprio objeto administrador/Cliente saberia como fazer o login.
aí vem minha dúvida, isso é modelo rico ou gambiarra?

Essa é a famosa refatoração: Substituir if/switch por polimorfismo.

Outra opção seria usar um map:

public Map<String, String> telas = new HashMap<String, String>(); telas.put("administrador", "administrador.html"); telas.put("convidado", "convidado.html"); telas.put("cliente", "cliente.html");

E na hora h…

return telas.get(User.getPerfil().toLowerCase());

Não acho que seja gambiarra.

Mas o que eu faria é criar uma classe para o atributo Perfil, ou mesmo uma enum.

Nesta classe/enum teria o método para retornar o html correto.

Como já até falaram, eu prefiro por enum viu. Algo parecido como abaixo:

[code]public enum Perfil{
ADMIN(“admin.xhtml”), USER(“user.xhtml”)

private String page;

private Perfil(String perfil){
    this.page = page;
}

public String getPage(){
    return page;
}

}[/code]

só que fazendo isso eu estou colocando a regra de login dentro do meu modelo

e sobre isso? não fica estranho isso?

[quote=gambazinho]só que fazendo isso eu estou colocando a regra de login dentro do meu modelo

e sobre isso? não fica estranho isso?[/quote]
Para fugir disso então, você poderia no caso do enum, criar: um arquivo de constantes de páginas, ou um resource bundle ou um arquivo de configurações e salvar apenas a chave da página no enum.

Tipo assim:
ADMIN(“PAGINA_ADMIN”), USER(“PAGINA_USER”)
e quando você fosse utilizar esse valor, você buscaria no real da página.

Desse modo, seu modelo sabe apenas que o ADMIN teria uma PAGINA_ADMIN mas não terá conhecimento de qual página é.

Que tal?

Vc esta correto sim!
Prefira estrutura polimórficas sobre controle de IF’s.
No caso vc optou bem, implementando o padrão STRATEGY.
Unico erro é que vc furou a camada, colocando a pagina dentro do modelo.
A regra de negocio deve ser independente de infra estrutura e detalhes de front-end.

[quote=FernandoFranzini]
Unico erro é que vc furou a camada, colocando a pagina dentro do modelo.
A regra de negocio deve ser independente de infra estrutura e detalhes de front-end.[/quote]

então é essa minha dúvida, como corrigir isso sem ter de criar enum??

[quote=gambazinho][quote=FernandoFranzini]
Unico erro é que vc furou a camada, colocando a pagina dentro do modelo.
A regra de negocio deve ser independente de infra estrutura e detalhes de front-end.[/quote]

então é essa minha dúvida, como corrigir isso sem ter de criar enum??[/quote]
Pq não criar enum?
O enum abaixo não vai ter detalhe algum do front-end. Em que a solução abaixo fura o modelo?

[quote=jakefrog]Para fugir disso então, você poderia no caso do enum, criar: um arquivo de constantes de páginas, ou um resource bundle ou um arquivo de configurações e salvar apenas a chave da página no enum.

Tipo assim:
ADMIN(“PAGINA_ADMIN”), USER(“PAGINA_USER”)
e quando você fosse utilizar esse valor, você buscaria no real da página.

Desse modo, seu modelo sabe apenas que o ADMIN teria uma PAGINA_ADMIN mas não terá conhecimento de qual página é.

Que tal?[/quote]

jakefrog poderia dar um pequeno exemplo de como isso ficaria? em que pacote eu criaria esse enum?

Você criaria dentro do mesmo pacote do seu usuário, pois faz parte do modelo saber qual o perfil do seu usuário.

Quando você fosse buscar a página, você buscar em um arquivo properties algo como abaixo:

public String getPage(){ return properties.get(user.getPerfil().getPage()); }
Perfil, seria o Enum. Note, que o perfil vai indicar o tipo/chave (admin.page) da página mas não a página em si (/pages/admin.xhtml).

Em seu arquivo de properties você teria algo do tipo:

pagina.admin=/pages/admin.xhtml pagina.user=/pages/user.xhtml
E seu enum seria tipo:

[code]public enum Perfil{
ADMIN(“pagina.admin”), USER(“pagina.user”)

private String page;  

private Perfil(String perfil){  
    this.page = page;  
}  

public String getPage(){  
    return page;  
}  

} [/code]

  1. O controle de decisão do front-end deve ficar na camada de visão utilizada - web, desktop, celular, smartphone etc.
  2. O tipo polimórfico do objeto de domínio ja indica isso, vc não precisa misturar o strategy + template method.

Na camada de visão:
if (objetoDominio instanceof X) {
return enviar para front-end A;
} else if (objetoDominio instanceof Y) {
return enviar para front-end B;
{

Tipico metodo de camada front-end MVC.

[quote=FernandoFranzini]1) O controle de decisão do front-end deve ficar na camada de visão utilizada - web, desktop, celular, smartphone etc.
2) O tipo polimórfico do objeto de domínio ja indica isso, vc não precisa misturar o strategy + template method.

Na camada de visão:
if (objetoDominio instanceof X) {
return enviar para front-end A;
} else if (objetoDominio instanceof Y) {
return enviar para front-end B;
{

Tipico metodo de camada front-end MVC.
[/quote]

mas aí eu estaria apenas trocando um if por outro… o problema dos ifs continuariam da mesma forma…
concorda?

if (objetoDominio instanceof X) {
return enviar para front-end A;
} else if (objetoDominio instanceof Y) {
return enviar para front-end B;
{ 

e

   if(User.getPerfil.equalsIgnoreCase("administrador"))  
        return "administrador.html";  
   if(User.getPerfil.equalsIgnoreCase("convidado"))  
        return "convidado.html";  
   if(User.getPerfil.equalsIgnoreCase("cliente"))  
        return "cliente.html"; 

[quote]mas aí eu estaria apenas trocando um if por outro… o problema dos ifs continuariam da mesma forma…
concorda? [/quote]
Sim…mas agora esta:

  1. flexível
  2. Extensível
  3. Na camada correta.

Veja que a cada vez que vc criar uma classe nova de sua API q tenha regras de autenticação intercambial, vc precisa manutenir esse if:

  1. criar uma classe novo, herdando da base.
  2. criar um html novo.
  3. aumentar o if.

Seu estrategia arquitetural esta 100% compatível.

Qual o problema?

[quote=FernandoFranzini]
3) aumentar o if.
Qual o problema?[/quote]

esse é o problema…

Eu não acho ruim não…acho que esta consistente.
A questão é - que adianta vc fazer um estrutura mais genérica, sem aumentar o if sendo que vc tem que criar mais uma pagina para cada estrategia?
Outro fator?
Qual é previsão desse tipo de caso de uso mudar?
normalmente vc vai criar um grupo agora no inicio e ficar estável para muito tempo.
Desde que vc documente no GUIDELINE arquitetural ta consistente sim.

Com todo respeito FernandoFranzini, discordo da sua abordagem.

Você afirma que o sistema está mais flexível, extensível e na camada correta.
Eu pergunto: Onde a solução do jakefrog falha neste aspecto?

Pela sua arquitetura, a cada novo perfil você teria que alterar em 3 pontos, na dele apenas em 2.
Uma vantagem adicional: você seria obrigado a implementar o método.
Um if é muito mais simples de se esquecer.

Como disse o gambazinho, você está trocando um if simples por um complexo.
Não vejo vantagens nisso.

Aliás, pensando melhor no código inicial postado, com uma simples convenção você trocaria o if por uma linha, no controller:

     return User.getPerfil() +  ".html";

Por ser uma página diferente por perfil, a criação do html em si é inevitável.
O que precisamos minimizar é o número de lugares que são afetados a cada mudança (isso facilita a manuntenção).

Em todos os outros pontos acredito que a duplicação é desnecessária.

É isso que ta matando a decisão…
adicionar uma pagina + 1 if não faz diferença…kkkkk Se existe manutenção…existe!
Eu particularmente, na verdade faria de tudo para não adicionar pagina a cada no perfil de login, deixando tudo flexível…Ou seja, uma unica pagina por perfil. Ou seja, mudança de filosofia…
Mesmo usando enum, ele teria que adicionar mais um valor nas constantes ao inves do if…ou seja…ficariamos na mesma…
Mas veja que o cenário do amigo ai…

O if extra faz diferença justamente na hora da manuntenção, é muito simples esquecer.
Se você pode resolver com menos pontos de alteração, para que resolver com mais?

O uso de enum pelo menos te forçaria a passar algum valor para o getPage() , evitando que esqueça de implementar esta parte.

Se você tiver o sistema com views em desktop, mobile, web, etc… terá que ter um if em cada view.
Poderia se resumir a uma linha de código por view realizando o mapeamento.

Se ambos não forem alterados o sistema não vai funcionar…ou seja, não tem como esquecer…
O esforço é o mesmo…kkkkkkk