Escrevendo um código React melhor com a separação do princípio de comando e consulta
Um dos princípios que uso constantemente em meu código é uma forma especial ou um uso concreto do Princípio de Responsabilidade Única em seu núcleo. Este princípio é o Princípio de Separação de Comando e Consulta .
A separação de comando e princípio de consulta é um princípio de design de software que sugere que métodos ou funções devem ser comandos que modificam o estado do sistema ou consultas que retornam informações sobre o estado do sistema, mas não ambos.
Comandos (ou modificadores) são métodos que executam uma ação ou alteram o estado de um objeto sem retornar um valor. As consultas, por outro lado, são métodos que alteram o estado de um objeto. A separação de comandos e consultas pode ajudar a reduzir o acoplamento entre os componentes, facilitando o teste, a manutenção e a modificação do código. Também torna mais fácil raciocinar sobre o comportamento do código e pode melhorar o design geral de um sistema.
O código abaixo é considerado ruim porque addItem
está removeItem
fazendo mais de uma coisa: alterando os dados, atualizando o preço total e retornando o valor atualizado.
class ShoppingCart {
constructor() {
this.items = [];
this.totalPrice = 0;
}
addItem(item) {
this.items.push(item);
this.updateTotalPrice();
return this.totalPrice;
}
removeItem(item) {
const index = this.items.indexOf(item);
if (index > -1) {
this.items.splice(index, 1);
this.updateTotalPrice();
}
return this.totalPrice;
}
updateTotalPrice() {
for (let i = 0; i < items.length; i++) {
this.totalPrice += items[i].price;
}
}
}
class ShoppingCart {
constructor() {
this.items = [];
this.totalPrice = 0;
}
addItem(item) {
this.items.push(item);
}
removeItem(item) {
const index = this.items.findIndex((item) => item.id === id);
if (index > -1) {
this.items.splice(index, 1);
}
}
get totalPrice () {
this.items.reduce((total, item) => total + item.price, 0)
}
}
Em um aplicativo React, podemos separar modificadores e consultas usando uma biblioteca de gerenciamento de estado como Redux ou React Context API. Isso nos permite separar a lógica para modificar o estado do aplicativo da lógica para renderizar a interface do usuário.
Para aprender mais sobre Context e outros hooks, você pode baixar uma folha de dicas aqui contendo os hooks React mais comuns com exemplos e ilustrações.
Este princípio de Separação de Responsabilidade de Comando-Consulta pode ser aplicado em diferentes níveis. No nível da arquitetura, você já deve ter ouvido falar do CQRS nesse contexto. Essencialmente, eles são o mesmo princípio aplicado em diferentes níveis de abstração.
Exemplo: carrinho de compras
Um aplicativo de carrinho de compras pode ter uma ShoppingCart
classe que gerencia os itens do carrinho e calcula o preço total. Os métodos addItem
e removeItem
da ShoppingCart
classe são comandos que modificam os itens do carrinho, enquanto o getTotalPrice
método é uma consulta que retorna o preço total dos itens do carrinho.
Antes de aplicar o Princípio de Separação de Comando e Consulta, os métodos addItem
e removeItem
também podem atualizar a propriedade de preço total diretamente. No entanto, isso pode tornar o código mais difícil de entender e manter.
Aqui está um exemplo de um componente React que lida com um carrinho de compras:
import { useState, useMemo } from 'react';
function ShoppingCart() {
const [cart, setCart] = useState([]);
// Command: Add item to cart
function addItemToCart(item) {
setCart([...cart, item]);
}
// Command: Remove item from cart
function removeItemFromCart(id) {
setCart(cart.filter((item) => item.id !== id));
}
// Query: Calculate total price of items in cart
const totalPrice = useMemo(() => {
return cart.reduce((total, item) => total + item.price, 0);
}, [cart]);
return (
<div>
<h2>Shopping Cart</h2>
<ul>
{cart.map((item) => (
<li key={item.id}>
{item.name} - {item.price}
<button onClick={() => removeItemFromCart(item.id)}>Remove</button>
</li>
))}
</ul>
<p>Total Price: {totalPrice}</p>
</div>
);
}
Benefícios da separação comando-consulta
A separação de comandos e consultas em seu código pode fornecer os seguintes benefícios:
- Clareza: Ao separar comandos e consultas, seu código fica mais fácil de ler e entender, pois fica mais fácil determinar o que cada método faz e o que retorna.
- Modularidade: separar comandos e consultas pode tornar seu código mais modular e reutilizável, pois comandos e consultas podem ser usados de forma independente.
- Testabilidade: separar comandos e consultas pode facilitar o teste do seu código, pois isolar e testar métodos individuais é mais fácil.
- Complexidade reduzida: separar comandos e consultas pode ajudar a reduzir a complexidade do seu código, pois o incentiva a dividir operações complexas em partes menores e mais gerenciáveis.
Por outro lado, o princípio introduz mais uma abstração extra no código que pode tornar a depuração um pouco mais difícil. Usar um mecanismo de pub-sub ou ouvinte de eventos para separar comandos e consultas pode dificultar a depuração e o raciocínio sobre o comportamento do código, especialmente quando o site de chamada está longe dos dados que ele modifica.
Usar uma arquitetura bem definida e um estilo de codificação consistente é essencial para manter o código organizado e de fácil manutenção. Também é importante usar nomes claros e descritivos para funções e variáveis para tornar seu propósito e comportamento mais compreensíveis.
Outra abordagem para separar comandos e consultas em um aplicativo React é usar uma biblioteca de gerenciamento de estado como Redux ou MobX. Essas bibliotecas fornecem um armazenamento centralizado para o estado do aplicativo e oferecem uma maneira clara e estruturada de modificar e acessar o estado de diferentes partes do aplicativo.
No Redux, por exemplo, podemos definir ações como comandos que descrevem uma mudança de estado e redutores como consultas que lidam com essas ações e retornam um novo estado:
// Define actions
const addToCart = (item) => ({
type: 'ADD_TO_CART',
payload: item,
});
const removeItemFromCart = (id) => ({
type: 'REMOVE_FROM_CART',
payload: id,
});
// Define reducer
const cartReducer = (state = [], action) => {
switch (action.type) {
case 'ADD_TO_CART':
return [...state, action.payload];
case 'REMOVE_FROM_CART':
return state.filter((item) => item.id !== action.payload);
default:
return state;
}
};
Conclusão
A separação de comando e princípio de consulta é um princípio importante a ser considerado ao projetar software. A separação de comandos e consultas torna seu código mais modular, testável e fácil de entender. Aderindo a esse princípio, você pode escrever um código mais fácil de manter e menos sujeito a erros e bugs.
Em resumo, ao separar o código que modifica o estado do código que retorna informações sobre o estado, você pode criar um código mais modular e testável, mais fácil de entender e manter. Com uma separação clara entre comandos e consultas, você pode obter uma arquitetura de software mais sustentável e escalável.
Se você gosta da leitura, por favor, inscreva-se na minha lista de e-mail . Compartilho técnicas de código limpo e refatoração semanalmente em blogs , livros e vídeos .