Functional Mixins, como utilizar
Hoje estarei falando um pouco sobre esse padrão de composição chamado Functional Mixins e dando alguns exemplos para mostrar como ele é a ferramenta perfeita para adicionar comportamentos aos seus objetos com total flexibilidade.
O Functional Mixin é um padrão voltado para a composição de objetos.
Ele foi criado para evitar a complexidade e a rigidez da herança clássica (evitando que seus objetos herdem coisas que não precisam).
Combinado com Factories, ele permite que você crie pequenos blocos de funcionalidades reutilizáveis e os adicione sob demanda.
O problema com a herança
Antes de irmos para o código, vale lembrar o principal motivo para usarmos Mixins. Quando usamos herança de classes tradicional, costumamos sofrer com um problema clássico: você quer apenas uma funcionalidade pequena, mas acaba herdando todo o escopo do objeto pai.
É famosa a frase no mundo do desenvolvimento: "O problema da herança é que você quer uma banana, mas recebe um gorila segurando a banana e toda a selva junto".
A solução para isso é a composição. E é aí que os Functional Mixins brilham.
Como implementar Functional Mixins em javascript
Se você leu meu post anterior sobre Factory Design Pattern, você já viu um pequeno spoiler de como podemos utilizar um objeto simples e o Object.assign para transferir métodos para um novo animal.
Aquela abordagem já é uma forma de aplicar Mixins. Mas podemos levar isso além utilizando funções (o que traz o "Functional" para o nome). Um Functional Mixin é, essencialmente, uma função que recebe um objeto alvo e retorna esse mesmo objeto enriquecido com novas propriedades ou métodos.
Vamos criar um Mixin em formato de função que adiciona a capacidade de algo "se conectar ao Wi-fi":
// Criamos uma função que recebe parâmetros de configuração (opcional)
// e retorna uma função que aplicará as mudanças no nosso objeto
const comConexaoWifi = (conectadoPorPadrao = false) => {
return function (alvo) {
return Object.assign(alvo, {
conectado: conectadoPorPadrao,
conectar() {
this.conectado = true;
console.log(`${this.nome} conectou à rede.`);
},
desconectar() {
this.conectado = false;
console.log(`${this.nome} desconectou da rede.`);
}
});
};
};Agora, vamos criar uma Factory bem simples de um eletrônico e ver como aplicar o Mixin nela:
const EletronicoFactory = function (nome) {
const eletronico = { nome };
eletronico.ligar = function () {
console.log(`${this.nome} está ligado.`);
};
return eletronico;
};Se quisermos criar um eletrônico que seja inteligente e tenha Wi-Fi, aplicamos o Mixin diretamente no objeto recém-criado:
const tv = EletronicoFactory("Smart TV");
// Aplicando o Mixin no objeto tv
comConexaoWifi()(tv);
tv.ligar(); // Smart TV está ligado.
tv.conectar(); // Smart TV conectou à rede.O mais legal dessa estrutura de função é que ela nos permite criar o famoso pipeline de composição.
Combinando múltiplos Mixins em suas Factories
Imagine que no nosso sistema nós temos objetos variados. Uma geladeira que também tem Wi-Fi, ou talvez um dispositivo que tenha Bluetooth. Podemos criar outro Mixin para o Bluetooth:
const comBluetooth = () => (alvo) => {
return Object.assign(alvo, {
parearBluetooth() {
console.log(`${this.nome} está pareando via Bluetooth...`);
}
});
};Agora, caso a gente queira construir uma Factory que gere um objeto altamente complexo, aplicando vários Mixins de forma organizada, podemos fazer assim:
const SmartphoneFactory = function (nome) {
// Criamos o objeto base
const celularBase = EletronicoFactory(nome);
// Compondo o objeto com vários comportamentos
comConexaoWifi(true)(celularBase);
comBluetooth()(celularBase);
return celularBase;
};
const meuCelular = SmartphoneFactory("Meu Smartphone");
meuCelular.ligar(); // Meu Smartphone está ligado.
meuCelular.conectar(); // Meu Smartphone conectou à rede.
meuCelular.parearBluetooth(); // Meu Smartphone está pareando via Bluetooth...Percebe como a arquitetura fica limpa? O SmartphoneFactory não precisou saber como a conexão Wi-Fi ou o Bluetooth funcionam. Ele simplesmente pegou esses comportamentos de nossos Mixins e adicionou ao objeto base.
Conclusões finais
Com os Functional Mixins ficou bem mais simples compartilhar comportamentos independentes. Juntando eles com as Factories, a gente consegue montar objetos exatamente com as peças (métodos) que eles precisam, sem forçar estruturas engessadas.
Ao meu ver a principal vantagem dos Functional Mixins é a flexibilidade gigantesca que a composição traz para o nosso código, isolando as responsabilidades de forma clara e tornando muito mais fácil a manutenção.
Espero que você tenha aproveitado alguma coisa do conteúdo apresentado.
Aqui no blog tem posts falando sobre outros Design Patterns, da uma olhada.
Acho que é isso, se tiver ficado com alguma dúvida ou tenha alguma sugestão escreve aqui nos comentários.