Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> C#

การนำ Null Object Pattern ไปใช้ใน C#

Null Object Pattern เป็น Behavioral Pattern ที่ให้ Object แทนค่า null เพื่อหลีกเลี่ยง Null Reference Checks ซ้ำซ้อน ทำให้โค้ดสะอาดขึ้น (Clean Code)

แทนที่จะ Return null และบังคับให้ Caller ตรวจสอบ if (obj != null) ทุกครั้ง ให้ Return Null Object ที่ Implement Interface เดียวกัน แต่ทำงานเป็น No-Op (No Operation)

โครงสร้าง

  • Abstract/Interface: กำหนด Contract
  • Real Object: Implementation จริง
  • Null Object: Implementation ว่าง (Do Nothing) - มักเป็น Singleton

ตัวอย่าง: Shape Drawing

// 1. Interface
public interface IShape
{
    void Draw();
}

// 2. Real Objects
public class Circle : IShape
{
    public void Draw() => Console.WriteLine("Drawing a Circle");
}

public class Rectangle : IShape
{
    public void Draw() => Console.WriteLine("Drawing a Rectangle");
}

// 3. Null Object (Singleton)
public class NullShape : IShape
{
    private static readonly Lazy<NullShape> _instance = new Lazy<NullShape>(() => new NullShape());
    
    private NullShape() { } // Private Constructor
    
    public static NullShape Instance => _instance.Value;
    
    public void Draw() { } // Do Nothing - No Op
}

// 4. Factory - คืน Null Object แทน null
public static class ShapeFactory
{
    public static IShape GetShape(string shapeType)
    {
        return shapeType?.ToLower() switch
        {
            "circle" => new Circle(),
            "rectangle" => new Rectangle(),
            _ => NullShape.Instance // แทนที่จะ return null
        };
    }
}

// Client - ไม่ต้อง Check Null!
static void Main()
{
    IShape shape1 = ShapeFactory.GetShape("Circle");
    shape1.Draw(); // "Drawing a Circle"
    
    IShape shape2 = ShapeFactory.GetShape("Triangle"); // ไม่มี
    shape2.Draw(); // No Output, No Exception, No Null Check!
    
    IShape shape3 = ShapeFactory.GetShape(null);
    shape3.Draw(); // Safe!
}

ผลลัพธ์

Drawing a Circle
(ไม่มี Output สำหรับ Triangle และ null - แต่ไม่ Crash)

ข้อดี

  • กำจัด Null Checks ทั่วโค้ด (Reduce Boilerplate)
  • ป้องกัน NullReferenceException
  • Client Code เรียบง่าย อ่านง่าย
  • Default Behavior ที่ปลอดภัย (Safe Default)

ข้อจำกัด / ข้อควรระวัง

  • ไม่เหมาะกับทุกสถานการณ์ - บางครั้ง null มีความหมายเฉพาะ (เช่น "ไม่พบข้อมูล" vs "ข้อมูลว่าง")
  • อาจซ่อน Bug ได้ หากควร Handle Missing Case แบบ Explicit
  • ต้องระวัง Side Effects ใน Null Object (ควรเป็น Pure No-Op)
  • Performance: สร้าง Object เพิ่ม (แต่ Singleton ช่วยลด)

ตัวเลือกใน C# Modern

  • Nullable Reference Types (C# 8.0+): #nullable enable ช่วย Catch Null ที่ Compile Time
  • ?. (Null Conditional Operator), ?? (Null Coalescing)
  • Option<T> / Maybe<T> (Functional Approach - LanguageExt, Optional)

เมื่อไหร่ควรใช้?

  • Default Behavior ที่สมเหตุสมผล (Empty Collection, Disabled Logger, No-op Command)
  • Strategy Pattern ที่มี "Do Nothing" Strategy
  • Composite Pattern: Leaf Node ที่ว่าง