디자인 패턴은 개발을 진행하면서 많이 사용할 수 밖에없는 소프트웨어 아키텍쳐 설계도와 같습니다.
특정상황에는 이런 패턴을 사용하자 이런 느낌으로 기억해두었다가 해당 상황이 나오면 사용하는데 그러기 위해선 디자인 패턴의 구분과 종류 또 어떤 상황에서 사용을 해야하는지 명확하게 알아야 합니다.
디자인 패턴의 종류 표
| 분류 | 패턴 | 목적 |
|---|---|---|
| 생성 | Singleton | 인스턴스 1개만 생성. 객체 생성을 특정 클래스에 위임 |
| Factory Method | 객체 생성을 서브 클래스에 위임 | |
| Abstract Factory | 관련 객체 집합 생성 | |
| Builder | 복잡한 객체 생성 단계 분리 | |
| Prototype | 객체 복제를 통한 생성 | |
| 구조 | Adapter | 호환되지 않는 인터페이스 연결 |
| Bridge | 추상화와 구현 분리 | |
| Composite | 부분-전체 계층 표현 | |
| Decorator | 동적으로 책임 및 확장 부여 | |
| Facade | 복잡한 서브시스템 단순화 | |
| Flyweight | 객체 공유로 메모리 절약 | |
| Proxy | 접근 제어 및 지연 로딩 | |
| 행위 | Chain of Responsibility | 요청을 체인으로 연결 |
| Command | 요청을 객체로 캡슐화 | |
| Interpreter | 언어/문법 규칙 해석 | |
| Iterator | 컬렉션 순회 접근 | |
| Mediator | 복잡한 관계를 중재 | |
| Memento | 객체 상태 저장 및 복원 | |
| Observer | 상태 변화 시 알림 | |
| State | 상태에 따라 행동 변화 | |
| Strategy | 알고리즘 교체 가능 | |
| Template Method | 알고리즘 골격 정의, 구체화 하위 | |
| Visitor | 구조 변경 없이 기능 추가 |
위의 표를 기준으로 하나씩 디자인 패턴에 관해 알아보겠습니다.
생성패턴
생성패턴은 객체의 생성방식을 추상화하고 시스템이 객체를 생성,조합하는 과정을 유연하고 재사용 가능하게 만드는 패턴입니다.
Singleton(싱글턴)
클래스의 인스턴스를 오직 단 하나만 생성하도록 보장하고 전역접근을 제공합니다.
- 메모리낭비 최소화
- 전역변수 사용 없이 어디서든 참조가능
- 특정 연산에만 쓰는 객체 관리 유용
예시 코드
public sealed class Logger
{
private static readonly Lazy<Logger> _instance = new(() => new Logger());
public static Logger Instance => _instance.Value;
private Logger() { }
public void Log(string message) => Console.WriteLine(message);
}
// 프로그램 전체에서 Logger.Instance로 동일한 인스턴스 사용.
// 프로그램 전체에서 Logger.Instance로 동일한 인스턴스 사용.
Factory Method(팩토리메서드)
객체 생성을 서브클래스에 위임하여 클라이언트 코드의 결합도를 낮추는 패턴
- 생성자 제약에서 벗어남
- 객체 생성로직을 별도 함수로 캡슐화
- 상위클래스에서 인터페이스 정의, 하위 클래스에서 구현
예시 코드
interface IButton { void Render(); }
abstract class Dialog
{
public abstract IButton CreateButton();
}
class WindowsDialog : Dialog
{
public override IButton CreateButton() => new WinButton();
}
class WinButton : IButton
{
public void Render() => Console.WriteLine("Windows Button");
}
// 사용: new WindowsDialog().CreateButton().Render();
Abstract Factory(추상팩토리 패턴)
관련된 객체 집합을 생성하는 인터페이스를 제공하며 구체적인 클래스를 지정하지 않고 생성
- 연관된 객체들을 한꺼번에 묶어서 생성
- 플랫폼별 컴포넌트 생성같은 느낌의 작업에 유용
- 객체간의 의존성 제거
예시 코드
interface IButton { void Render(); }
abstract class Dialog
{
public abstract IButton CreateButton();
}
class WindowsDialog : Dialog
{
public override IButton CreateButton() => new WinButton();
}
class WinButton : IButton
{
public void Render() => Console.WriteLine("Windows Button");
}
// 사용: new WindowsDialog().CreateButton().Render();
Builder(빌더 패턴)
복잡한 객체의 생성 과정과 표현을 분리해 동일한 생성 절차에 다양한 표현을 생성
- 복잡한 인스턴스를 단계별로 조립함
- 생성 메서드, 구현 메서드 분리
- 선택적 매개 변수가 많을 때 유용
예시 코드
class Report { public string Title = ""; public string Body = ""; }
interface IReportBuilder
{
IReportBuilder SetTitle(string t);
IReportBuilder SetBody(string b);
Report Build();
}
class ReportBuilder : IReportBuilder
{
private readonly Report _r = new();
public IReportBuilder SetTitle(string t) { _r.Title = t; return this; }
public IReportBuilder SetBody(string b) { _r.Body = b; return this; }
public Report Build() => _r;
}
// 사용: var r = new ReportBuilder().SetTitle("T").SetBody("B").Build();
Prototype(프로토타입 패턴)
기존 객체를 복제하여 새로운 객체를 생성하는 패턴입니다.
- 처음무터 일반적인 원형을 만들고 필요한 부분만 수정
- 생성할 객체의 원형을제공
- 사이즈가 큰 객체 생성시 유용
예시 코드
public class Shape : ICloneable
{
public string Color = "Black";
public object Clone() => MemberwiseClone();
}
// 사용: var copy = (Shape)new Shape().Clone();
구조 패턴
클래스나 객체를 조합하여 더 큰 구조를 만들 때 사용하는 패턴, 클래스간의 관계를 효율적으로 구성합니다.
Adapter(어댑터 패턴)
호환되지않는 인터페이스르 가진 클래스들을 연결해 함께 작동하도록 하는 패턴입니다.
- 기존클래스를 재사용 가능하게 만듬
- 중간에서 인터페이스를 맞춰주는 역할
- 레거시 시스템과 신규 시스템 연동에 유용
예시 코드
class OldPayment { public void PayOld(int amount) => Console.WriteLine($"PayOld {amount}"); }
interface INewPayment { void Pay(decimal amount); }
class PaymentAdapter : INewPayment
{
private readonly OldPayment _old = new();
public void Pay(decimal amount) => _old.PayOld((int)amount);
}
// 사용: INewPayment p = new PaymentAdapter(); p.Pay(100m);
Bridge(브릿지 패턴)
구현부와 추상화를 분리해 각자 독립적으로 확장할 수 있게 하는 패턴입니다.
- 추상화와 구현이 강하게 결합되는 문제를 해결
- 두 계층을 독립적으로 변경 가능
- 확장성과 유연성 증대
예시 코드
interface IRenderer { void RenderCircle(float radius); }
class VectorRenderer : IRenderer { public void RenderCircle(float r) => Console.WriteLine($"Vector circle {r}"); }
abstract class ShapeB
{
protected readonly IRenderer renderer;
protected ShapeB(IRenderer r) { renderer = r; }
public abstract void Draw();
}
class CircleB : ShapeB
{
private readonly float radius;
public CircleB(IRenderer r, float rad) : base(r) { radius = rad; }
public override void Draw() => renderer.RenderCircle(radius);
}
// 사용: new CircleB(new VectorRenderer(), 5).Draw();
Composite(컴포지트 패턴)
객체들을 트리 구조로 구성해 전체~부분적 관계를 동일하게 다루는 패턴입니다.
- 단일 객체와 복합 객체 동일 처리
- 재귀적 트리구조 표현
- 폴더/파일 구조관리에 유용
예시 코드
interface IGraphic { void Draw(); }
class Dot : IGraphic { public void Draw() => Console.WriteLine("dot"); }
class CompoundGraphic : IGraphic
{
private readonly List<IGraphic> _children = new();
public void Add(IGraphic g) => _children.Add(g);
public void Draw() { foreach (var c in _children) c.Draw(); }
}
// 사용: var g = new CompoundGraphic(); g.Add(new Dot()); g.Draw();
Decorator(데코레이터 패턴)
객체에 동적으로 책임을 추가하는 구조, 서브클래싱의 유연한 대안을 하는 패턴입니다.
- Wrapper를 활용한 기능 추가
- 상속대신 조합을 통한 확장
- 런타임에 기능 조합 가능
예시 코드
interface INotifier { void Send(string msg); }
class EmailNotifier : INotifier { public void Send(string msg) => Console.WriteLine($"Email: {msg}"); }
class NotifierDecorator : INotifier
{
protected readonly INotifier wrap;
public NotifierDecorator(INotifier w) { wrap = w; }
public virtual void Send(string msg) => wrap.Send(msg);
}
class SlackNotifier : NotifierDecorator
{
public SlackNotifier(INotifier w) : base(w) { }
public override void Send(string msg) { base.Send(msg); Console.WriteLine($"Slack: {msg}"); }
}
// 사용: new SlackNotifier(new EmailNotifier()).Send("hi");
Facade(파사드 패턴)
복잡한 서브시스템에 대한 단순한 통합 인터페이스를 제공하는 패턴입니다.
- 복잡성 숨기기
- 클라이언트 코드 간소화
- api래퍼 역할
예시 코드
class SubA { public void DoA() => Console.WriteLine("A"); }
class SubB { public void DoB() => Console.WriteLine("B"); }
class SystemFacade
{
private readonly SubA _a = new();
private readonly SubB _b = new();
public void DoAll() { _a.DoA(); _b.DoB(); }
}
// 사용: new SystemFacade().DoAll();
Flyweight(플라이웨이트 패턴)
동일한 객체를 공유하여 메모리를 절약하는 구조패턴입니다.
- 대량의 비슷한 객체 생성 시 메모리 최적화
- 내재적 상태 , 외재적 상태로 분리
- 문자 렌더링,ui컴포넌트에 유리
예시 코드
class Glyph
{
public readonly char Char;
public readonly string Font;
public Glyph(char c, string f) { Char = c; Font = f; }
}
class GlyphFactory
{
private readonly Dictionary<string, Glyph> _pool = new();
public Glyph Get(char c, string font)
{
var key = $"{c}:{font}";
return _pool.TryGetValue(key, out var g) ? g : _pool[key] = new Glyph(c, font);
}
}
// 사용: var g = new GlyphFactory().Get('a', "Consolas");
Proxy(프록시 패턴)
대리 객체를 통한 접근 제어를 제공하는 패턴입니다.
- 접근 제어, 로깅 ,캐싱 가능
- 실제 객체 생성을 지연시킬수 있음(lazy loading)
- 원격 접근, 보안등에 활용
예시 코드
interface IImage { void Display(); }
class RealImage : IImage
{
private readonly string file;
public RealImage(string f) { file = f; Console.WriteLine("Loading..."); }
public void Display() => Console.WriteLine($"Display {file}");
}
class ImageProxy : IImage
{
private readonly string file; private RealImage? real;
public ImageProxy(string f) { file = f; }
public void Display() { real ??= new RealImage(file); real.Display(); }
}
// 사용: IImage img = new ImageProxy("a.png"); img.Display();
행위 패턴
객체나 클래스 간의 상호작용 방식을 정의하여 책임을 분배하고 통신을 효율적으로 하는 패턴이며 복잡한 로직을 분리하고 결합도를 낮추고 유연하게 흐름을 제어하도록 함
Chain of Responsibility(책임 연쇄 패턴)
요청 처리하는 객체를 체인으로 연결한 뒤 순차적으로 전달하는 패턴입니다.
- 요청 처리를 여러 객체에 분산
- 런타임에 체인 동적 구성간으
- 이벤트 처리, 필터 체인에 유용
예시 코드
abstract class Handler
{
private Handler? _next;
public Handler SetNext(Handler n) { _next = n; return n; }
public void Handle(int level) { if (!Process(level)) _next?.Handle(level); }
protected abstract bool Process(int level);
}
class Manager : Handler
{
protected override bool Process(int level) { if (level <= 1) { Console.WriteLine("Manager"); return true; } return false; }
}
class Director : Handler
{
protected override bool Process(int level) { if (level <= 2) { Console.WriteLine("Director"); return true; } return false; }
}
// 사용: new Manager().SetNext(new Director()).Handle(2);
Command(명령 패턴)
요청을 객체로 캡슐화해 실행/취소등을 유연하게 제어하는 패턴입니다.
- 요청을 매개변수화하여 전달 가능
- 요청 큐잉 실행 취소 지원
- 리모컨 메뉴시스템에 유용함
예시 코드
interface ICommand { void Execute(); }
class Light { public void On() => Console.WriteLine("on"); public void Off() => Console.WriteLine("off"); }
class LightOnCommand : ICommand
{
private readonly Light _l;
public LightOnCommand(Light l) { _l = l; }
public void Execute() => _l.On();
}
class Remote { public void Submit(ICommand c) => c.Execute(); }
// 사용: new Remote().Submit(new LightOnCommand(new Light()));
Interpreter(인터프리터 패턴)
언어나 문법 규칙을 클래스화해 해석하는 패턴입니다.
- 간단한 문법 규칙을 클래스로 표현
- 복잡한 언어 파싱에 유용
- 간단한 계산기, dsl에 활용함
예시 코드
interface IExpression { int Interpret(); }
class Number : IExpression
{
private readonly int v;
public Number(int v) { this.v = v; }
public int Interpret() => v;
}
class Plus : IExpression
{
private readonly IExpression l, r;
public Plus(IExpression l, IExpression r) { this.l = l; this.r = r; }
public int Interpret() => l.Interpret() + r.Interpret();
}
// 사용: new Plus(new Number(1), new Number(2)).Interpret(); // 3
Iterator(반복자 패턴)
컬렉션의 내부 구조를 노출하지않고 요소들을 순차적으로 접근하는 패턴입니다.
- 컬렉션 구조와 무관하게 순회 가능
- 여러 순회 방식의 지원
- c# foreach, java Iterator, python for-each와 같음
예시 코드
class MyCollection : IEnumerable<int>
{
private readonly int[] data;
public MyCollection(params int[] d) { data = d; }
public IEnumerator<int> GetEnumerator() { foreach (var i in data) yield return i; }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
}
// 사용: foreach (var x in new MyCollection(1,2,3)) Console.WriteLine(x);
Mediator(중재자 패턴)
객체간의 복잡한 관계를 중재자 하나에 집중해 의존성을 제거하는 패턴입니다.
- 다대다 관계 일대다로 단순화
- 객체 간 결합도 감소
- 채팅방, 항공제어 시스템에서 유용
예시 코드
class ChatRoom { public void Show(string user, string msg) => Console.WriteLine($"{user}: {msg}"); }
class User
{
private readonly string name;
private readonly ChatRoom room;
private readonly ChatRoom room;
public User(string n, ChatRoom r) { name = n; room = r; }
public void Send(string m) => room.Show(name, m);
}
// 사용: var room=new ChatRoom();
new User("A",room).Send("hi");
new User("B",room).Send("hi");
new User("A",room).Send("hi");
new User("B",room).Send("hi");
Memento(메멘토 패턴)
객체의 이전 상태를 외부에 저장하고 필요할 때 복원하는 패턴입니다.
- 객체 캡슐화 유지하면서 상태 저장
- 되돌리기 기능구현
- 스냅샷 세이브 포인트에 유용
예시 코드
record Memento(string State);
class Editor
{
public string Text { get; set; } = "";
public Memento Save() => new(Text);
public void Restore(Memento m) => Text = m.State;
}
// 사용: var e=new Editor{Text="v1"}; var m=e.Save(); e.Text="v2"; e.Restore(m);
Observer(옵져버 패턴)
한 객체의 상태가 변화하면 관련된 객체들에게 자동으로 알리는 패턴입니다.
- 하나의 소스, 여러 객체가 수신(one-to-many)구조
- 느슨한 결합
- 이벤트 리스너, pub/sub구조
예시 코드
class Subject
{
public event Action<int>? Changed;
private int _v;
public int Value { get => _v; set { _v = value; Changed?.Invoke(_v); } }
}
// 사용: var s=new Subject(); s.Changed += v=>Console.WriteLine(v); s.Value=42;
State(상태 패턴)
객체의 상태에 따라 행동이 달라지는 구조를 캡슐화하는 패턴입니다.
- 상태별로 클래스를 나누어서 구현
- 상태 전이 로직 간소화
- 문이나 신호등처럼 상태가 여러개인 객체에 유용함
예시 코드
interface ITurnstileState { void Coin(Turnstile t); void Push(Turnstile t); }
class Locked : ITurnstileState
{
public void Coin(Turnstile t) { Console.WriteLine("Unlocked"); t.State = new Unlocked(); }
public void Push(Turnstile t) => Console.WriteLine("Locked");
}
class Unlocked : ITurnstileState
{
public void Coin(Turnstile t) => Console.WriteLine("Already unlocked");
public void Push(Turnstile t) { Console.WriteLine("Pass"); t.State = new Locked(); }
}
class Turnstile
{
public ITurnstileState State { get; set; } = new Locked();
public void Coin() => State.Coin(this);
public void Push() => State.Push(this);
}
// 사용: var t=new Turnstile(); t.Coin(); t.Push();
Strategy(전략 패턴)
알고리즘을 캡슐화해 교체 가능하게 하여서 유연성을 높이는 패턴입니다.
- 런타임에 알고리즘 선택 가능
- 조건문 제거
- 정렬 결제방식 선택등에 유용
예시 코드
interface ISortStrategy { IEnumerable<int> Sort(IEnumerable<int> data); }
class QuickSortStrategy : ISortStrategy
{
public IEnumerable<int> Sort(IEnumerable<int> data) => data.OrderBy(x => x);
}
class ContextSort
{
private ISortStrategy strategy;
public ContextSort(ISortStrategy s) { strategy = s; }
public IEnumerable<int> DoSort(IEnumerable<int> d) => strategy.Sort(d);
}
// 사용: new ContextSort(new QuickSortStrategy()).DoSort(new[]{3,1,2});
Template Method(템플릿 메서드 패턴)
알고리즘의 구조를 상위 클래스에 정의하고 일부단계를 하위클래스가 구현하는 패턴입니다.
- 상위 클래스에서 알고리즘 스켈레톤 정의
- 하위클래스에서 오버라이딩으로 구현
- 코드 중복 감소
예시 코드
abstract class DataExporter
{
public void Export() { Read(); Transform(); Save(); }
protected abstract void Read();
protected virtual void Transform() { }
protected abstract void Save();
}
class CsvExporter : DataExporter
{
protected override void Read() => Console.WriteLine("Read CSV");
protected override void Save() => Console.WriteLine("Save");
}
// 사용: new CsvExporter().Export();
Visitor(방문자 패턴)
객체의 구조는 변경하지 않으면서 새로운 기능을 추가할 수 있는 패턴입니다.
- 데이터 구조와 연산 분리
- 새로운 연산 추가가 용이
- 컴파일러 ast 방문 보고서 생성등에 유용
예시 코드
interface IVisitor { void Visit(Foo f); void Visit(Bar b); }
interface IElement { void Accept(IVisitor v); }
class Foo : IElement { public void Accept(IVisitor v) => v.Visit(this); }
class Bar : IElement { public void Accept(IVisitor v) => v.Visit(this); }
class PrintVisitor : IVisitor
{
public void Visit(Foo f) => Console.WriteLine("Foo");
public void Visit(Bar b) => Console.WriteLine("Bar");
}
// 사용: IVisitor v=new PrintVisitor(); IElement e=new Foo(); e.Accept(v);
상황에 맞는 패턴 정리
전역 객체가 필요할 때 | Singleton
객체 생성 로직이 복잡할 때 | Factory Method, Builder
다양한 플랫폼 지원이 필요할 때 | Abstract Factory
기존 코드와 신규 코드 연동 | Adapter
여러 기능의 조합이 필요할 때 | Decorator
복잡한 시스템을 단순화할 때 | Facade
요청을 미루거나 취소할 때 | Command
상태에 따라 동작이 달라질 때 | State
여러 알고리즘 중 선택할 때 | Strategy
이벤트 기반 시스템 | Observer
반응형
'CS' 카테고리의 다른 글
| TCP vs UDP 차이 완벽 정리: 연결형 vs 비연결형 패킷 교환 방식 (0) | 2025.11.07 |
|---|---|
| 응집도 완벽정리 - 기능적·순차적·교환적·절차적·시간적·우연적 응집도 예제 코드로 이해하기 (0) | 2025.11.04 |
| 테스트 커버리지 종류완벽 정리 - 구문 커버리지, 분기 커버리지 , 조건/분기 커버리지, 경로커버리지, 다중조건 커버리지 (0) | 2025.10.30 |
| 페이지 교체 알고리즘 알아보기 FIFO OPT LRU LFU NUR 예제 (0) | 2025.10.29 |
| 네트워크 - ip와서브넷 마스크 CIDR이란 (0) | 2023.02.17 |
