Usar uma interface vazia é muito horrendo?

Boa tarde colegas, vou explanar a situação pra vocês.

Estou desenvolvendo em dupla com um colega um projeto de final de semestre. Em resumo estamos seguindo uma especificação que o professor nos deu, onde precisamos aplicar os conhecimentos de Herança, Polimorfismo, Sobrescrita, Sobrecarga e Interfaces aprendidos durante o semestre.

Porém chegamos em uma parte do sistema, que - pode ser devido ao mau dimensionamento do diagrama de classes, ou algum outro problema que não estamos encontrando - vemos necessidade de usar uma interface vazia.

Interface vazia pra que?

Pra não usar o maldito instanceof e matar todo o polimorfismo que estamos mantendo até então.

O que vocês acham colegas? Essa é uma abordagem muito bizarra? Já presenciaram esse tipo de “técnica” no dia-a-dia?

Li um tópico aqui onde um user encontrou esse caso no trabalho, e inclusive estava com dúvidas sobre o motivo de se ter uma interface vazia.

Vejo que essa é a única solução que vai me ajudar a não meter vários instanceof inúteis no meu código.

Abraços!

Bom, exemplo de interface vazia é a Serializable do próprio Java…
Mas ser do Java não significa que é bom.

Eu acho ruim e sim, horrendo.

Vamos ver o seguinte, você vai usar esses objetos para quê? COMO você vai usar essas classes diferentes, se vc precisar acessar algum método em comum, essa interface pode ser a saída…

No mais, você pode fazer heranças de interface, se for o caso, ai ela pode ficar vazia, mas ela é composta de várias interfaces…

Pode mostrar o código?

[quote=Rafael Guerreiro]Bom, exemplo de interface vazia é a Serializable do próprio Java…
Mas ser do Java não significa que é bom.

Eu acho ruim e sim, horrendo.

Vamos ver o seguinte, você vai usar esses objetos para quê? COMO você vai usar essas classes diferentes, se vc precisar acessar algum método em comum, essa interface pode ser a saída…

No mais, você pode fazer heranças de interface, se for o caso, ai ela pode ficar vazia, mas ela é composta de várias interfaces…

Pode mostrar o código?[/quote]

No meu caso, tenho que simular um Computador.

O meu problema é esse: Tenho uma classe PortaParalela. Na PortaParalela posso acoplar tanto um Leitor(CD ou DVD por exemplo) como um DispositivoArmazenamentoParalelo(um HD por exemplo).

Por isso veio a idéia de criar uma interface que une Leitor e DispositivoArmazenamento, e fazer com que PortaParalela receba qualquer dispositivo que implemente tal interface.

Como o colega acima já falou, a própria API java possui marker interfaces, interfaces vazias. Dependendo do motivo dá pra aceitar.

[quote=Ruttmann]
No meu caso, tenho que simular um Computador.

O meu problema é esse: Tenho uma classe PortaParalela. Na PortaParalela posso acoplar tanto um Leitor(CD ou DVD por exemplo) como um DispositivoArmazenamentoParalelo(um HD por exemplo).

Por isso veio a idéia de criar uma interface que une Leitor e DispositivoArmazenamento, e fazer com que PortaParalela receba qualquer dispositivo que implemente tal interface.[/quote]
Mas existe algum método que você queira usar, como “salvar(Arquivo arquivo)”, “ler(Arquivo arquivo)”, “remover(Arquivo arquivo)”?

Dê uma olhadinha na Lei de Demeter, acho que ela é a saída que você procura.

Por exemplo, temos a classe Pessoa e a classe Endereço:

class Pessoa {
   private String nome;
   private Endereco endereco;

// getters e setters
}

class Endereco {
   private String rua;
   private int numero;
   private String cep;

// getters e setters
}

Então para acessar o cep de uma pessoa, você precisa fazer assim:

pessoa.getEndereco().getCep();

Isso gera um custo elevado de manutenção. Vamos supor que vamos criar a classe CEP e dentro dela o getCepFormatado() iria formatar o cep para nós… Teríamos que alterar todos os lugares que fazem o getCep() para incluir um .getCepFormatado()…

Portanto, seria menos custoso manter um código que fizesse assim:

pessoa.getCep();

Pois assim, só precisaria mexer na classe Pessoa… Para que isso aconteça, as nossas classes precisam ficar assim:

class Pessoa {
   private String nome;
   private Endereco endereco;

// Construtor para setar

   public String getNome() {
      return nome;
   }

   public String getCep() {
      return endereco.getCep();
   }
}

class Endereco {
   private String rua;
   private int numero;
   private String cep;

// getters e setters
}

E depois de arrumado, ficaria assim:

class Pessoa {
   private String nome;
   private Endereco endereco;

// Construtor para setar

   public String getNome() {
      return nome;
   }

   public String getCep() {
      return endereco.getCepFormatado();
   }
}

class Endereco {
   private String rua;
   private int numero;
   private CEP cep;

// Construtor para setar

   public String getCepFormatado() {
      return cep.getCepFormatado();
   }

// outros getters
}

class CEP {
   private primeirosDigitos;
   private ultimosDigitos;

// Construtor para setar

   public String getCepFormatado() {
      return primeirosDigitos + "-" + ultimosDigitos;
   }
}

Porque não cria uma classe abstrata pai? Com isso vc acaba criando uma camada a mais (acima) e caso precise algo que todas suas subclasses forem precisar um dia, fica bem mais manutenívele dará pra fazer o polimorfismo. Se fizer com interfaces, todas as classes que implementam ela vai quebrar, pois vai ter que obrigatoriamente implementar esse novo método.

Só aproveitando: No Java 8 vai ser possível adicionar métodos a interfaces sem quebrar código já existente. Serão os chamados “métodos default”. :slight_smile:

Foi a gambiarra que encontraram para evoluir as interfaces da API Collections para acomodar as lambda expressions.

[quote=rodrigo.uchoa]Só aproveitando: No Java 8 vai ser possível adicionar métodos a interfaces sem quebrar código já existente. Serão os chamados “métodos default”. :slight_smile:

Foi a gambiarra que encontraram para evoluir as interfaces da API Collections para acomodar as lambda expressions.[/quote]Só uma observação, adicionar “método” apenas um e não muitos. [=

Hum…tem certeza disso? Não achei nada relacionado a essa limitação de ser apenas um método por interface.

Eu sei que interfaces com apenas um método terão um tratamento especial (acho que consideradas funções, não tenho certeza), mas não vi essa limitação de apenas um método por interface.

Respondendo a pergunta do tópico: qual o sentido de você ter diferentes componentes implementando a mesma interface, se você não consegue extrair comportamento de nenhum deles?

No caso da porta paralela, a real, ela deve ter um protocolo padrão para se comunicar com qualquer componente que se conecte a ela. O componente tem que implementar esse protocolo.

O protocolo seria justamente o comportamento que você não está definindo porque a interface está vazia.

Hum…tem certeza disso? Não achei nada relacionado a essa limitação de ser apenas um método por interface.

Eu sei que interfaces com apenas um método terão um tratamento especial (acho que consideradas funções, não tenho certeza), mas não vi essa limitação de apenas um método por interface.[/quote]Foi o que o representante da Oracle disse no TDC 2013. [=

Acho que ele se enganou nessa.

Testando aqui o early access release do Java 8, essa funcionalidade já está disponível (default methods ou defender methods).
E ela aceita mais de um método concreto sem problemas.
Espero que não tirem isso…

Tem o conceito agora de Functional Interface, que é uma interface com apenas um método abstrato e que pode ser convertido em lambda.

Então uma Runnable da vida, por exemplo, não precisará mais de uma classe anônima.
Você poderá passar só uma lambda direto.

Acho que ele se enganou nessa.

Testando aqui o early access release do Java 8, essa funcionalidade já está disponível (default methods ou defender methods).
E ela aceita mais de um método concreto sem problemas.
Espero que não tirem isso…

Tem o conceito agora de Functional Interface, que é uma interface com apenas um método abstrato e que pode ser convertido em lambda.

Então uma Runnable da vida, por exemplo, não precisará mais de uma classe anônima.
Você poderá passar só uma lambda direto.
[/quote]Legal. [=
Pode ser então que ele estava falando sobre Functional Interface e eu me confundi.

My Bad. [=

Os default methods me parecem uma implementação desajeitada dos extension methods, do C#. E, pelo visto, tem as limitações da herança múltipla do C++. Me parece fugir completamente do conceito de interfaces prover implementações. O estranho é que, apesar de inserirem esses recursos extremamente complexos e sujeitos a erros, a comunidade ainda insiste em manter de fora da linguagem a sobrecarga de operadores. =(

Mas, respondendo ao colega. No caso, o seu uso de interfaces vazias parece correto. Entretanto, não há absolutamente nenhum método que suas interfaces tenham em comum e que tenham que ser implementados? Por exemplo, como a classe que usa as interfaces “conversa” com elas?

Me parece que tanto leitor quando dispositivo de armazenamento poderiam implementar uma interface chamada de “OrigemDeDados”. E essa interface poderia conter, no mínimo, o método “ler()” que traria dados de seja lá o que esses dispositivos representam (mais ou menos na mesma idéia dos InputStreams, do próprio Java). Outros métodos possíveis seriam “ligar()” e “desligar()”, que faria os dispositivos acionarem e desligarem.

Poxa, que discussão que essa dúvida já levantou! Muito legal! :slight_smile:

[quote=ViniGodoy]Mas, respondendo ao colega. No caso, o seu uso de interfaces vazias parece correto. Entretanto, não há absolutamente nenhum método que suas interfaces tenham em comum e que tenham que ser implementados? Por exemplo, como a classe que usa as interfaces “conversa” com elas?

Me parece que tanto leitor quando dispositivo de armazenamento poderiam implementar uma interface chamada de “OrigemDeDados”. E essa interface poderia conter, no mínimo, o método “ler()” que traria dados de seja lá o que esses dispositivos representam (mais ou menos na mesma idéia dos InputStreams, do próprio Java). Outros métodos possíveis seriam “ligar()” e “desligar()”, que faria os dispositivos acionarem e desligarem.[/quote]

Criei a interface vazia, e aliás eu estava com dois casos de interfaces vazias. Já estava até levantando os argumentos pra me defender com o professor na entrega do trabalho.

Porém, ao longo do desenvolvimento (fiquei até as 4 da manhã dessa noite trabalhando nisso :stuck_out_tongue: ) consegui comportamentos em comum entre as classes que implementam essas interfaces.

No fim das contas, tanto faz se eu usar classes abstratas ou interfaces, se fosse pra ficar vazio seria melhor que fosse uma classe abstrata (tenho algumas ocorrências de classes abstratas vazias no projeto) ao invés de interface, acho que fica menos “horrendo”.

Estou montando o diagrama UML, logo edito o post e coloco ele aqui pra vocês darem uma olhada. Foi minha primera experiência em projetar um pequeno sistema, creio que minha estrutura de classes não é a mais correta, mas com a prática que tenho até então vejo que ficou muito bom. :slight_smile:

Obrigado a todos que deram suas opiniões e observações! :wink:

Não. Definitivamente as interfaces ficam menos horrendas do que classes abstratas. As classes abstratas representam um conceito forte, de tipos. Na evolução do sistema, elas também criariam compromissos com a implementação - enquanto a interface representa única e exclusivamente um compromisso com comportamento. Por isso é possível implementar mais de uma interface numa classe.

Pensando por esse lado conceitual você tem razão mesmo.

Estou terminando e logo vou postar o UML. :slight_smile:

Segue o diagrama! Ainda é uma versão beta, quase todas as funcionalidades estão implementadas. Estamos implementando a interface gráfica agora e pode ter alguma alteração, mas a base é essa. :slight_smile:

Por que existe na classe computador os métodos criarPortaParalela e criarPortaUSB? Não era melhor ter só um addPorta que recebe a classe mãe Porta? Assim, você não precisará alterar essa classe caso um novo tipo de porta surja.

No caso da pasta e dos arquivos, considere a possibilidade de implementar o padrão de projetos Composite.

De qualquer forma, ficou bem legal. Dá para ver que você se empenhou muito. Parabéns. :slight_smile:

[quote=ViniGodoy]Por que existe na classe computador os métodos criarPortaParalela e criarPortaUSB? Não era melhor ter só um addPorta que recebe a classe mãe Porta? Assim, você não precisará alterar essa classe caso um novo tipo de porta surja.

No caso da pasta e dos arquivos, considere a possibilidade de implementar o padrão de projetos Composite.

De qualquer forma, ficou bem legal. Dá para ver que você se empenhou muito. Parabéns. :)[/quote]

Você tem razão, a responsabilidade de criar portas não é do Computador. Já estou conversando com meu colega, e acho que vamos tirar estes métodos dali. Obrigado pela dica!

Quanto ao Composite, eu não conhecia, aliás faz tempo que adio meus estudos em Design Patterns. :stuck_out_tongue:

Estou lendo sobre ele aqui e tentarei implementar(se der tempo!).

Agradeço novamente! :slight_smile: