Abstract Factory Pattern (Mẫu Abstract Factory)
Định nghĩa (Definition)
Abstract Factory cung cấp một interface để tạo họ đối tượng liên quan hoặc phụ thuộc lẫn nhau (Families of Related or Dependent Objects) mà không chỉ định concrete class. Mỗi concrete factory tạo ra một họ sản phẩm hoàn chỉnh hoạt động cùng nhau.
Ví dụ Coffee Shop
Một chuỗi quán cà phê hoạt động tại ba khu vực — Ý (Italy), Việt Nam, và Mỹ (America). Mỗi khu vực có phong cách cà phê (Coffee), bánh ngọt (Pastry), và bao bì (Packaging) riêng. Abstract Factory đảm bảo tất cả món từ cùng một khu vực nhất quán với nhau.
Cấu trúc (Structure)
Interface sản phẩm (Product Interfaces)
// Họ sản phẩm — Coffee
public abstract class Coffee
{
public string Name { get; init; } = "";
public double Price { get; init; }
public List<string> Ingredients { get; init; } = new();
public override string ToString() => $"{Name} — ${Price:F2} [{string.Join(", ", Ingredients)}]";
}
// Họ sản phẩm — Pastry (Bánh ngọt)
public abstract class Pastry
{
public string Name { get; init; } = "";
public double Price { get; init; }
public override string ToString() => $"{Name} — ${Price:F2}";
}
// Họ sản phẩm — Packaging (Bao bì)
public abstract class Packaging
{
public string Material { get; init; } = "";
public string Style { get; init; } = "";
public override string ToString() => $"{Style} ({Material})";
}
Sản phẩm cụ thể (Concrete Products)
// --- Sản phẩm Ý (Italian Products) ---
public class ItalianEspresso : Coffee
{
public ItalianEspresso()
{
Name = "Italian Espresso";
Price = 1.50;
Ingredients = new() { "Fine-ground Arabica", "Pressed in portafilter" };
}
}
public class Cornetto : Pastry
{
public Cornetto()
{
Name = "Cornetto";
Price = 2.00;
}
}
public class CeramicCup : Packaging
{
public CeramicCup()
{
Material = "Ceramic";
Style = "Traditional demitasse";
}
}
// --- Sản phẩm Việt Nam (Vietnamese Products) ---
public class CaPheSuaDa : Coffee
{
public CaPheSuaDa()
{
Name = "Cà Phê Sữa Đá";
Price = 1.00;
Ingredients = new() { "Robusta beans", "Condensed milk", "Ice" };
}
}
public class BanhMi : Pastry
{
public BanhMi()
{
Name = "Bánh Mì";
Price = 1.50;
}
}
public class GlassWithMetalFilter : Packaging
{
public GlassWithMetalFilter()
{
Material = "Glass + Phin filter";
Style = "Traditional Vietnamese";
}
}
// --- Sản phẩm Mỹ (American Products) ---
public class DripCoffee : Coffee
{
public DripCoffee()
{
Name = "Drip Coffee";
Price = 3.00;
Ingredients = new() { "Medium roast", "Paper filter brew" };
}
}
public class Muffin : Pastry
{
public Muffin()
{
Name = "Blueberry Muffin";
Price = 3.50;
}
}
public class PaperCup : Packaging
{
public PaperCup()
{
Material = "Paper";
Style = "Disposable to-go cup";
}
}
Abstract Factory & Concrete Factories
// Abstract Factory
public interface ICoffeeShopFactory
{
Coffee CreateCoffee();
Pastry CreatePastry();
Packaging CreatePackaging();
string Region { get; }
}
// Concrete Factories
public class ItalianFactory : ICoffeeShopFactory
{
public string Region => "Italy";
public Coffee CreateCoffee() => new ItalianEspresso();
public Pastry CreatePastry() => new Cornetto();
public Packaging CreatePackaging() => new CeramicCup();
}
public class VietnameseFactory : ICoffeeShopFactory
{
public string Region => "Vietnam";
public Coffee CreateCoffee() => new CaPheSuaDa();
public Pastry CreatePastry() => new BanhMi();
public Packaging CreatePackaging() => new GlassWithMetalFilter();
}
public class AmericanFactory : ICoffeeShopFactory
{
public string Region => "America";
public Coffee CreateCoffee() => new DripCoffee();
public Pastry CreatePastry() => new Muffin();
public Packaging CreatePackaging() => new PaperCup();
}
Client — Quán cà phê (Coffee Shop)
public class CoffeeShop
{
private readonly ICoffeeShopFactory _factory;
public CoffeeShop(ICoffeeShopFactory factory)
{
_factory = factory;
}
public void ServeCombo()
{
var coffee = _factory.CreateCoffee();
var pastry = _factory.CreatePastry();
var packaging = _factory.CreatePackaging();
Console.WriteLine($"=== {_factory.Region} Combo ===");
Console.WriteLine($" Coffee: {coffee}");
Console.WriteLine($" Pastry: {pastry}");
Console.WriteLine($" Packaging: {packaging}");
}
}
// Usage — chuyển khu vực bằng cách đổi factory
var shops = new CoffeeShop[]
{
new(new ItalianFactory()),
new(new VietnameseFactory()),
new(new AmericanFactory()),
};
foreach (var shop in shops)
shop.ServeCombo();
// Output:
// === Italy Combo ===
// Coffee: Italian Espresso — $1.50 [Fine-ground Arabica, Pressed in portafilter]
// Pastry: Cornetto — $2.00
// Packaging: Traditional demitasse (Ceramic)
// === Vietnam Combo ===
// Coffee: Cà Phê Sữa Đá — $1.00 [Robusta beans, Condensed milk, Ice]
// Pastry: Bánh Mì — $1.50
// Packaging: Traditional Vietnamese (Glass + Phin filter)
// === America Combo ===
// Coffee: Drip Coffee — $3.00 [Medium roast, Paper filter brew]
// Pastry: Blueberry Muffin — $3.50
// Packaging: Disposable to-go cup (Paper)
Factory Method vs Abstract Factory
| Factory Method | Abstract Factory | |
|---|---|---|
| Tạo ra | Một sản phẩm (One Product) | Họ sản phẩm liên quan (Family of Products) |
| Cơ chế | Kế thừa (Inheritance) — override một method | Composition — inject một factory |
| Trọng tâm | "Subclass nào tạo cái này?" | "Họ sản phẩm nào?" |
| Ví dụ | CreateCoffee() được override theo kênh | ICoffeeShopFactory với factory theo khu vực |
Sử dụng thực tế trong .NET (.NET Real-World Usage)
DbProviderFactory— tạo connection, command, parameter cho DB cụ thể (SQL Server, PostgreSQL, v.v.)- UI frameworks — theme factory tạo button, textbox, scrollbar cho look-and-feel cụ thể
- Cross-platform abstractions — factory tạo implementation theo nền tảng cụ thể
Khi nào sử dụng (When to Use)
- Hệ thống cần độc lập với cách sản phẩm được tạo
- Sản phẩm được thiết kế để hoạt động cùng nhau như một họ (Product Family)
- Cần chuyển đổi giữa họ sản phẩm lúc runtime (themes, khu vực, nền tảng)
- Muốn đảm bảo tính nhất quán (Consistency) — sản phẩm từ cùng một họ luôn được dùng cùng nhau
Khi nào KHÔNG sử dụng (When NOT to Use)
- Chỉ có một loại sản phẩm — dùng Factory Method
- Sản phẩm không tạo thành họ — đ ừng ép các đối tượng không liên quan vào cùng một factory
- Họ sản phẩm không bao giờ thay đổi — simple factory với configuration nhẹ hơn
Điểm chính (Key Takeaways)
- Abstract Factory tạo họ sản phẩm liên quan (Families of Related Products) thông qua interface chung
- Chuyển đổi toàn bộ họ (Switch Entire Family) bằng cách đổi factory — không cần sửa client code
- Mỗi concrete factory đảm bảo tính nhất quán (Consistency): tất cả sản phẩm thuộc cùng một "theme"
- Thường được triển khai bằng cách sử dụng nhiều Factory Method bên trong một factory class
Câu hỏi phỏng vấn (Interview Questions)
Q: Abstract Factory khác gì Factory Method? Factory Method tạo một sản phẩm thông qua kế thừa (Inheritance). Abstract Factory tạo họ sản phẩm thông qua composition. Abstract Factory thường sử dụng Factory Method bên trong.
Q: Làm sao thêm sản phẩm mới vào họ (Product Family)?
Thêm method mới vào ICoffeeShopFactory và implement trong mọi concrete factory. Đây là điểm yếu của pattern — vi phạm OCP khi thêm sản phẩm.
Q: Làm sao thêm họ sản phẩm mới (New Product Family)?
Tạo concrete factory mới (ví dụ: JapaneseFactory). Điều này tuân thủ OCP — code hiện có không cần thay đổi.
Chủ đề liên quan (Related Topics)
- Factory Method — tạo một sản phẩm
- Builder — xây dựng đối tượng phức tạp từng bước
- SOLID — DIP — phụ thuộc vào abstraction (
ICoffeeShopFactory), không phải concrete factory