본문 바로가기
- CS/OOP

[OOP] Design Patterns ( GoF )

by david_동근 2025. 5. 26.

Object Oriented 프로그래밍에서 협업을 위해 설계를 할때,

내가 작성한 코드, 다른 분께서 작성한 코드를 전달해줄 때 생기는 어려움을 극복하기 위해

디자인 패턴이라는 일종의 의사소통 방법을 사용합니다. (일종의 베스트 프랙티스 이기도 하죠)

 


 

Design Patterns

Object Oriented Programming; OOP 에서, 설계한 프로젝트 마다의 이해관계를 좀 더 수월하게 할 수 있도록,

제안된 개념 패턴들을 Design Patterns 라고 부르며, 일종의 코딩 방법론입니다.

위 다자인 패턴을 통해, 코드를 재사용하고, 유연하게 작성하며, 유지보수가 쉽게 만들어지는 것에 도움을 줍니다.

맨 처음 아이디어는 논문 Using Pattern Languages for Object-Oriented Programs(1987) 으로부터 시작됐습니다.

https://c2.com/doc/oopsla87.html

 

Using Pattern Languages for Object-Oriented Programs

Using Pattern Languages for Object-Oriented Programs Kent Beck, Apple Computer, Inc. Ward Cunningham, Tektronix, Inc. Technical Report No. CR-87-43 September 17, 1987 Submitted to the OOPSLA-87 workshop on the Specification and Design for Object-Oriented P

c2.com

 

일반적으로 프로그래머가 만나게 되는 문제들은 대체로, 본인이 지구상에서 맨 처음 겪는 문제가 아닐 확률이 높습니다.

많은 사람들이 똑같거나 유사한 문제에 이미 부딪혀 봤을 것이기 때문에, 문제들의 패턴을 모아둔 해결책이 필요했습니다.

위 아이디어를 통해, 'Gang of 4; GoF' 23개의 디자인 패턴들을 시작으로 지금까지 다양한 패턴들이 등장했습니다.

 

GoF 23 Patterns

프로젝트 설계에서 자주 나오는 문제 상황에 대해 검증된 해결 방법 (패턴)을 정리해 놓은 것입니다.

분류 범위와 목적에 따라, Scope (클래스와 오브젝트) 와 Purpose 로 분류된 건 아래 표와 같습니다.

(Structural 을 보면, 클래스 범위와 오브젝트 범위에도 Adaptor가 2개 있는 건 오타가 아니에용)

 

객체 (Creational) 패턴

객체 생성 방식에 유연성을 부여하여, 코드의 의존성을 줄이는 데 초점을 둡니다.

Factory Method 객체 생성을 하위 클래스에다가 위임
Abstract Factory 관련 객체들을 생성하는 Interface 제공
Builder 복잡한 객체의 생성 과정을 분리, 단계별로
Prototype 기존의 객체를 clone(복제)해 새로운 객체 생성
Singleton 오직 하나의 인스턴스 생성만 보장

 

 

구조 (Structural) 패턴

클래스나 객체를 조합해 더 큰 구조를 만드는데 초점을 둡니다.

Adaptor 인터페이스 호환이 되지 않는 클래스들을 연결
Bridge 구현부에서 추상층을 분리해 독립적으로 확장
Composite 객체들을 트리 구조로 구성해 부분-전체 계층으로 표현
Decorator 객체에 새로운 책임(기능)을 동적으로 추가
Facade 복잡한 sub-시스템을 단순화한 interface를 제공
Flyweight 공유를 통해 메모리 최소화
Proxy 접근 제어, Logging 등을 위한 대리 객체 사용

 

 

행위 (Behavioral) 패턴

객체 간의 책임 분산과 Communication에 초점을 둡니다.

Interpreter 언어의 문법을 클래스로 해석, 표현
Template Method 알고리즘의 구조는 고정하고, 세부는 하위 클래스에다가 위임
Chain of Responsibility 요청을 처리할 수 있는 객체가 (chain을 따라 연쇄적으로) chain 상에 위치
Command 명령을 객체로 캡슐화, Undo/Redo 기능 제공
Iterator 컬렉션 내부 구조 노출 없이 순회
Mediator 객체 간 상호작용을 중재자 객체로 처리
Memento 객체의 특정 상태로 돌아갈 수 있도록 상태를 실체화
Observer 한 객체의 상태 변화시, 관련 객체에게 알림
State 상태에 따라 행동을 바꿈
Strategy 알고리즘을 캡슐화해 자유롭게 교체 가능
Visitor 객체의 구조를 변경하지 않고 새로운 동작 추가 (수행할 연산을 분리해 별도의 클래스로 구성)

 

 

예시 상황

게임 개발에서의 예시로 한번 떠올려 보겠습니다. (주관적인 정리라, 정확하지 않을 수도 있어요!)

 

Singleton

전체 프로그램에서 오직 하나의 인스턴스만 존재하도록 보장
  • GameManager, AudioManager 등에서 글로발하게 접근이 필요한 경우
public class GameManager : MonoBehaviour {
    // 전역에서 접근 가능한 인스턴스
    public static GameManager Instance { get; private set; }

    void Awake() {
        // 이미 인스턴스가 존재한다면, 자기자신 제거 (중복 방지)
        if (Instance != null && Instance != this) {
            Destroy(gameObject);
            return;
        }

        // 인스턴스 설정
        Instance = this;

        // Scene이 변경돼도 지우지마쇼
        DontDestroyOnLoad(gameObject);
    }
}

 

Factory Method

객체 생성 코드를 sub클래스에 위임, 객체 생성을 캡슐화
  • 다양한 타입의 적 캐릭터나, 무기, 아이템 등을 조건에 따라 생성
// Enemy 추상 클래스, 적들의 부모 클래스 (공통적으로 상속할 거)
public abstract class Enemy {
    public abstract void Attack(); // 각각의 적마다 다른 공격 방식
}

// Zombie 클래스, Enemy를 상속 받음
public class Zombie : Enemy {
    public override void Attack() {
        Debug.Log("Zombie Bites!"); // Zombie 만의 공격 방식 (추상클래스 Override 참고해주셔용)
    }
}

// EnemyFactory, 적을 생성하는 팩토리 (마크의 몹 생성기처럼)
public class EnemyFactory {
    // 타입에 따라 적 객체를 생성해 반환
    public Enemy CreateEnemy(string type) {
        if (type == "Zombie")
            return new Zombie(); // "Zombie"일 경우 좀비 생성
        return null; // 알 수 없는 타입이면 null 반환
    }
}

 

Observer

객체의 상태가 변할 때, 의존(성이 있는) 객체들에게 자동으로 알림 (관찰자패턴)
  • Event system, UI 갱신이나 이것 저것 상태 변화 등 전파 매개가 필요할 때
public class Subject {
    // 이벤트 선언, subscriber가 등록될 수 있음
    public event Action OnSomethingHappened;

    // 어떤 일이 발생했을 때 호출
    public void DoSomething() {
        Debug.Log("Something happened!");

        // subscriber들에게 알림 보내기, (null 체크)
        OnSomethingHappened?.Invoke();
    }
}

// ex) subject.OnSomethingHappened += SomeMethod;  // subscriber가 이벤트에 등록

 

Strategy

알고리즘을 (행동을) 인터페이스로 분리하고, 런타임에도 교체 가능토록 설계
  • AI 패턴, 이동 방식 변경이나, 공격 패턴 전략 등등 선택할 때
// strategy 인터페이스, 이동 방식 정의
public interface IMoveStrategy {
    void Move(); // 전략 메서드
}

// 걷기
public class WalkStrategy : IMoveStrategy {
    public void Move()
    {
        Debug.Log("Walking");
    }
}

// 달리기
public class RunStrategy : IMoveStrategy {
    public void Move() {
        Debug.Log("Running");
    }
}

// 위의 strategy를 주입받아 사용되는 Player Class
public class Player {
    private IMoveStrategy strategy; // 현재 strategy

    // strategy 설정
    public void SetStrategy(IMoveStrategy s) {
        strategy = s;
    }

    // strategy에 따른 이동
    public void Move() {
        strategy.Move(); // 현재 설정된 strategy 실행
    }
}

 

Command

요청을 객체로 캡슐화, 실행/취소/저장 등을 구현
  • Undo나, 입력 키 Queue, UI 버튼 시스템에서
// Command 인터페이스
public interface ICommand {
    void Execute(); // 실행
    void Undo(); // 취소
}

// 점프 Command
public class JumpCommand : ICommand {
    public void Execute() {
        Debug.Log("Jump"); // 점프 실행
    }

    public void Undo() {
        Debug.Log("Undo Jump"); // 점프 취소
    }
}

// Command 실행 예시
// var cmd = new JumpCommand();
// cmd.Execute();
// cmd.Undo();

 

State

객체의 상태에 따라 행동에 변화를 주도록 구성
  • 플레이어 상태 (애니메이션이나 다른 용도로 필요한 Idle, Run, Attack 등), 적 공격 패턴 등
// state 인터페이스
public interface IState {
    void Handle(); // 상태별 행동
}

// 대기 state
public class IdleState : IState {
    public void Handle() {
        Debug.Log("Idle..."); // 대기 중
    }
}

// 달리기 state
public class RunState : IState {
    public void Handle() {
        Debug.Log("Running..."); // 달리는 중
    }
}

// 캐릭터 클래스, state에 따라 행동
public class Character {
    private IState state; // 현재 state

    // state 설정
    public void SetState(IState s) {
        state = s;
    }

    // state 기반 처리
    public void Update() {
        state.Handle(); // 현재 state의 행동 실행
    }
}

 

Decorator

기존 객체에 동적으로 기능을 덧붙임
  • 아이템 효과 부여, 능력 강화, 인챈티드 템 같은 거
// 무기 인터페이스
public interface IWeapon {
    void Attack(); // 공격 메서드
}

// 기본 검
public class Sword : IWeapon {
    public void Attack() {
        Debug.Log("sword Attack"); // 기본 공격
    }
}

// 불 속성 부여 데코레이터
public class FireDecorator : IWeapon {
    private IWeapon baseWeapon; // 원본 무기 참조

    public FireDecorator(IWeapon weapon) {
        baseWeapon = weapon; // 주입
    }

    public void Attack() {
        baseWeapon.Attack(); // 원래 무기 공격
        Debug.Log("Fire Enchant"); // 불 속성 효과 추가
    }
}

// 사용 예시
// IWeapon sword = new Sword();
// IWeapon fireSword = new FireDecorator(sword);
// fireSword.Attack();

 

Facade

복잡한 sub-시스템을 간단한 Interface로 구성
  • 서브시스템 초기화, API 간편화
// 게임 시작을 간단하게 처리하는 facade 클래스
public class GameSystemFacade {
    public void StartGame() {
        // 각 서브 시스템들 초기화
        AudioManager.Instance.Init(); // 오디오 초기화
        SaveManager.Instance.Load(); // 저장된 데이터 로드
        UIManager.Instance.ShowStartUI(); // UI 표시
    }
}

 

Builder

복잡한 객체를 단계별로 구성할 수 있도록 분리하고 싶을때 사용
  • 몹 생성기, UI 창 구성이나 복잡한 옵션에서 선택할 때
// 캐릭터 클래스, 설정할 속성들
public class Character {
    public string Name;
    public int HP;
    public int MP;
}

// 캐릭터 생성 빌더
public class CharacterBuilder {
    private Character character = new Character(); // 빌더 내부 객체

    // 이름 설정
    public CharacterBuilder SetName(string name) {
        character.Name = name;
        return this; // 체이닝 가능
    }

    // HP 설정
    public CharacterBuilder SetHP(int hp) {
        character.HP = hp;
        return this;
    }

    // MP 설정
    public CharacterBuilder SetMP(int mp) {
        character.MP = mp;
        return this;
    }

    // 최종 객체 반환
    public Character Build() {
        return character;
    }
}

///<summary>
/// 사용 예시
/// var warrior = new CharacterBuilder()
///                  .SetName("Warrior")
///                  .SetHP(200)
///                  .SetMP(50)
///                  .Build();
///</summary>

 


 

너무 많네용 ᐡ´•﹃•`ᐡ

모두들 화이팅하시고,

오늘도 좋은 하루 되시길 바랍니다~ ( ⸝⸝•ᴗ•⸝⸝ )੭⁾⁾

'- CS > OOP' 카테고리의 다른 글

[CS] DI Dependency Injection  (2) 2025.06.23
[OOP] OOP Object-Oriented Programming 개요  (1) 2025.06.07
[OOP] SOLID 설계 원칙  (2) 2025.06.07