รูปแบบ Factory และ Options
ใช้ factory delegates, IServiceProvider และรูปแบบ Options เพื่อจัดการ dependency ที่มีเงื่อนไขหรือกำหนดค่าได้
รูปแบบ Factory และ Options เป็นบทเรียน C# Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน C# Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน
เมื่อการฉีดผ่านคอนสตรัคเตอร์ไม่เพียงพอ
บางครั้งคุณจำเป็นต้องสร้างบริการแบบ มีเงื่อนไข โดยอิงข้อมูลขณะทำงาน หรือต้องกำหนดค่าให้แตกต่างกันตามกรณีใช้งาน รูปแบบแฟกทอรีและรูปแบบตัวเลือกเหมาะสำหรับสถานการณ์เช่นนี้
ดีลีเกตของแฟกทอรีกับ AddTransient
คุณสามารถส่งดีลีเกตของแฟกทอรีให้กับ AddTransient, AddScoped หรือ AddSingleton ได้ ดีลีเกตจะได้รับ IServiceProvider ทำให้คุณสามารถแก้ไขบริการอื่น ๆ ได้
builder.Services.AddTransient<IPaymentGateway>(sp =>
{
var config = sp.GetRequiredService<IOptions<PaymentConfig>>().Value;
return config.Provider == "stripe"
? new StripeGateway(config.ApiKey)
: new PayPalGateway(config.ClientId, config.Secret);
});ตัวเลือกแบบตั้งชื่อกับ IOptionsSnapshot
รูปแบบตัวเลือกจะผูกส่วนการกำหนดค่าเข้ากับคลาสที่มีชนิดข้อมูลชัดเจน ใช้ IOptions<T> สำหรับซิงเกิลตัน และใช้ IOptionsSnapshot<T> สำหรับค่าที่รีเฟรชต่อคำขอ
public class SmtpSettings
{
public string Host { get; set; } = "";
public int Port { get; set; } = 587;
public string Username { get; set; } = "";
}
// Registration
builder.Services.Configure<SmtpSettings>(
builder.Configuration.GetSection("Smtp"));
// Consumption
public class EmailService
{
private readonly SmtpSettings _settings;
public EmailService(IOptions<SmtpSettings> opts)
=> _settings = opts.Value;
}IOptionsMonitor สำหรับการโหลดใหม่ขณะทำงาน
IOptionsMonitor<T> ให้ค่าตัวเลือกปัจจุบันในเวลาที่เข้าถึง และแจ้งให้คุณทราบเมื่อการกำหนดค่าเปลี่ยนแปลง จึงเหมาะอย่างยิ่งกับบริการแบบซิงเกิลตันที่ต้องการอัปเดตการกำหนดค่าแบบสด
public class FeatureFlagService
{
private readonly IOptionsMonitor<FeatureFlags> _monitor;
public FeatureFlagService(IOptionsMonitor<FeatureFlags> monitor)
=> _monitor = monitor;
public bool IsEnabled(string flag)
{
// Always reads the latest config value
return _monitor.CurrentValue.Flags.GetValueOrDefault(flag);
}
}ตัวเลือกแบบตั้งชื่อ
ตัวเลือกแบบตั้งชื่อช่วยให้คุณลงทะเบียนการกำหนดค่าหลายแบบของชนิดเดียวกัน ใช้ Configure<T>(name, ...) แล้วแก้ไขด้วย IOptionsSnapshot<T>.Get(name)
builder.Services.Configure<S3Settings>("primary",
builder.Configuration.GetSection("S3:Primary"));
builder.Services.Configure<S3Settings>("backup",
builder.Configuration.GetSection("S3:Backup"));
public class S3Service
{
public S3Service(IOptionsSnapshot<S3Settings> opts)
{
var primary = opts.Get("primary");
var backup = opts.Get("backup");
}
}การตรวจสอบตัวเลือกเมื่อเริ่มต้น
ใช้ ValidateDataAnnotations() หรือตัวตรวจสอบแบบกำหนดเองเพื่อให้แน่ใจว่าการกำหนดค่าถูกต้องก่อนแอปเริ่มทำงาน ใช้ร่วมกับ ValidateOnStart() เพื่อให้ระบบล้มเหลวทันทีเมื่อพบข้อผิดพลาด
public class SmtpSettings
{
[Required] public string Host { get; set; } = "";
[Range(1, 65535)] public int Port { get; set; } = 587;
}
builder.Services
.AddOptions<SmtpSettings>()
.Bind(builder.Configuration.GetSection("Smtp"))
.ValidateDataAnnotations()
.ValidateOnStart();รูปแบบแฟกทอรีนามธรรม
อินเทอร์เฟซแฟกทอรีนามธรรมช่วยให้คุณฉีดแฟกทอรีเข้าไปในบริการ และเลื่อนการสร้างออกไปจนกว่าจะมีข้อมูลขณะทำงาน โดยไม่ต้องพึ่งพา IServiceProvider โดยตรง
public interface IReportFactory
{
IReport Create(string reportType);
}
public class ReportFactory : IReportFactory
{
private readonly IServiceProvider _sp;
public ReportFactory(IServiceProvider sp) => _sp = sp;
public IReport Create(string reportType) => reportType switch
{
"pdf" => _sp.GetRequiredService<PdfReport>(),
"excel"=> _sp.GetRequiredService<ExcelReport>(),
_ => throw new ArgumentException("Unknown type")
};
}
builder.Services.AddTransient<IReportFactory, ReportFactory>();บริการแบบใช้คีย์ (.NET 8)
.NET 8 เพิ่ม บริการแบบใช้คีย์ ซึ่งช่วยให้ลงทะเบียนการนำไปใช้งานหลายแบบภายใต้คีย์ต่างกัน แล้วแก้ไขรายการที่ถูกต้องด้วย [FromKeyedServices] หรือ GetKeyedService
builder.Services.AddKeyedScoped<IPaymentGateway, StripeGateway>("stripe");
builder.Services.AddKeyedScoped<IPaymentGateway, PayPalGateway>("paypal");
// Resolve in a class:
public class CheckoutService(
[FromKeyedServices("stripe")] IPaymentGateway stripe,
[FromKeyedServices("paypal")] IPaymentGateway paypal) { }PostConfigure สำหรับการเขียนทับค่า
PostConfigure จะทำงานหลังจากการเรียก Configure ทั้งหมด ใช้เพื่อใช้ค่าที่เขียนทับในการทดสอบ หรือบังคับใช้เงื่อนไขที่ต้องเป็นจริงไม่ว่าการกำหนดค่าใดจะถูกโหลดมา
// In integration tests: force test values after real config
builder.Services.PostConfigure<SmtpSettings>(opts =>
{
opts.Host = "smtp.test.local";
opts.Port = 25;
});เมธอดส่วนขยายของ IServiceCollection
รวมการลงทะเบียนของคุณไว้ในเมธอดส่วนขยาย เพื่อให้ Program.cs สะอาดและทำให้โมดูลนำกลับมาใช้ซ้ำในหลายโครงการได้
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddPaymentServices(
this IServiceCollection services,
IConfiguration config)
{
services.Configure<PaymentConfig>(config.GetSection("Payment"));
services.AddScoped<IPaymentGateway, StripeGateway>();
services.AddScoped<PaymentService>();
return services;
}
}
// Usage in Program.cs:
builder.Services.AddPaymentServices(builder.Configuration);ตัวอย่างจริง: การแจ้งเตือนจากหลายผู้ให้บริการ
เมื่อใช้แฟกทอรีและตัวเลือกแบบตั้งชื่อร่วมกัน คุณสามารถเลือกผู้ให้บริการแจ้งเตือนที่เหมาะสมขณะทำงาน โดยอิงจากค่ากำหนดของผู้ใช้ที่เก็บไว้ในฐานข้อมูล
builder.Services.AddKeyedScoped<INotifier, EmailNotifier>("email");
builder.Services.AddKeyedScoped<INotifier, SmsNotifier>("sms");
builder.Services.AddKeyedScoped<INotifier, PushNotifier>("push");
public class NotificationService
{
private readonly IServiceProvider _sp;
public NotificationService(IServiceProvider sp) => _sp = sp;
public Task SendAsync(string channel, string message)
{
var notifier = _sp.GetRequiredKeyedService<INotifier>(channel);
return notifier.SendAsync(message);
}
}ตรวจสอบความเข้าใจ
อินเทอร์เฟซใดให้ค่าตัวเลือกปัจจุบัน AND แจ้งเตือนเมื่อการกำหนดค่าเปลี่ยนแปลง จึงเหมาะกับบริการแบบซิงเกิลตัน
ทบทวน: รูปแบบแฟกทอรีและตัวเลือก
ประเด็นสำคัญ:
- ดีลีเกตของแฟกทอรีในเมธอด
Add*ช่วยให้สร้างบริการแบบมีเงื่อนไขหรือกำหนดค่าตามข้อมูลขณะทำงานได้ - รูปแบบตัวเลือกจะผูกส่วนการกำหนดค่าเข้ากับ POCO ที่มีชนิดข้อมูลชัดเจน
IOptions= คงที่,IOptionsSnapshot= ต่อคำขอ,IOptionsMonitor= อัปเดตแบบสด- ตรวจสอบตัวเลือกเมื่อเริ่มต้นด้วย
ValidateDataAnnotations().ValidateOnStart() - บริการแบบใช้คีย์ (.NET 8) ช่วยแทนแนวทางแก้ปัญหาด้วยแฟกทอรีสำหรับการนำไปใช้งานแบบตั้งชื่อ
คำถามที่พบบ่อย
บทเรียน “รูปแบบ Factory และ Options” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “รูปแบบ Factory และ Options” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส C# Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “รูปแบบ Factory และ Options”
ใช้ factory delegates, IServiceProvider และรูปแบบ Options เพื่อจัดการ dependency ที่มีเงื่อนไขหรือกำหนดค่าได้ คุณปฏิบัติ C# Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน C# Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน C# Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน
บทเรียน “รูปแบบ Factory และ Options” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน C# Academy นี้ได้ไหม
ได้ บทเรียน C# Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- พื้นฐานคอนเทนเนอร์ DI
- อายุการใช้งานบริการ: ชั่วคราว มีขอบเขต Singleton
- การฉีดผ่าน Constructor และอินเทอร์เฟซ
- รูปแบบ Factory และ Options