Uma das melhores características do Scala são os traços. Mixins para Ruby peeps. Interfaces com implementações para peeps Java. Uma classe pode implementar quantas características você quiser e, portanto, é muito fácil compor uma classe de características reutilizáveis (empilhadas para serem técnicas), e esse é o poder por trás disso. No entanto, à medida que um sistema se torna maior, tenho visto características causarem problemas em algumas áreas. E aqui está minha opinião sobre por que e como evitá-lo.
É muito tentador criar o máximo possível de seu código como características. Faça com que os traços estendam os traços. Afinal, isso significa que mais seu código é, por definição, reutilizável, certo? Simplesmente crie uma nova classe que combina todas as funcionalidades de que você precisa, substitua um valor aqui e ali. Em princípio, isso é válido e é usado para reduzir a duplicação de código a quase zero. Isso é usado com grande efeito na biblioteca de coleção do scala.
No entanto, um antipadrão específico em relação às características é o que chamo de padrão de salada .
No padrão de bolo familiar , temos ingredientes (características) que são colocados em camadas para formar um todo coerente. Ninguém nunca comeu um bolo e ficou confuso onde termina o chocolate e começa a geléia.
Mas no padrão da salada , todos os tipos de métodos e variáveis são jogados em uma tigela gigante. A maionese se espalha pelo foguete. Você não consegue encontrar os pimentões picados para toda a alface. Em uma salada ruim, você não pode determinar facilmente os ingredientes individuais.
Quando os desenvolvedores ficam loucos por características, logo existem características compostas por características compostas por características. Características que fornecem um único método abstrato com outra característica de implementação única. Traços usados para fornecer escopo fácil para dependências em vez de importações e variáveis. Eu vi classes que quando linearizadas têm 50 ou mais ancestrais (eu realmente tenho).
Isso leva ao que diabos essa classe faz . Traçar um gráfico cíclico de dependências para entender uma classe pode rapidamente se tornar um derretimento cerebral. Especialmente quando uma classe acaba se misturando na mesma característica repetidamente devido à herança de características.
Ao orientar desenvolvedores juniores no Scala, tento fornecer algumas regras simples que, creio, são uma boa regra prática. É claro que as regras devem ser quebradas e é fácil encontrar contra-exemplos, mas descobri que esses fornecem um bom ponto de partida.
Um traço geralmente fornece um relacionamento é-um, posso fazer ou algo semelhante. Se então é porque um pato pode nadar e um pato pode grasnar. A porque List é um Traversable. A funcionalidade adicionada pelo traço faz parte do todo e se ajusta naturalmente ao objeto composto.
Duck extends Swimming with Quacking
List extends Traversable
Evite características como um atalho de dependência. Se seu TimelineService precisa de acesso a métodos de um cliente HTTP, não misture no cliente. Mixin uma dependência sobre o cliente. parâmetros do construtor, membros abstratos ou tipos próprios com o padrão de bolo. Uma exceção óbvia são as DSLs, como as características de teste Scala.
Evite traços de granulação muito fina. Quando há muitas engrenagens pequenas compostas juntas, torna-se difícil racionalizar todas as engrenagens como um todo. Eu disse anteriormente que a API Scala Collection é excelente. E isso porque é fácil de usar e extremamente poderoso. Mas não é fácil decifrar internamente. É uma troca decente para um SDK principal, mas provavelmente não para o seu código.
Se uma classe herda a mesma característica por meio de muitos pais, vale a pena investigar se declarar tipos próprios para os pais é um relacionamento mais apropriado.
Resumo apenas quando útil. Isso não é Java. É aceitável ter serviços sem interfaces (características abstratas). Se você se pegar nomeando as coisas como Authorization e AuthorizationImpl, isso é uma boa indicação de que é um exagero. Caso contrário, a nomenclatura é óbvia porque é baseada na distinção. Por exemplo, com SearchIndex, ElasticsearchSearchIndex e SolrSearchIndex, fica imediatamente claro por que existe um traço comum.
Lembre-se: ninguém gosta de salada .