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)