2023-12-20 22:28 — 4 phút đọc

[Design principles] - Phần 1: Design principles là gì? Đóng gói những business logic có thể dễ dàng thay đổi.

#design-principles#how-to#software-architecture


Một phần mềm được thiết kế tốt là gì? Làm sao để đo lường và đánh giá được nó? Cần làm gì để có thể triển khai một phần mềm tốt? Làm sao để phần mềm đạt được sự linh hoạt, ổn định, và dễ hiểu?

Design principlesGiới thiệu về Design principles

Đó là các câu hỏi tuyệt vời, nhưng thật không may là các câu trả lời thường khác nhau tùy thuộc vào điều kiện của từng loại hệ thống mà bạn xây dựng. Tuy nhiên, có một số nguyên tắc thiết kế chung mà bạn có thể áp dụng cho hầu hết các hệ thống. Các nguyên tắc này được coi là nền tảng của Design principles.

Nguyên tắc thiết kế phần mềm

  1. Đóng gói những business logic có thể dễ dàng thay đổi. (Encapsulate What Varies)
  2. Lập trình hướng interface, không hướng implementation. (Program to an interface, not an implementation)
  3. Thành phần ưu tiên hơn thừa kế. (Favor composition over inheritance)

Đóng gói những business logic có thể dễ dàng thay đổi

Đóng gói những business logic có thể dễ dàng thay đổi là một trong những nguyên tắc thiết kế quan trọng nhất. Nó có thể được áp dụng ở nhiều mức độ khác nhau, từ mức method cho đến mức class.

Identify the aspects of your application that vary and separate them from what stays the same. => Xác định những khía cạnh của ứng dụng của bạn có thể thay đổi và tách chúng ra khỏi những gì không thay đổi.

Mục tiêu chính của nguyên tắc này là giảm thiểu sự phụ thuộc giữa các thành phần của hệ thống. Nếu một thành phần có thể thay đổi, bạn cần đóng gói nó để nó không ảnh hưởng đến các thành phần khác.

Ví dụ: bạn đang xây dựng một website bán hàng. Trong website của bạn có một phần tính toán tổng số tiền của đơn hàng. Phần tính toán này có thể thay đổi theo từng quốc gia, và có thể thay đổi theo thời gian. Vì vậy, bạn cần đóng gói phần tính toán này để nó không ảnh hưởng đến các phần khác của website.

  1. Đóng gói ở mức method

Giả sử bạn đang tạo một website bán hàng. Trong code của bạn có một method là getOrderTotal dùng để tính toán một số lượng lớn hàng trong đơn, và thuế.

Chúng ta biết rằng phần tính thuế có thể thay đổi theo từng quốc gia, và có thể thay đổi theo thời gian. Vì vậy, chúng ta cần đóng gói phần tính thuế này để nó không ảnh hưởng đến phần tính toán đơn hàng.

public class Order {
    private List<Item> items;
    private double tax;

    public double getOrderTotal() {
        double total = 0;
        for (Item item : items) {
            if (item.getCountry().equals("US")) {
                tax = 0.07;
            } else if (item.getCountry().equals("UK")) {
                tax = 0.2;
            } else {
                tax = 0.1;
            }
            total += item.getPrice() * tax;
        }
        return total;
    }
}

BEFORE: phần tính toán thuế được trộn lần vào phương thức tính tổng số tiền của đơn hàng

Để tách phần tính thuế ra khỏi phương thức tính tổng số tiền của đơn hàng, chúng ta cần tạo một phương thức tính thuế riêng getTaxRate và sử dụng nó trong phương thức getOrderTotal.

public class Order {
    private List<Item> items;
    private double tax;

    public double getOrderTotal() {
        double total = 0;
        for (Item item : items) {
            total += item.getPrice() * getTaxRate(item.getCountry());
        }
        return total;
    }

    private double getTaxRate(String country) {
        if (country.equals("US")) {
            tax = 0.07;
        } else if (country.equals("UK")) {
            tax = 0.2;
        } else {
            tax = 0.1;
        }
        return tax;
    }
}

AFTER: phần tính thuế được tách ra thành một phương thức riêng

Phần tính thuế đã được tách ra thành một phương thức riêng, và nó không ảnh hưởng đến phương thức tính tổng số tiền của đơn hàng. Hơn thế nữa, nếu phương thức tính thuế mà có thay đổi phức tạp trong tương lai, chúng ta chỉ cần thay đổi business logic bên trong phương thức getTaxRate mà không cần thay đổi phương thức getOrderTotal.

  1. Đóng gói ở mức class

Ở ví dụ trên, chúng ta đã đóng gói phần tính thuế ở mức method. Bây giờ chúng ta sẽ thêm hành vi cho đơn hàng, đó là tính tổng số tiền của đơn hàng và tính tổng số lượng hàng trong đơn hàng…

BEFORE: hàm tính toán trong Order classBEFORE: hàm tính toán trong Order class

Objects của lớp Order chuyển các Object liên quan tới thuế sang lớp TaxCalculator. Lớp TaxCalculator sẽ tính toán thuế và trả về kết quả cho lớp Order.

AFTER: hàm tính toán thuế được tách ra thành class riêng tách ra khỏi OrderAFTER: hàm tính toán thuế được tách ra thành class riêng tách ra khỏi Order


aitu avatar

Hi! Tôi là Tuyên — Hiện tại tôi đang làm Software Architect, Senior developer tại một công ty nhỏ ở Hà Nội. Tôi cảm thấy thích thú, đam mê, yêu thích với việc viết lách và chia sẻ những kiến thức mà tôi biết. Hãy đọc nhiều hơn tại blogs và tới about để biết thêm về tôi nhé.