paint-brush
Domine princípios SÓLIDOS como a palma da sua mão em apenas 8 minutos!por@arulvalananto
26,247 leituras
26,247 leituras

Domine princípios SÓLIDOS como a palma da sua mão em apenas 8 minutos!

por Arul Valan Anto11m2023/07/07
Read on Terminal Reader

Muito longo; Para ler

Neste blog, demonstrarei a implementação dos princípios SOLID em um aplicativo React. No final deste artigo, você compreenderá totalmente os princípios do SOLID.
featured image - Domine princípios SÓLIDOS como a palma da sua mão em apenas 8 minutos!
Arul Valan Anto HackerNoon profile picture
0-item

Neste blog, demonstrarei a implementação dos princípios SOLID em um aplicativo React. No final deste artigo, você compreenderá totalmente os princípios do SOLID. Antes de começarmos, deixe-me fazer uma breve introdução a esses princípios.

O que são princípios SOLID?

Os princípios SOLID são cinco princípios de design que nos ajudam a manter nosso aplicativo reutilizável, sustentável, escalável e fracamente acoplado. Os princípios SOLID são:


  • Princípio da responsabilidade única
  • Princípio Aberto-Fechado
  • Princípio de substituição de Liskov
  • Princípio de segregação de interface
  • Princípio de inversão de dependência


Ok, vamos examinar cada um desses princípios individualmente. Eu uso o React como exemplo, mas os conceitos principais são semelhantes a outras linguagens de programação e estruturas.

Princípio da responsabilidade única

“Um módulo deve ser responsável por um, e apenas um, ator.” - Wikipédia.


O Princípio da Responsabilidade Única afirma que um componente deve ter um propósito ou responsabilidade claro. Ele deve se concentrar em funcionalidades ou comportamentos específicos e evitar assumir tarefas não relacionadas. Seguir o SRP torna os componentes mais focados, modulares e facilmente compreendidos e modificados. Vamos ver a implementação real.


 // ❌ Bad Practice: Component with Multiple Responsibilities const Products = () => { return ( <div className="products"> {products.map((product) => ( <div key={product?.id} className="product"> <h3>{product?.name}</h3> <p>${product?.price}</p> </div> ))} </div> ); };


No exemplo acima, o componente Products viola o Princípio de Responsabilidade Única ao assumir múltiplas responsabilidades. Ele gerencia a iteração de produtos e lida com a renderização da interface do usuário para cada produto. Isso pode tornar o componente difícil de entender, manter e testar no futuro.

Em vez disso, faça isso para aderir ao SRP:

 // ✅ Good Practice: Separating Responsibilities into Smaller Components import Product from './Product'; import products from '../../data/products.json'; const Products = () => { return ( <div className="products"> {products.map((product) => ( <Product key={product?.id} product={product} /> ))} </div> ); }; // Product.js // Separate component responsible for rendering the product details const Product = ({ product }) => { return ( <div className="product"> <h3>{product?.name}</h3> <p>${product?.price}</p> </div> ); };


Essa separação garante que cada componente tenha uma única responsabilidade, tornando-os mais fáceis de entender, testar e manter.

Princípio Aberto-Fechado

“entidades de software (classes, módulos, funções, etc.) devem ser abertas para extensão, mas fechadas para modificação.” - Wikipédia.


O Princípio Aberto-Fechado enfatiza que os componentes devem ser abertos para extensão (podem adicionar novos comportamentos ou funcionalidades), mas fechados para modificação (o código existente deve permanecer inalterado). Esse princípio incentiva a criação de código resiliente a mudanças, modular e de fácil manutenção. Vamos ver a implementação real.


 // ❌ Bad Practice: Violating the Open-Closed Principle // Button.js // Existing Button component const Button = ({ text, onClick }) => { return ( <button onClick={onClick}> {text} </button> ); } // Button.js // Modified Existing Button component with additional icon prop (modification) const Button = ({ text, onClick, icon }) => { return ( <button onClick={onClick}> <i className={icon} /> <span>{text}</span> </button> ); } // Home.js // 👇 Avoid: Modified existing component prop const Home = () => { const handleClick= () => {}; return ( <div> {/* ❌ Avoid this */} <Button text="Submit" onClick={handleClick} icon="fas fa-arrow-right" /> </div> ); }


No exemplo acima, modificamos o componente Button existente adicionando uma propriedade icon . Alterar um componente existente para acomodar novos requisitos viola o Princípio Aberto-Fechado. Essas mudanças tornam o componente mais frágil e introduzem o risco de efeitos colaterais não intencionais quando usados em diferentes contextos.

Em vez disso, faça o seguinte:

 // ✅ Good Practice: Open-Closed Principle // Button.js // Existing Button functional component const Button = ({ text, onClick }) => { return ( <button onClick={onClick}> {text} </button> ); } // IconButton.js // IconButton component // ✅ Good: You have not modified anything here. const IconButton = ({ text, icon, onClick }) => { return ( <button onClick={onClick}> <i className={icon} /> <span>{text}</span> </button> ); } const Home = () => { const handleClick = () => { // Handle button click event } return ( <div> <Button text="Submit" onClick={handleClick} /> {/* <IconButton text="Submit" icon="fas fa-heart" onClick={handleClick} /> </div> ); }


No exemplo acima, criamos um componente funcional IconButton separado. O componente IconButton encapsula a renderização de um botão de ícone sem modificar o componente Button existente. Ele adere ao Princípio Aberto-Fechado, estendendo a funcionalidade por meio da composição em vez da modificação.

Princípio da Substituição de Liskov

“Objetos de subtipo devem ser substituíveis por objetos de supertipo” — Wikipedia.


O Princípio de Substituição de Liskov (LSP) é um princípio fundamental da programação orientada a objetos que enfatiza a necessidade de substituição de objetos dentro de uma hierarquia. No contexto dos componentes React, o LSP promove a ideia de que os componentes derivados devem ser capazes de substituir seus componentes básicos sem afetar a correção ou o comportamento do aplicativo. Vamos ver a implementação real.


 // ⚠️ Bad Practice // This approach violates the Liskov Substitution Principle as it modifies // the behavior of the derived component, potentially resulting in unforeseen // problems when substituting it for the base Select component. const BadCustomSelect = ({ value, iconClassName, handleChange }) => { return ( <div> <i className={iconClassName}></i> <select value={value} onChange={handleChange}> <options value={1}>One</options> <options value={2}>Two</options> <options value={3}>Three</options> </select> </div> ); }; const LiskovSubstitutionPrinciple = () => { const [value, setValue] = useState(1); const handleChange = (event) => { setValue(event.target.value); }; return ( <div> {/** ❌ Avoid this */} {/** Below Custom Select doesn't have the characteristics of base `select` element */} <BadCustomSelect value={value} handleChange={handleChange} /> </div> );


No exemplo acima, temos um componente BadCustomSelect destinado a servir como uma entrada de seleção personalizada no React. No entanto, ele viola o Princípio de Substituição de Liskov (LSP) porque restringe o comportamento do elemento select base.

Em vez disso, faça o seguinte:

 // ✅ Good Practice // This component follows the Liskov Substitution Principle and allows the use of select's characteristics. const CustomSelect = ({ value, iconClassName, handleChange, ...props }) => { return ( <div> <i className={iconClassName}></i> <select value={value} onChange={handleChange} {...props}> <options value={1}>One</options> <options value={2}>Two</options> <options value={3}>Three</options> </select> </div> ); }; const LiskovSubstitutionPrinciple = () => { const [value, setValue] = useState(1); const handleChange = (event) => { setValue(event.target.value); }; return ( <div> {/* ✅ This CustomSelect component follows the Liskov Substitution Principle */} <CustomSelect value={value} handleChange={handleChange} defaultValue={1} /> </div> ); };


No código revisado, temos um componente CustomSelect destinado a estender a funcionalidade do elemento select padrão no React. O componente aceita props como value , iconClassName , handleChange e props adicionais usando o operador spread ...props . Ao permitir o uso das características do elemento select e aceitar props adicionais, o componente CustomSelect segue o Princípio de Substituição de Liskov (LSP).

Princípio de Segregação de Interface

“Nenhum código deve ser forçado a depender de métodos que não usa.” - Wikipédia.


O Princípio de Segregação de Interface (ISP) sugere que as interfaces devem ser focadas e adaptadas aos requisitos específicos do cliente, em vez de serem excessivamente amplas e forçar os clientes a implementar funcionalidades desnecessárias. Vamos ver a implementação real.


 // ❌ Avoid: disclose unnecessary information for this component // This introduces unnecessary dependencies and complexity for the component const ProductThumbnailURL = ({ product }) => { return ( <div> <img src={product.imageURL} alt={product.name} /> </div> ); }; // ❌ Bad Practice const Products = ({ product }) => { return ( <div> <ProductThumbnailURL product={product} /> <h4>{product?.name}</h4> <p>{product?.description}</p> <p>{product?.price}</p> </div> ); }; const Products = () => { return ( <div> {products.map((product) => ( <Product key={product.id} product={product} /> ))} </div> ); }


No exemplo acima, passamos todos os detalhes do produto para o componente ProductThumbnailURL , mesmo que isso não seja necessário. Acrescenta riscos e complexidade desnecessários ao componente e viola o Princípio de Segregação de Interface (ISP).

Vamos refatorar para aderir ao ISP:

 // ✅ Good: reducing unnecessary dependencies and making // the codebase more maintainable and scalable. const ProductThumbnailURL = ({ imageURL, alt }) => { return ( <div> <img src={imageURL} alt={alt} /> </div> ); }; // ✅ Good Practice const Products = ({ product }) => { return ( <div> <ProductThumbnailURL imageURL={product.imageURL} alt={product.name} /> <h4>{product?.name}</h4> <p>{product?.description}</p> <p>{product?.price}</p> </div> ); }; const Products = () => { return ( <div> {products.map((product) => ( <Product key={product.id} product={product} /> ))} </div> ); };


No código revisado, o componente ProductThumbnailURL recebe apenas as informações necessárias em vez de todos os detalhes do produto. Previne riscos desnecessários e promove o Princípio de Segregação de Interface (ISP).

Princípio de Inversão de Dependência

“Uma entidade deve depender de abstrações, não de concreções” – Wikipedia.


O Princípio de Inversão de Dependência (DIP) enfatiza que componentes de alto nível não devem depender de componentes de baixo nível. Este princípio promove acoplamento fraco e modularidade e facilita a manutenção de sistemas de software. Vamos ver a implementação real.


 // ❌ Bad Practice // This component follows concretion instead of abstraction and // breaks Dependency Inversion Principle const CustomForm = ({ children }) => { const handleSubmit = () => { // submit operations }; return <form onSubmit={handleSubmit}>{children}</form>; }; const DependencyInversionPrinciple = () => { const [email, setEmail] = useState(); const handleChange = (event) => { setEmail(event.target.value); }; const handleFormSubmit = (event) => { // submit business logic here }; return ( <div> {/** ❌ Avoid: tightly coupled and hard to change */} <BadCustomForm> <input type="email" value={email} onChange={handleChange} name="email" /> </BadCustomForm> </div> ); };


O componente CustomForm é fortemente acoplado a seus filhos, impedindo a flexibilidade e dificultando a alteração ou extensão de seu comportamento.

Em vez disso, faça o seguinte:

 // ✅ Good Practice // This component follows abstraction and promotes Dependency Inversion Principle const AbstractForm = ({ children, onSubmit }) => { const handleSubmit = (event) => { event.preventDefault(); onSubmit(); }; return <form onSubmit={handleSubmit}>{children}</form>; }; const DependencyInversionPrinciple = () => { const [email, setEmail] = useState(); const handleChange = (event) => { setEmail(event.target.value); }; const handleFormSubmit = () => { // submit business logic here }; return ( <div> {/** ✅ Use the abstraction instead */} <AbstractForm onSubmit={handleFormSubmit}> <input type="email" value={email} onChange={handleChange} name="email" /> <button type="submit">Submit</button> </AbstractForm> </div> ); };


No código revisado, introduzimos o componente AbstractForm , que atua como uma abstração para o formulário. Ele recebe a função onSubmit como prop e manipula o envio do formulário. Essa abordagem nos permite trocar ou estender facilmente o comportamento do formulário sem modificar o componente de nível superior.

Conclusão

Os princípios SOLID fornecem diretrizes que capacitam os desenvolvedores a criar soluções de software bem projetadas, sustentáveis e extensíveis. Aderindo a esses princípios, os desenvolvedores podem obter modularidade, reutilização de código, flexibilidade e complexidade de código reduzida.


Espero que este blog tenha fornecido informações valiosas e inspirado você a aplicar esses princípios em seus projetos React existentes ou futuros.


Fique curioso; continue codificando!


Referência:


Publicado também aqui .