Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code Refactoring Based on SOLID Principles #3

Open
jatoapan opened this issue Nov 11, 2024 · 0 comments
Open

Code Refactoring Based on SOLID Principles #3

jatoapan opened this issue Nov 11, 2024 · 0 comments

Comments

@jatoapan
Copy link

Hello Stationary Shop Management Development Team,

This issue documents the analysis and refactoring carried out for the transaction management system of the stationery store. The goal was to improve the code structure by addressing previously identified violations of SOLID principles.

UML Diagram

image

Changes Made

  1. Single Responsibility Principle (SRP): The Shop class had multiple responsibilities, such as managing the catalog, purchases, sales, and reports by date. Now, it is divided into ProductManager for product management and OperationManager for operation management.

  2. Open/Closed Principle (OCP): The Shop was not closed to modification. Now, OperationManager uses the OperationExecutable interface, allowing new types of operations like Loan and Return to be added without modifying the base class.

  3. Liskov Substitution Principle (LSP): Purchase, Sale, and Damage were not completely substitutable by the base class OperationOnProduct. The OperationExecutable interface was created, and the classes were adjusted to ensure that all implement the same contract, ensuring consistency and compatibility.

  4. Dependency Inversion Principle (DIP): Shop depended on concrete classes (Purchase, Sale, Damage). Now it depends on the abstraction OperationExecutable and uses OperationManager to execute operations, eliminating the coupling with specific classes.

New Classes Implemented

  • OperationManager: Manages the registration and query of operations for the stationery, using a list of OperationExecutable to handle the registered operations.

  • ProductManager: Exclusively manages the product catalog, with methods to list and query products.

  • OperationExecutable (Interface): Defines the contract for operations (execute(), getDate(), getQuantity(), and getType()), allowing any specific operation to be handled uniformly.

  • ProductManageable (Interface): Defines the contract for product management, allowing methods for listing and querying products to be implemented.

Modified Code

Shop.java

import java.util.Date;
import java.util.List;

public class Shop {
    private ProductManageable productManager = new ProductManager();
    private OperationManager operationManager = new OperationManager();

    public String addProduct(Product product) {
        return productManager.enlistProduct(product);
    }

    public void registerTransaction(Product product, OperationExecutable operation) {
        operationManager.registerOperation(product, operation);
    }

    public List<Product> getProducts() {
        return productManager.getAllProducts();
    }

    public List<OperationExecutable> getOperationsByTypeAndDate(String type, Date date) {
        return operationManager.getOperationsByTypeAndDate(type, date);
    }
    
}

Product.java

public class Product {
    private String name, code;
    private int totalQuantity, reorderLevel;

    public Product(String name, String code, int totalQuantity, int reorderLevel) {
        this.name = name;
        this.code = code;
        this.totalQuantity = totalQuantity;
        this.reorderLevel = reorderLevel;
    }

    public String getName() {
        return name;
    }

    public String getCode() {
        return code;
    }

    public int getTotalQuantity() {
        return totalQuantity;
    }

    public int getReorderLevel() {
        return reorderLevel;
    }

    public void setTotalQuantity(int totalQuantity) {
        this.totalQuantity = totalQuantity;
    }

}

OperationOnProduct.java

public abstract class OperationOnProduct implements OperationExecutable {
    protected int quantity;
    protected Date date;

    public OperationOnProduct(int quantity, Date date) {
        this.quantity = quantity;
        this.date = date;
    }

    @Override
    public Date getDate() {
        return date;
    }

    @Override
    public int getQuantity() {
        return quantity;
    }

    public abstract void execute(Product product);

    public abstract String getType();
    
}

Damage.java

import java.util.Date;

public class Damage extends OperationOnProduct {
    private String cause;

    public Damage(int quantity, Date date, String cause) {
        super(quantity, date);
        this.cause = cause;
    }

    @Override
    public void execute(Product product) {
        if (product.getTotalQuantity() >= quantity) {
            product.setTotalQuantity(product.getTotalQuantity() - quantity);
            System.out.println("Daño registrado: " + quantity + " unidades de " + product.getName() + " dañadas.");
        } else {
            System.out.println("Error: La cantidad de daño excede el stock disponible de " + product.getName());
        }
    }

    @Override
    public String getType() {
        return "Daño";
    }
}

Sale.java

public class Sale extends OperationOnProduct {

    public Sale(int quantity, Date date) {
        super(quantity, date);
    }

    @Override
    public void execute(Product product) {
        if (product.getTotalQuantity() >= quantity) {
            product.setTotalQuantity(product.getTotalQuantity() - quantity);
            System.out.println("Venta registrada: " + quantity + " unidades de " + product.getName());
        } else {
            System.out.println("Error: No hay suficientes unidades de " + product.getName() + " para la venta.");
        }
    }

    @Override
    public String getType() {
        return "Venta";
    }
}

Purchase.java

import java.util.Date;

public class Purchase extends OperationOnProduct {
    private String vendorName;

    public Purchase(int quantity, Date date, String vendorName) {
        super(quantity, date);
        this.vendorName = vendorName;
    }

    @Override
    public void execute(Product product) {
        product.setTotalQuantity(product.getTotalQuantity() + quantity);
        System.out.println("Compra registrada: " + quantity + " unidades de " + product.getName());
    }

    @Override
    public String getType() {
        return "Compra";
    }
}

ProductManageable.java

import java.util.List;

public interface ProductManageable {
    String enlistProduct(Product product);
    List<Product> getAllProducts();
}

ProductManager.java

import java.util.ArrayList;
import java.util.List;

public class ProductManager implements ProductManageable {
    private List<Product> productList = new ArrayList<>();

    @Override
    public String enlistProduct(Product product) {
        for (Product p : productList) {
            if (p.getCode().equals(product.getCode())) return "Código ya existe";
            if (p.getName().equals(product.getName())) return "Nombre ya existe";
        }
        productList.add(product);
        return "Producto enlistado.";
    }

    @Override
    public List<Product> getAllProducts() {
        return productList;
    }
}

OperationExecutable.java

import java.util.Date;

public interface OperationExecutable {
    void execute(Product product);
    Date getDate();
    int getQuantity();
    String getType();
}

OperationManager.java

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class OperationManager{
    private List<OperationExecutable> operations = new ArrayList<>();

    public void registerOperation(Product product, OperationExecutable operation) {
        operation.execute(product);
        operations.add(operation);
    }

    public List<OperationExecutable> getOperationsByTypeAndDate(String type, Date date) {
        List<OperationExecutable> filteredOperations = new ArrayList<>();
        for (OperationExecutable op : operations) {
            if (op.getType().equals(type) && op.getDate().equals(date)) {
                filteredOperations.add(op);
            }
        }
        return filteredOperations;
    }

    public List<OperationExecutable> getAllOperations() {
        return operations;
    }
}

This updated design resolves SOLID principle violations, enhances system flexibility, and facilitates adding new functionality without modifying existing classes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant