[객체지향] 3.디자인 패턴 개요 및 구현 예제, 생성 패턴 싱글턴, 팩토리 메서드, 추상 팩토리

1. 디자인 패턴 소개

디자인 패턴은 소프트웨어 엔지니어링에서 반복적으로 발생하는 문제를 해결하기 위해
고안된 일반적인 솔루션을 의미합니다. 이러한 패턴은 개발자들이 더 나은 구조의
소프트웨어를 설계할 수 있도록 도와줍니다. 디자인 패턴은 주로 객체지향 프로그래밍에서
널리 사용되며, 코드를 재사용하고 유지관리할 수 있는 용이성을 제공합니다.

디자인 패턴은 크게 세 가지 유형으로 나눌 수 있습니다: 생성 패턴, 구조 패턴, 행동 패턴.
이번 글에서는 생성 패턴에 대해 자세히 살펴보겠습니다.

2. 생성 패턴

생성 패턴은 객체 생성 관련 문제를 다룹니다. 이 패턴들은 객체 생성 방식을 정의하여
클라이언트 코드와 객체 생성 로직 간의 결합도를 낮춰줍니다. 생성 패턴의 대표적인 예로는
싱글턴(Singleton), 팩토리 메서드(Factory Method), 추상 팩토리(Abstract Factory)
가 있습니다.

3. 싱글턴 패턴

싱글턴 패턴은 클래스의 인스턴스가 오직 하나만 존재하도록 보장하며,
그 인스턴스에 접근할 수 있는 전역적인 접근점을 제공합니다.
이 패턴은 주로 설정, 로그 기록, 데이터베이스 연결 등과 같이
애플리케이션 전역에서 단일 인스턴스가 필요한 경우에 사용됩니다.

3.1. 구현 예제


public class Singleton
{   
    private static Singleton instance;

    private Singleton()
    {
    }

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

    public void SomeBusinessLogic()
    {
        // 비즈니스 로직
    }
}
        

설명: 위의 구현에서 Singleton 클래스는 private 생성자를 가지고 있어
외부에서 직접 인스턴스를 생성할 수 없습니다. Instance 속성을 통해 싱글턴 객체를
접근할 수 있으며, 인스턴스가 null인 경우에만 새로 생성됩니다.

3.2. 사용 예제


class Program
{
    static void Main(string[] args)
    {
        Singleton singleton = Singleton.Instance;
        singleton.SomeBusinessLogic();
    }
}
        

설명: Main 메서드에서는 Singleton.Instance를 호출하여
싱글턴 인스턴스에 접근하고, 비즈니스 로직을 수행합니다.

4. 팩토리 메서드 패턴

팩토리 메서드 패턴은 객체 생성의 인터페이스를 정의하지만,
어떤 클래스의 인스턴스를 만들지는 서브클래스에서 결정하도록 하는
패턴입니다. 이 패턴은 객체 생성을 캡슐화하여 클라이언트 코드에서
객체 생성에 대한 의존성을 줄여줍니다.

4.1. 구현 예제


public abstract class Creator
{
    public abstract Product FactoryMethod();

    public void SomeOperation()
    {
        // 제품 객체를 생성하고 사용함
        var product = FactoryMethod();
    }
}

public class ConcreteCreatorA : Creator
{
    public override Product FactoryMethod()
    {
        return new ConcreteProductA();
    }
}

public class ConcreteCreatorB : Creator
{
    public override Product FactoryMethod()
    {
        return new ConcreteProductB();
    }
}

public abstract class Product
{
    public abstract string GetInfo();
}

public class ConcreteProductA : Product
{
    public override string GetInfo()
    {
        return "ConcreteProductA";
    }
}

public class ConcreteProductB : Product
{
    public override string GetInfo()
    {
        return "ConcreteProductB";
    }
}
        

설명: Creator 추상 클래스는 FactoryMethod() 메서드를 정의합니다.
ConcreteCreatorA와 ConcreteCreatorB는 각각 다른 종류의 Product 객체를 생성합니다.

4.2. 사용 예제


class Program
{
    static void Main(string[] args)
    {
        Creator creator;

        // A 타입의 제품 생성
        creator = new ConcreteCreatorA();
        var productA = creator.FactoryMethod();
        Console.WriteLine(productA.GetInfo());

        // B 타입의 제품 생성
        creator = new ConcreteCreatorB();
        var productB = creator.FactoryMethod();
        Console.WriteLine(productB.GetInfo());
    }
}
        

설명: Main 메서드에서는 ConcreteCreatorA와 ConcreteCreatorB를 사용하여
서로 다른 타입의 제품을 생성하고 출력합니다.

5. 추상 팩토리 패턴

추상 팩토리 패턴은 관련된 객체들의 집합을 생성하는 인터페이스를 제공합니다.
이 패턴은 클라이언트 코드가 구체적인 클래스에 의존하지 않고,
고수준의 인터페이스를 통해 객체를 생성할 수 있도록 합니다.
주로 관련된 제품군을 만들어야 할 때 유용합니다.

5.1. 구현 예제


public interface IAbstractFactory
{
    IProductA CreateProductA();
    IProductB CreateProductB();
}

public class ConcreteFactory1 : IAbstractFactory
{
    public IProductA CreateProductA()
    {
        return new ProductA1();
    }

    public IProductB CreateProductB()
    {
        return new ProductB1();
    }
}

public class ConcreteFactory2 : IAbstractFactory
{
    public IProductA CreateProductA()
    {
        return new ProductA2();
    }

    public IProductB CreateProductB()
    {
        return new ProductB2();
    }
}

public interface IProductA
{
    string GetProductInfo();
}

public class ProductA1 : IProductA
{
    public string GetProductInfo()
    {
        return "ProductA1";
    }
}

public class ProductA2 : IProductA
{
    public string GetProductInfo()
    {
        return "ProductA2";
    }
}

public interface IProductB
{
    string GetProductInfo();
}

public class ProductB1 : IProductB
{
    public string GetProductInfo()
    {
        return "ProductB1";
    }
}

public class ProductB2 : IProductB
{
    public string GetProductInfo()
    {
        return "ProductB2";
    }
}
        

설명: IAbstractFactory 인터페이스는 두 가지 타입의 제품을
생성하는 메서드를 정의합니다. 각 구체적인 팩토리는 이를 구현하여
스스로의 제품을 생성합니다.

5.2. 사용 예제


class Program
{
    static void Main(string[] args)
    {
        IAbstractFactory factory = new ConcreteFactory1();
        var productA = factory.CreateProductA();
        var productB = factory.CreateProductB();

        Console.WriteLine(productA.GetProductInfo());
        Console.WriteLine(productB.GetProductInfo());
    }
}
        

설명: Main 메서드에서는 ConcreteFactory1을 통해
ProductA1과 ProductB1 객체를 생성하고 정보를 출력합니다.
디펜던시가 구체적인 제품이 아닌 추상화된 형태로 제공되므로,
변경이 용이해집니다.

6. 결론

이번 글에서는 C#에서 디자인 패턴, 특히 생성 패턴에 대해 알아보았습니다.
싱글턴, 팩토리 메서드, 추상 팩토리 패턴을 통해 객체 생성을 좀 더 유연하고,
재사용 가능한 방법으로 처리할 수 있음을 보여주었습니다. 디자인 패턴을 적절히
활용하면 코드의 가독성과 유지보수성을 높일 수 있습니다. 앞으로도 다양한
디자인 패턴에 대한 이해를 넓히고, 적절히 활용해 보시길 바랍니다.