Featured image of post Design patern - Strategy và cách mình apply Strategy pattern vào dự án eCommerce để apply discount voucher như thế nào?

Design patern - Strategy và cách mình apply Strategy pattern vào dự án eCommerce để apply discount voucher như thế nào?

Bằng cách sử dụng design pattern - chúng ta có thể "đứng trên vai người khổng lồ", giảm bớt công sức cho giai đoạn thiết kế và giúp chúng ta tổ chức code tốt hơn. Trong bài viết này, chúng ta sẽ tìm hiểu về Strategy Pattern và cách mình sử dụng Strategy pattern để xử lý logic liên quan đến việc nhập mã giảm giá cho sản phẩm cho các eCommerce product.

Strategy Pattern là gì?

Strategy Pattern là một mẫu thiết kế thuộc Behavioral Design Pattern (mẫu thiết kế hành vi) - cho phép bạn định nghĩa một nhóm các thuật toán, đóng gói từng thuật toán dưới dạng các lớp riêng biệt, và có thể hoán đổi chúng cho nhau một cách dễ dàng. Ưu điểm chính của mẫu thiết kế này là cho phép mã client lựa chọn thuật toán cần sử dụng trong lúc chạy (runtime) mà không cần thay đổi cấu trúc của chương trình.

Khi nào nên sử dụng Strategy Pattern?

Bạn nên xem xét sử dụng Strategy Pattern khi:

  • Bạn có nhiều thuật toán cho một nhiệm vụ cụ thể và muốn thay đổi chúng một cách linh hoạt trong lúc chạy.
  • Bạn muốn tránh việc sử dụng các cấu trúc điều kiện phức tạp như if…else hoặc switch…case để chọn thuật toán.
  • Bạn muốn mã của mình dễ mở rộng, cho phép thêm mới các thuật toán mà không cần thay đổi mã đã có.

Sử dụng Strategy Pattern để xử lý việc apply discount voucher

Dạo trước mình có làm một project liên quan đến eCommerce và họ hay tung ra nhiều discount voucher để marketing. Ban đầu thì mình vẫn sử dụng switch|case thông thường để handle cho từng loại voucher. Tuy nhiên khi số lượng voucher càng nhiều code sẽ trở nên rất khhó maintain và scale. Nên thay vì làm rối code bằng rất nhiều switch|case và gộp tất cả vô 1 class, chúng ta có thể triển khai logic này một cách gọn gàng bằng Strategy Pattern.

Định nghĩa Strategy Interface

Trước tiên, chúng ta sẽ định nghĩa một interface chung mà tất cả các class discount service sẽ implement:

1
2
3
4
5
class DiscountStrategy {
    applyDiscount(product) {
        throw new Error("This method need to be overrided");
    }
}

Triển khai các Concrete Strategies

Giả sử chúng ta có các mã giảm giá theo phần trăm, giảm giá cố định, hoặc chương trình black Friday hoặc 11/11 gì đấy v..v. Chúng ta sẽ tạo các class cụ thể cho từng loại giảm giá:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// Chiến lược giảm giá theo phần trăm
class PercentageDiscount extends DiscountStrategy {
    constructor(percentage) {
        super();
        this.percentage = percentage;
    }

    applyDiscount(product) {
        product.price -= product.price * (this.percentage / 100);
        return product.price;
    }
}

// Chiến lược giảm giá cố định
class FixedAmountDiscount extends DiscountStrategy {
    constructor(amount) {
        super();
        this.amount = amount;
    }

    applyDiscount(product) {
        product.price -= this.amount;
        return product.price;
    }
}

// Chiến lược black friday sale up to 90%
class BlackFridayDiscount extends DiscountStrategy {
    applyDiscount(product) {
        product.price /= 10;
        return product.price;
    }
}

Triển khai Context Class

Class context sẽ bao gồm Strategy class của chúng ta, sẽ cho phép chúng ta chuyển đổi giữa các chiến lược giảm giá khác nhau:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class DiscountContext {
    constructor(strategy) {
        this.strategy = strategy;
    }

    setStrategy(strategy) {
        this.strategy = strategy;
    }

    applyDiscount(product) {
        return this.strategy.applyDiscount(product);
    }
}

Ví dụ sử dụng

Bây giờ, chúng ta hãy xem cách sử dụng class DiscountContext để áp dụng các loại giảm giá khác nhau cho một sản phẩm:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const product = { name: "Laptop", price: 1000 };

// Sử dụng chiến lược giảm giá theo phần trăm
const discountContext = new DiscountContext(new PercentageDiscount(10));
console.log(`Giá sau khi giảm 10%: $${discountContext.applyDiscount(product)}`); // Kết quả: 900

// Chuyển sang chiến lược giảm giá cố định
discountContext.setStrategy(new FixedAmountDiscount(200));
console.log(`Giá sau khi giảm $200: $${discountContext.applyDiscount(product)}`); // Kết quả: 700

// Chuyển sang chiến lược black friday sale up to 90%
discountContext.setStrategy(new BlackFridayDiscount());
console.log(`Giá sau khi áp dụng BOGO: $${discountContext.applyDiscount(product)}`); // Kết quả: 100

Tại sao chọn Strategy Pattern?

Sử dụng Strategy Pattern trong trường hợp này mang lại nhiều lợi ích:

  • Tính linh hoạt: Bạn có thể dễ dàng thêm mới các loại giảm giá mà không cần sửa đổi mã nguồn hiện có.
  • Dễ bảo trì: Bằng cách tránh các cấu trúc điều kiện phức tạp, mã nguồn trở nên sạch sẽ và dễ bảo trì hơn.
  • Tái sử dụng: Mỗi chiến lược giảm giá là một phần mã tự chứa và có thể tái sử dụng ở các phần khác của ứng dụng.

Tổng kết

Strategy Pattern là một design pattern tương đối tốt khi bạn cần hoán đổi giữa các thuật toán hoặc hành vi khác nhau trong chương trình. Trong ví dụ thương mại điện tử này, nó cho phép chúng ta xử lý linh hoạt các mã giảm giá, giúp hệ thống dễ dàng mở rộng và bảo trì.

Bằng cách hiểu và áp dụng Strategy Pattern, bạn có thể tổ chức code theo dạng các module, dễ main tain và dễ scale hơn — những yếu tố vô cùng quý giá trong bất kỳ dự án phần mềm nào. Dù bạn đang xử lý mã giảm giá hay các logic phức tạp khác, Strategy Pattern là giải pháp hữu hiệu để làm cho mã của bạn trở nên gọn gàng và có tính hệ thống cao hơn.

HAPPY CODING!

comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy