0Pricing
C# Academy · บทเรียน

รูปแบบ 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. พื้นฐานคอนเทนเนอร์ DI
  2. อายุการใช้งานบริการ: ชั่วคราว มีขอบเขต Singleton
  3. การฉีดผ่าน Constructor และอินเทอร์เฟซ
  4. รูปแบบ Factory และ Options
← กลับไปที่ C# Academy