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 | 복잡한 시스템을 단순화하는 인터페이스 제공 |
이와 같은 구조 패턴을 적절히 사용하면 코드의 유연성, 유지보수성, 확장성을 극대화할 수 있습니다. 🚀