Abstraction in object-oriented concepts is a practice of defining only essential aspects that a class must have. Classes, by nature, must be incomplete and imprecise so that we can model specificities through child classes. Thus arises the concept of daughter classes, mother classes and inheritance.
Inheritance is the representation of relationships between classes in which one class extends another in order to inherit the behaviors of the parent class.
SOLID is an acronym that represents five fundamental principles of object-oriented programming, proposed by Robert C. Martin - Uncle Bob. Here you can read more about his article.
These principles aim to improve the structure and maintenance of code, making it more flexible, scalable, and easier to understand. Such principles help the programmer to create more organized codes, dividing responsibilities, reducing dependencies, simplifying the refactoring process and promoting code reuse.
The "O" in the acronym stands for "Open/Closed Principle". The phrase that uncle bob used to define this principle was:
"A class must be open for extension but closed for modification"
According to this principle, we must develop an application ensuring that we write classes or modules in a generic way so that whenever you feel the need to extend the behavior of the class or object, you do not need to change the class itself. Extension here can be read as addition or change of procedures.
The objective is to allow the addition of new functionalities without the need to change existing code. This minimizes the risk of introducing bugs and makes the code more maintainable.
Imagine you have a DiscountCalculator class that calculates product discounts. Initially, we have two product categories: Electronics and Clothing. Let's start without applying the OCP (Open/Closed Principle):
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 } }
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
Encapsulation Violation: Every time a new product type requires a different discount, it will be necessary to modify the calculateDiscount method, including a new conditional in the if.
Difficulty in maintenance: If the method grows with too many if/else or switches, it will become difficult to maintain and test.
Risk of introducing bugs: Changes to the method can introduce bugs into other parts of the code that depend on that method.
Now, let's apply the Open/Closed Principle by refactoring the code to allow the addition of new types of discounts without modifying the existing code.
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 } }
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
Applying the Open/Closed Principle is essential if we need to add new features or behaviors without having to modify the existing code base so deeply. In fact, over time, we see that it is practically impossible to avoid 100% changing the code base, but it is possible to mitigate the gross amount of code to be changed to insert a new functionality.
This principle makes the code more adaptable to changes, whether to meet new requirements or correct errors.
The above is the detailed content of Aplicando o Open/Closed Principle com Typescript e Java. For more information, please follow other related articles on the PHP Chinese website!