본문 바로가기

카테고리 없음

디자인 패턴 - 구조 in C#

C#의 구조 패턴(Structural Patterns) 은 객체 간의 관계를 쉽게 구성하여 유연성과 효율성을 높이는 디자인 패턴입니다. 이는 클래스와 객체를 조합하여 더 큰 구조를 형성하는 데 초점을 맞추며, 유지보수성과 확장성을 개선하는 역할을 합니다.


Adapter (어댑터) 패턴

서로 다른 인터페이스를 가진 클래스를 호환 가능하게 만들어주는 패턴입니다.

사용 사례

  • 기존 시스템과 새로운 시스템을 연결할 때
  • 인터페이스가 다른 두 클래스를 함께 사용할 때
// 기존 인터페이스
public interface ITarget
{
    void Request();
}

// 적응 대상 클래스 (호환되지 않는 인터페이스)
public class Adaptee
{
    public void SpecificRequest()
    {
        Console.WriteLine("Adaptee SpecificRequest 호출됨");
    }
}

// 어댑터 클래스
public class Adapter : ITarget
{
    private Adaptee _adaptee = new Adaptee();
    
    public void Request()
    {
        _adaptee.SpecificRequest();
    }
}

// 사용 예
class Program
{
    static void Main()
    {
        ITarget target = new Adapter();
        target.Request(); // "Adaptee SpecificRequest 호출됨"
    }
}

Bridge (브리지) 패턴

구현과 추상화를 분리하여 독립적으로 확장할 수 있도록 하는 패턴입니다.

사용 사례

  • 플랫폼 독립적인 개발이 필요할 때
  • 코드 변경 없이 기능 확장이 필요할 때
// 구현부 (Implementor)
public interface IRenderer
{
    void Render(string shape);
}

// 구현 세부 사항
public class VectorRenderer : IRenderer
{
    public void Render(string shape) => Console.WriteLine($"Rendering {shape} as vector");
}

public class RasterRenderer : IRenderer
{
    public void Render(string shape) => Console.WriteLine($"Rendering {shape} as raster");
}

// 추상화 (Abstraction)
public abstract class Shape
{
    protected IRenderer renderer;
    
    protected Shape(IRenderer renderer)
    {
        this.renderer = renderer;
    }

    public abstract void Draw();
}

// 세부 클래스 (Refined Abstraction)
public class Circle : Shape
{
    public Circle(IRenderer renderer) : base(renderer) { }

    public override void Draw()
    {
        renderer.Render("Circle");
    }
}

// 사용 예
class Program
{
    static void Main()
    {
        Shape vectorCircle = new Circle(new VectorRenderer());
        vectorCircle.Draw(); // "Rendering Circle as vector"

        Shape rasterCircle = new Circle(new RasterRenderer());
        rasterCircle.Draw(); // "Rendering Circle as raster"
    }
}

Composite (컴포지트) 패턴

객체를 트리 구조로 구성하여 계층적으로 사용할 수 있도록 하는 패턴입니다.

사용 사례

  • UI 구성 요소와 같은 계층 구조를 표현할 때
  • 전체-부분 관계를 명확하게 구현할 때
public interface IComponent
{
    void Display();
}

public class Leaf : IComponent
{
    private string _name;
    public Leaf(string name) => _name = name;
    public void Display() => Console.WriteLine("Leaf: " + _name);
}

public class Composite : IComponent
{
    private List<IComponent> _children = new List<IComponent>();
    public void Add(IComponent component) => _children.Add(component);
    public void Display()
    {
        Console.WriteLine("Composite contains:");
        foreach (var child in _children) child.Display();
    }
}

Decorator (데코레이터) 패턴

객체에 새로운 기능을 동적으로 추가할 수 있도록 하는 패턴입니다.

사용 사례

  • 기존 클래스를 수정하지 않고 기능을 확장할 때
  • 여러 기능을 조합하여 유연한 설계를 원할 때
public interface IComponent
{
    void Operation();
}

public class ConcreteComponent : IComponent
{
    public void Operation() => Console.WriteLine("ConcreteComponent Operation");
}

public class Decorator : IComponent
{
    protected IComponent _component;
    public Decorator(IComponent component) => _component = component;
    public virtual void Operation() => _component.Operation();
}

Facade (퍼사드) 패턴

복잡한 시스템의 인터페이스를 단순화하여 클라이언트가 쉽게 사용할 수 있도록 하는 패턴입니다.

사용 사례

  • 여러 개의 서브 시스템을 하나의 진입점으로 제공할 때
  • 복잡한 시스템을 단순화하고 사용할 때
public class SubsystemA
{
    public void OperationA() => Console.WriteLine("SubsystemA Operation");
}

public class SubsystemB
{
    public void OperationB() => Console.WriteLine("SubsystemB Operation");
}

public class Facade
{
    private SubsystemA _subsystemA = new SubsystemA();
    private SubsystemB _subsystemB = new SubsystemB();
    public void Operation()
    {
        _subsystemA.OperationA();
        _subsystemB.OperationB();
    }
}

정리

패턴 설명
Adapter 서로 다른 인터페이스를 변환하여 호환성 제공
Bridge 구현과 추상화를 분리하여 확장성 증가
Composite 객체를 트리 구조로 만들어 계층적 사용 가능
Decorator 객체에 동적으로 기능 추가
Facade 복잡한 시스템을 단순화하는 인터페이스 제공

이와 같은 구조 패턴을 적절히 사용하면 코드의 유연성, 유지보수성, 확장성을 극대화할 수 있습니다. 🚀