A existência de muitos métodos duplicados e hierarquias de herança profunda em APIs da GUI (Java Swing, Android SDK) viola os princípios de design do SOLID?

5

Para aqueles que não estão familiarizados com os princípios do SOLID, você pode começar a ler aqui: Artigo da Wikipédia . No entanto, a maior parte do meu entendimento vem de: link

Independentemente disso, parece que toda interface GUI que encontro em Java (mesmo a do Adobe Flex honestamente) é preenchida com dezenas de métodos que são duplicados em muitas classes. Hierarquias de herança profundas são a norma e a composição definitivamente não é preferida à herança . Esta parece ser uma clara violação do SRP e ISP .

O que eu gostaria de saber é, estou correto em pensar que essas estruturas estão violando os princípios de design do SOLID? Se sim, então por quê?

Resposta ao boneco de neve

Eu tenho que discordar que o SWING é um exemplo de SOLID. Vamos usar o exemplo JButton:

1) SRP: O método isDoubleBuffered vai além da responsabilidade do JButton para representar um botão de UI. Esse método representa um detalhe de implementação que não tem nada a ver com o conceito abstrato de um botão de interface do usuário.

2) OCP: Se eu quiser mudar como o JButton lida com os eventos, eu tenho que substituir a classe JButton completamente.

3) LSP: A implementação do JButton não pode ser facilmente substituída por outras implementações. O JButton herda a maioria dos detalhes de implementação diretamente. Se eu quiser mudar a forma como um JButton cria dicas de ferramentas, atribui ouvintes de ação ou acessa o contexto gráfico, eu tenho que jogar fora todo o SWING. Eu não posso simplesmente incluir uma nova classe que manipule essas coisas, já que o JButton herda diretamente do JComponent.

4) ISP: A interface pública do JButton está cheia de métodos de cada classe da qual ela herda. É litterally um grupo de dumping de métodos públicos de JComponent, Container, Compound, etc. Se a interface do JButton aderisse ao ISP, haveria apenas 10 ou mais métodos publicamente expostos.

5) DIP: O JButton expõe publicamente os métodos de implementação concretos. Se fosse aplicada uma verdadeira inversão de dependência, coisas como ícones, layout, manipulação de eventos, etc. seriam delegadas a classes separadas que o usuário poderia facilmente trocar para obter novas funcionalidades.

    
por Bernard Igiri 09.10.2014 / 15:25
fonte

2 respostas

3

Em geral, eu não concordo que as estruturas da GUI violem SOLID . Pode haver exceções, mas isso encerra minha experiência com vários frameworks:

  • SRP: uma classe de GUI geralmente tem uma responsabilidade. Talvez esteja respondendo a um evento, renderizando um objeto ou talvez "represente esse elemento da interface do usuário". Esse último pode parecer que não é uma responsabilidade "única", porque essa responsabilidade é unir tudo (por exemplo, JButton ou JPanel ). A chave para procurar é esse objeto "grande", muitas vezes delegar a outros objetos que são especializados e lidar com um aspecto do objeto em detalhe (manipulação de eventos, renderização, etc).

  • Aberto / fechado: seu exemplo mostra como as classes de interface do usuário estão definitivamente abertas para extensão: há muita herança acontecendo. Pode-se argumentar que as classes de interface do usuário violam o aspecto "fechado para modificação", uma vez que são tipicamente muito configuráveis e podem ser modificadas para fazer muitas coisas de maneira diferente, mas é um objetivo dessas classes.

  • Substituição Liskov: em praticamente todas as estruturas que vi, as subclasses adicionam mais funcionalidade e ainda permitem o comportamento da classe pai. Nem sempre faz sentido, mas é suportado. Por exemplo, você poderia aninhar elementos da interface do usuário dentro de um botão se estendesse um quadro (talvez um ícone pudesse ser adicionado a um botão, um elemento de imagem aninhado dentro de um botão apesar do "botão" não ser considerado como um elemento pai ). Esse princípio é fundamental para a natureza aninhada de estruturas de interface do usuário.

  • Segregação de interface: minha principal experiência aqui é com Java, onde existem muitas pequenas interfaces para vários ouvintes e manipuladores. Parece um bom ajuste.

  • Inversão de dependência: junto com o argumento de Liskov, muitos frameworks dependem de abstrações. Por exemplo, pode haver alguma interface de alto nível ou classe abstrata para representar todos os componentes da interface do usuário. Os componentes pai podem aceitar qualquer componente de alto nível como filho, o que permite layouts arbitrariamente complexos por meio de aninhamento.

No geral, acho que há um argumento strong para estruturas de GUI que aderem ao SOLID.

    
por 09.10.2014 / 20:10
fonte
2

Em geral, os princípios do SOLID geralmente são cumpridos (veja resposta do Snowman )

Mas desde que você mencionou o SDK do Android, vou detalhar um pouco como os princípios do SOLID são muito curtos nessa API específica.

Princípio da responsabilidade única . A classe View é responsável por

  • posicionamento
  • desenho
  • processamento de eventos
  • lidando com uma enorme carga de outros retornos de chamada.

Portanto, se você considerar o SRP como "Uma classe deve ter apenas um motivo para mudar" - você já perdeu o argumento. Eles tentaram criar ViewGroup e fazer o ListView usar Adapter para os dados reais. Essas coisas realmente funcionam muito bem.

Princípio aberto fechado . Embora basicamente aberto para extensão, infelizmente somente via herança. Não há como modificar o comportamento (por exemplo, algo no processo de desenho ou layout) via padrão de estratégia ou algo similar. Tudo é uma constante, então você tem que escolher entre as opções existentes.

O princípio da substituição de Liskov é, obviamente, cumprido através dos requisitos da linguagem java. No entanto, lembro-me de que tivemos problemas com TextView ou EditText no passado, se o método herdado retornou o mesmo tipo e recebeu o mesmo nome; comportou-se de maneira diferente. A pós-condição não foi cumprida. Pena que não me lembro exatamente o que era. Algo com o Focus ou mesmas propriedades tendo resultados diferentes ou algo assim.

Segregação de interface . O que devo dizer? A interface básica View está poluída com métodos sobre métodos. Por que a rolagem é implementada (e acessada) por meio da classe View ? Lembre-se dos callbacks que eu mencionei antes? Mas, em geral, se você está tentando "implementar" a interface "View" (estendendo a classe), você só precisa substituir os métodos que realmente precisa - mas isso é mais como um recurso de linguagem em java. Se você vê-lo mais pedantemente, você vai realmente implementar a rolagem, ignorando a classe, mesmo que você não precise dela.

Inversão de dependência parece estar ok no android. Mas tenho certeza de que, se você realmente procurar por ele, encontrará pontos suficientes na estrutura em que algo depende de uma implementação em vez de uma abstração. Não tenho certeza se as chamadas de 34 instanceof na classe View são um sinal de boas abstrações.

Em geral, o android sdk tenta o seu melhor cumprimento de todos os princípios. Também sei que é bastante grande e também precisa levar em conta o desempenho, por isso é mais difícil cumprir todos os princípios.

Devo acrescentar que provavelmente não conseguiria fazer nada melhor.

    
por 09.10.2014 / 23:08
fonte