Singleton & Factory: tips & trik

Singleton dan Factory—dua konsep penting yang akan mengubah cara kalian memandang pembuatan objek di aplikasi. Bayangkan, dengan pola Singleton, kalian bisa mengelola instance tunggal dengan mudah dan mencegah bug tricky. Sementara itu, pola Factory memisahkan logika pembuatan objek dari aplikasi utama, membuat kode kalian lebih fleksibel dan scalable. Ayo kita bahas lebih lanjut.

Singleton

Bayangkan kamu punya kelas yang hanya boleh punya satu instance di seluruh aplikasi, semacam "raja" dari kelas-kelas lain. Itu dia tujuan dari pola Singleton. Pola ini memastikan bahwa sebuah kelas hanya memiliki satu instance dan menyediakan titik akses global ke instance tersebut.

Contoh

public class Singleton
{
    private static Singleton instance = null;
    private static readonly object padlock = new object();

    Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            lock (padlock)
            {
                if (instance == null)
                {
                    instance = new Singleton();
                }
                return instance;
            }
        }
    }
}

Di sini, kita punya kelas Singleton dengan konstruktor privat untuk mencegah instansiasi langsung. Properti Instance menggunakan lock untuk memastikan thread safety saat membuat instance tunggal.

Contoh penggunaan singleton untuk logger

Bayangkan kita memiliki aplikasi yang membutuhkan logging. Kita ingin memastikan bahwa semua log masuk melalui satu titik untuk menjaga konsistensi dan menghindari konflik. Pola Singleton sangat cocok untuk ini.

public class Logger
{
    private static Logger instance = null;
    private static readonly object padlock = new object();
    
    private Logger() 
    {
    }

    public static Logger Instance
    {
        get
        {
            lock (padlock)
            {
                if (instance == null)
                {
                    instance = new Logger();
                }
                return instance;
            }
        }
    }

    public void Log(string message)
    {
        Console.WriteLine($"Log entry: {message}");
    }
}

Di sini, kelas Logger menggunakan pola Singleton untuk memastikan hanya ada satu instance logger. Kita bisa menggunakannya seperti ini:

Logger.Instance.Log("This is a log message");

Tips dan Trik Singleton

  • Thread Safety: Gunakan kunci (lock) untuk menghindari masalah ketika mengakses instance dari beberapa thread.
  • Lazy Initialization: Membuat instance hanya saat dibutuhkan menghemat sumber daya.
  • Global Access: Pastikan kamu benar-benar perlu satu instance global sebelum menggunakan Singleton, karena bisa memperkenalkan ketergantungan tersembunyi.

Factory

Sekarang bayangkan kamu ingin membuat objek tanpa harus mengetahui kelas spesifik apa yang akan dibuat. Nah, di sinilah pola Factory membantu. Factory pattern menyediakan cara untuk membuat objek tanpa harus mengetahui detail spesifik dari kelas yang akan dibuat.

Contoh

public interface IProduct
{
    void Display();
}

public class ConcreteProductA : IProduct
{
    public void Display()
    {
        Console.WriteLine("ConcreteProductA");
    }
}

public class ConcreteProductB : IProduct
{
    public void Display()
    {
        Console.WriteLine("ConcreteProductB");
    }
}

public class ProductFactory
{
    public IProduct CreateProduct(string type)
    {
        switch (type)
        {
            case "A":
                return new ConcreteProductA();
            case "B":
                return new ConcreteProductB();
            default:
                throw new ArgumentException("Invalid type");
        }
    }
}

Kita punya interface IProduct dan dua implementasi konkret: ConcreteProductA dan ConcreteProductB. Kelas ProductFactory punya metode CreateProduct untuk mengembalikan produk berdasarkan tipe.

Contoh penggunaan Factory untuk pembayaran

Misalnya kita membuat aplikasi yang mendukung berbagai metode pembayaran seperti Kartu Kredit, PayPal, dan Transfer Bank. Dengan pola Factory, kita bisa membuat objek pembayaran berdasarkan tipe yang dipilih oleh pengguna.

public interface IPayment
{
    void ProcessPayment();
}

public class CreditCardPayment : IPayment
{
    public void ProcessPayment()
    {
        Console.WriteLine("Processing credit card payment.");
    }
}

public class PayPalPayment : IPayment
{
    public void ProcessPayment()
    {
        Console.WriteLine("Processing PayPal payment.");
    }
}

public class BankTransferPayment : IPayment
{
    public void ProcessPayment()
    {
        Console.WriteLine("Processing bank transfer payment.");
    }
}

public class PaymentFactory
{
    public IPayment CreatePayment(string type)
    {
        switch (type)
        {
            case "CreditCard":
                return new CreditCardPayment();
            case "PayPal":
                return new PayPalPayment();
            case "BankTransfer":
                return new BankTransferPayment();
            default:
                throw new ArgumentException("Invalid payment type");
        }
    }
}

Kita bisa menggunakan PaymentFactory untuk membuat objek pembayaran berdasarkan tipe:

PaymentFactory factory = new PaymentFactory();

IPayment payment = factory.CreatePayment("CreditCard");
payment.ProcessPayment(); // Output: Processing credit card payment.

payment = factory.CreatePayment("PayPal");
payment.ProcessPayment(); // Output: Processing PayPal payment.

Dengan pola ini, kita dapat dengan mudah menambahkan metode pembayaran baru tanpa mengubah kode yang sudah ada.

Tips dan Trik Factory

  • Decoupling: Factory membantu memisahkan pembuatan objek dari logika bisnis, meningkatkan keterbacaan kode.
  • Scalability: Mudah menambahkan tipe produk baru tanpa mengubah kode existing.
  • Error Handling: Pastikan factory menangani tipe produk yang tidak valid dengan baik.

Kapan Menggunakan Singleton dan Factory

  • Singleton: Gunakan ketika kamu butuh satu instance objek di seluruh aplikasi, seperti konfigurasi, log manager, atau koneksi database.
  • Factory: Gunakan ketika kamu punya beberapa tipe objek yang dibuat berdasarkan kondisi atau parameter tertentu, seperti parser, handler, atau strategi.

Semoga penjelasan ini membantu kalian memahami bagaimana dan kapan menggunakan pola Singleton dan Factory dalam pengembangan C#. Ayo lanjut belajar bareng jika ada yang ingin ditanyakan atau didiskusikan!

Selamat belajar!