Conceitos
Abstração
A Abstração nos conceitos da orientação a objeto é uma prática de definir apenas aspectos essenciais que uma classe deve possuir. As classes, devem por natureza, ser incompletas e imprecisas para que possamos modelar especificidades através de classes filhas. Assim surge o conceito de classes filhas, classes mães e herança.
Herança
Herança é a representação de relacionamento entre classes em que uma classe extende a outra de modo a herdar os comportamentos da classe mãe.
SOLID
SOLID é um acrônimo que representa cinco princípios fundamentais da programação orientada a objetos, propostos por Robert C. Martin - o uncle Bob. Aqui você pode ler mais sobre o artigo dele.
Esses princípios têm como objetivo melhorar a estrutura e a manutenção do código, tornando-o mais flexível, escalável e fácil de entender. Tais princípios auxiliam o programador a criar códigos mais organizados, dividindo responsabilidades, reduzindo dependências, simplificando o processo de refatoração e promovendo a reutilização do código.
Open/Closed Principle
O "O" do acrônimo significa "Open/Closed Principle". A frase que o uncle bob utilizou para definir esse princípio foi:
"Uma classe deve estar aberta para extensão, mas fechada para modificação"
Segundo o Princípio do Aberto/Fechado, devemos desenvolver uma aplicação garantindo que escreveremos classes ou módulos de maneira genérica de modo que sempre que sentir a necessidade de estender o comportamento da classe ou do objeto, você não precisará alterar a classe propriamente dita. Extensão aqui pode-se ler como adição ou alteração de procedimentos.
O objetivo é permitir a adição de novas funcionalidades sem a necessidade de alterar o código existente. Isso minimiza o risco de introduzir bugs e deixa o código mais manutenível.
Aplicação prática
Imagine que você tem uma classe DiscountCalculator que calcula descontos de produtos. Inicialmente, temos duas categorias de produtos: Electronics
e Clothing
. Vamos começar sem aplicar o OCP (Open/Closed Principle):
Java
class Product { private String name; private double price; public Product(String name, double price) { this.name = name; this.price = price; } public String getName() { return name; } public double getPrice() { return price; } } class DiscountCalculator { public double calculateDiscount(Product product) { if (product.getName().equals("Electronics")) { return product.getPrice() * 0.9; // 10% de desconto } else if (product.getName().equals("Clothing")) { return product.getPrice() * 0.8; // 20% de desconto } return product.getPrice(); } } public class Main { public static void main(String[] args) { Product electronics = new Product("Electronics", 100); Product clothing = new Product("Clothing", 50); DiscountCalculator calculator = new DiscountCalculator(); System.out.println(calculator.calculateDiscount(electronics)); // 90 System.out.println(calculator.calculateDiscount(clothing)); // 40 } }
Typescript
class Product { private _name: string; private _price: number; constructor(name: string, price: number) { this._name = name; this._price = price; } public get name() { return this.name }; public set name(value: string) { this.name = value }; public get price() { return this.price }; public set price(value: number) { this.price = value }; } class DiscountCalculator { public calculateDiscount(product: Product): number { if (product.name === 'Electronics') { return product.price * 0.9; // 10% de desconto } else if (product.name === 'Clothing') { return product.price * 0.8; // 20% de desconto } return product.price; } } const electronics = new Product('Electronics', 100); const clothing = new Product('Clothing', 50); const calculator = new DiscountCalculator(); console.log(calculator.calculateDiscount(electronics)); // 90 console.log(calculator.calculateDiscount(clothing)); // 40
Problemas ao Não Aplicar o OCP
Violação do encapsulamento: Toda vez que um novo tipo de produto precisar de um desconto diferente, será necessário modificar o método calculateDiscount, incluindo uma nova condicional no if
.
Dificuldade em manutenção: Se o método crescer com muitos if/else ou switch, ele se tornará difícil de manter e testar.
Risco de introdução de erros: Alterações no método podem introduzir bugs em outras partes do código que dependem desse método.
Como corrigir?
Agora, vamos aplicar o Open/Closed Principle refatorando o código para permitir a adição de novos tipos de descontos sem modificar o código existente.
Java
class Product { private String name; private double price; public Product(String name, double price) { this.name = name; this.price = price; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } } interface DiscountStrategy { double calculate(Product product); } class ElectronicsDiscount implements DiscountStrategy { @Override public double calculate(Product product) { return product.getPrice() * 0.9; // 10% de desconto } } class ClothingDiscount implements DiscountStrategy { @Override public double calculate(Product product) { return product.getPrice() * 0.8; // 20% de desconto } } class NoDiscount implements DiscountStrategy { @Override public double calculate(Product product) { return product.getPrice(); } } class DiscountCalculator { private DiscountStrategy discountStrategy; public DiscountCalculator(DiscountStrategy discountStrategy) { this.discountStrategy = discountStrategy; } public double calculateDiscount(Product product) { return discountStrategy.calculate(product); } } public class Main { public static void main(String[] args) { Product electronics = new Product("Electronics", 100); Product clothing = new Product("Clothing", 50); Product books = new Product("Books", 30); DiscountCalculator electronicsDiscount = new DiscountCalculator(new ElectronicsDiscount()); DiscountCalculator clothingDiscount = new DiscountCalculator(new ClothingDiscount()); DiscountCalculator booksDiscount = new DiscountCalculator(new NoDiscount()); System.out.println(electronicsDiscount.calculateDiscount(electronics)); // 90 System.out.println(clothingDiscount.calculateDiscount(clothing)); // 40 System.out.println(booksDiscount.calculateDiscount(books)); // 30 } }
Typescript
class Product { private _name: string; private _price: number; constructor(name: string, price: number) { this._name = name; this._price = price; } public get name() { return this.name }; public set name(value: string) { this.name = value }; public get price() { return this.price }; public set price(value: number) { this.price = value }; } interface DiscountStrategy { calculate(product: Product): number; } class ElectronicsDiscount implements DiscountStrategy { calculate(product: Product): number { return product.price * 0.9; // 10% de desconto } } class ClothingDiscount implements DiscountStrategy { calculate(product: Product): number { return product.price * 0.8; // 20% de desconto } } class NoDiscount implements DiscountStrategy { calculate(product: Product): number { return product.price; } } class DiscountCalculator { private discountStrategy: DiscountStrategy; constructor(discountStrategy: DiscountStrategy) { this.discountStrategy = discountStrategy; } public calculateDiscount(product: Product): number { return this.discountStrategy.calculate(product); } } const electronics = new Product('Electronics', 100); const clothing = new Product('Clothing', 50); const books = new Product('Books', 30); const electronicsDiscount = new DiscountCalculator(new ElectronicsDiscount()); const clothingDiscount = new DiscountCalculator(new ClothingDiscount()); const booksDiscount = new DiscountCalculator(new NoDiscount()); console.log(electronicsDiscount.calculateDiscount(electronics)); // 90 console.log(clothingDiscount.calculateDiscount(clothing)); // 40 console.log(booksDiscount.calculateDiscount(books)); // 30
Conclusão
Aplicar o Princípio do Aberto/Fechado é imprescindível se precisamos adicionar novos recursos ou comportamentos sem ter a necessidade de modificar tão profundamente a base de código existente. Na verdade, com o tempo, vemos que é praticamente impossível evitar 100% a mudança da base do código porém é possível sim mitigar a quantidade bruta de código a ser alterado para inserção de uma nova funcionalidade.
Esse princípio torna o código mais adaptável a mudanças, seja para atender novos requisitos ou corrigir erros.
Top comments (0)