VieTopik
Tiếng HànTiếng AnhIT
  • Góc học tập
Tải app
  • Thư viện
  • Luyện thi
  • Cẩm nang
  • Góc học tập
Kiến trúc và chất lượng codeDesign pattern
Bài 11/17
5 phút

Strategy

Nội dung bài · 5 mục
  1. 1.Khái niệm
  2. 2.Ví dụ
  3. 3.Thử ngay
  4. 4.Lỗi hay gặp
  5. 5.Tóm tắt

Cửa hàng có ba cách giao: tiêu chuẩn, hoả tốc và khách tự đến lấy. Mỗi cách tính phí một kiểu. Nếu viết hết vào một method, dùng if theo tên cách giao, thì mỗi lần thêm cách giao lại phải sửa method đó. Strategy tách mỗi cách tính thành một class riêng.

Khái niệm

📘 Design pattern: cách giải có tên gọi chung cho một vấn đề thiết kế hay gặp.

♟️ Strategy: mỗi cách làm một việc nằm trong một class riêng, các class cùng implement một interface, và nơi dùng nhận cách làm qua interface đó.

Strategy là một cách áp dụng OCP của khoá OOP: thêm cách giao là thêm class, không sửa code cũ. IDiscount ở bài OCP và IShipping ở bài Composition đều là Strategy.

StandardShipping dưới đây tính đơn giản hơn ShippingCalculator của bài Refactor an toàn: từ 500.000 thì miễn phí, còn lại 20.000 cho mọi nơi.

Ví dụ

var checkout = new Checkout(new StandardShipping());
Console.WriteLine(checkout.Total(200000m));   // 220000

public interface IShippingStrategy
{
    decimal Fee(decimal orderTotal);
}

public class StandardShipping : IShippingStrategy
{
    public decimal Fee(decimal orderTotal)
    {
        if (orderTotal >= 500000)
        {
            return 0;
        }
        return 20000;
    }
}

public class ExpressShipping : IShippingStrategy
{
    public decimal Fee(decimal orderTotal)
    {
        return 50000;
    }
}

public class Checkout
{
    private readonly IShippingStrategy _shipping;

    public Checkout(IShippingStrategy shipping)
    {
        _shipping = shipping;
    }

    public decimal Total(decimal subtotal)
    {
        return subtotal + _shipping.Fee(subtotal);
    }
}
  • IShippingStrategy đại diện cho việc tính phí giao hàng, mỗi class là một cách tính.
  • Checkout chỉ biết interface và nhận cách tính qua constructor, như bài DIP và dependency injection.
  • Đổi cách giao là đổi object truyền vào, Checkout không đổi dòng nào.
<<interface>> IShippingStrategy Fee(orderTotal) StandardShipping ExpressShipping Checkout
Checkout chỉ biết interface, mỗi cách giao là một class

Thử ngay

Thêm cách "khách tự đến lấy", miễn phí, rồi dùng thử:

Console.WriteLine(
    new Checkout(new ExpressShipping()).Total(200000m));
Console.WriteLine(
    new Checkout(new PickupShipping()).Total(200000m));

public class PickupShipping : IShippingStrategy
{
    public decimal Fee(decimal orderTotal)
    {
        return 0;
    }
}

Đoán trước khi chạy: hai dòng in ra bao nhiêu? Phải sửa Checkout không?

Xem kết quả
250000
200000

Hoả tốc cộng 50.000, tự lấy cộng 0. Thêm PickupShipping là thêm một class mới, Checkout và hai cách giao cũ giữ nguyên.

Lỗi hay gặp

Rải if theo tên cách giao khắp code. Thêm cách giao mới phải tìm và sửa mọi chỗ có các if này, sót một chỗ là tính sai.

// SAI — thêm cách giao mới phải sửa method này
decimal Fee(string method, decimal orderTotal)
{
    if (method == "express")
    {
        return 50000;
    }
    if (orderTotal >= 500000)
    {
        return 0;
    }
    return 20000;
}
// ĐÚNG — nơi dùng chỉ nhận interface
public class Checkout
{
    private readonly IShippingStrategy _shipping;

    public Checkout(IShippingStrategy shipping)
    {
        _shipping = shipping;
    }
}

Tóm tắt

  • Design pattern là lời giải có tên cho vấn đề thiết kế hay gặp.
  • Strategy: mỗi cách làm một class, chung một interface.
  • Nơi dùng nhận interface qua constructor, đổi cách làm không phải sửa nó.
  • Thêm cách mới là thêm class, đúng tinh thần OCP.

Tự kiểm tra

0/3 câu
Câu 1

Cửa hàng thêm cách giao "giao trong 2 giờ". Với Strategy, cần làm gì?

Câu 2

Checkout biết gì về cách tính phí giao hàng?

Câu 3

Nguyên lý SOLID nào đứng sau Strategy rõ nhất?

Refactor an toànFactory

Nội dung bài

  1. 1.Khái niệm
  2. 2.Ví dụ
  3. 3.Thử ngay
  4. 4.Lỗi hay gặp
  5. 5.Tóm tắt