1. 브리지 패턴이란?

브리지 패턴은 두 개의 독립적인 개념을 연결(Bridge)해 서로의 변화에 영향을 덜 받게 만드는 구조적 디자인 패턴입니다. 일반적으로 추상화구현을 분리해 독립적으로 확장할 수 있도록 설계합니다. 이 패턴은 코드의 확장성과 유지보수성을 높이는 데 유용합니다. 🎯

특징

  • 추상화와 구현의 독립성 유지
  • 런타임에 구현을 바꿀 수 있는 유연성 제공
  • 다형성을 활용한 깔끔한 구조

2. 브리지 패턴의 구성 요소

브리지 패턴은 크게 4가지 구성 요소로 이루어집니다:

  1. Abstraction (추상화)
    • 기능의 상위 레벨을 정의한 인터페이스 또는 추상 클래스
  2. Refined Abstraction (구체화된 추상화)
    • 추상화를 확장한 구체적인 클래스
  3. Implementor (구현자)
    • 실제 기능을 수행하는 인터페이스
  4. Concrete Implementor (구체 구현자)
    • 구현자 인터페이스를 실제로 구현한 클래스

구조 다이어그램

출처: 위키

3. 브리지 패턴 예제

문제 상황

게임 아이템을 화면에 표시할 때, 무기 종류효과(예: 불, 얼음 등)를 조합해야 한다고 가정해봅시다.

브리지 패턴을 활용해 조합 가능한 클래스 수를 줄이고 유연성을 높이는 구조를 설계해 보겠습니다. 💡

코드 예제

// Implementor
public interface IEffect
{
    void ApplyEffect(string weapon);
}

// Concrete Implementors
public class FireEffect : IEffect
{
    public void ApplyEffect(string weapon)
    {
        Debug.Log($"{weapon}에 불 효과를 적용합니다.");
    }
}

public class IceEffect : IEffect
{
    public void ApplyEffect(string weapon)
    {
        Debug.Log($"{weapon}에 얼음 효과를 적용합니다.");
    }
}

// Abstraction
public abstract class Weapon
{
    protected IEffect effect;

    public Weapon(IEffect effect)
    {
        this.effect = effect;
    }

    public abstract void Use();
}

// Refined Abstraction
public class Sword : Weapon
{
    public Sword(IEffect effect) : base(effect) { }

    public override void Use()
    {
        Debug.Log("검을 휘두릅니다.");
        effect.ApplyEffect("검");
    }
}

public class Bow : Weapon
{
    public Bow(IEffect effect) : base(effect) { }

    public override void Use()
    {
        Debug.Log("활을 쏩니다.");
        effect.ApplyEffect("활");
    }
}

// 사용 예시
public class Game
{
    public static void Main()
    {
        IEffect fireEffect = new FireEffect();
        IEffect iceEffect = new IceEffect();

        Weapon sword = new Sword(fireEffect);
        sword.Use(); // 출력: 검을 휘두릅니다. 검에 불 효과를 적용합니다.

        Weapon bow = new Bow(iceEffect);
        bow.Use(); // 출력: 활을 쏩니다. 활에 얼음 효과를 적용합니다.
    }
}

4. 브리지 패턴의 사용 시 고려할 점

장점 ✨

  1. 확장성 증가: 추상화와 구현을 독립적으로 확장할 수 있습니다.
  2. 코드 중복 감소: 조합 가능한 클래스 수를 줄입니다.
  3. 유연성 제공: 런타임에 구현을 바꿀 수 있습니다.

주의점 ⚠️

  • 설계가 복잡해질 수 있으므로, 모든 상황에 적합하지 않습니다.
  • 추상화와 구현이 지나치게 단순하다면, 오히려 불필요한 구조가 될 수 있습니다.

5. 결론

브리지 패턴은 추상화와 구현을 분리해 서로 독립적으로 확장할 수 있도록 돕는 강력한 디자인 패턴입니다. 특히, 조합 가능한 객체가 많아질 때 설계의 복잡도를 줄이고 코드의 유연성을 높이는 데 효과적입니다. 하지만 과도하게 사용하면 불필요한 복잡성을 초래할 수 있으므로 적절한 상황에서 활용하는 것이 중요합니다. 🚀

6. 관련 링크

반응형

1. 컴포지트 패턴이란? 🌳

컴포지트 패턴(Composite Pattern)은 객체를 트리(tree) 구조로 구성하여 부분-전체 계층 구조를 구현하는 데 사용됩니다. 이를 통해 클라이언트는 개별 객체와 복합 객체를 동일하게 다룰 수 있습니다.

이 패턴은 특히 UI 구성 요소파일 시스템처럼 계층적 구조를 가진 시스템에서 유용합니다. 컴포지트 패턴은 단순한 인터페이스를 통해 복잡한 구조를 효율적으로 관리할 수 있게 해줍니다.

출처: 위키

 

2. 컴포지트 패턴의 구성 요소 🧩

  1. Component (구성 요소): 트리 구조에서 공통 인터페이스를 정의합니다.
  2. Leaf (잎 노드): 트리 구조의 말단에 해당하며, 실제 작업을 수행합니다.
  3. Composite (복합 노드): 다른 Leaf 또는 Composite를 자식으로 포함할 수 있으며, 자식 요소에 작업을 위임합니다.

3. 컴포지트 패턴 예제 🎮

Unity3D에서 게임 오브젝트 트리 관리

Unity의 GameObject 트리 구조는 컴포지트 패턴의 대표적인 예입니다. 이를 간단히 구현한 예제를 보겠습니다.

using System;
using System.Collections.Generic;

// Component: 공통 인터페이스
public interface IGameObject
{
    void Render();
}

// Leaf: 단일 요소
public class GameObject : IGameObject
{
    private string _name;

    public GameObject(string name)
    {
        _name = name;
    }

    public void Render()
    {
        Console.WriteLine($"Rendering GameObject: {_name}");
    }
}

// Composite: 자식 요소를 포함하는 복합 객체
public class GameObjectGroup : IGameObject
{
    private List<IGameObject> _children = new List<IGameObject>();

    public void Add(IGameObject gameObject)
    {
        _children.Add(gameObject);
    }

    public void Remove(IGameObject gameObject)
    {
        _children.Remove(gameObject);
    }

    public void Render()
    {
        Console.WriteLine("Rendering GameObjectGroup:");
        foreach (var child in _children)
        {
            child.Render();
        }
    }
}

// 예제 실행
public class Program
{
    public static void Main()
    {
        // Leaf 생성
        var player = new GameObject("Player");
        var enemy = new GameObject("Enemy");

        // Composite 생성 및 Leaf 추가
        var scene = new GameObjectGroup();
        scene.Add(player);
        scene.Add(enemy);

        // Composite 내에 또 다른 Composite 추가
        var uiGroup = new GameObjectGroup();
        var uiElement = new GameObject("HealthBar");
        uiGroup.Add(uiElement);
        scene.Add(uiGroup);

        // 전체 트리 구조 렌더링
        scene.Render();
    }
}

출력 결과

Rendering GameObjectGroup:
Rendering GameObject: Player
Rendering GameObject: Enemy
Rendering GameObjectGroup:
Rendering GameObject: HealthBar

4. 컴포지트 패턴 사용 시 고려할 점 ⚠️

  • 객체의 복잡성: 트리 구조가 너무 깊거나 복잡하면 관리가 어려워질 수 있습니다. 불필요한 계층화를 피해야 합니다.
  • 단일 책임 원칙: 컴포지트 객체는 자식 관리와 작업 위임이라는 두 가지 책임을 가지므로, 적절히 설계해야 책임이 분리됩니다.
  • 성능: 큰 트리 구조에서는 순회를 최소화하는 최적화가 필요할 수 있습니다.

5. 결론 🚀

컴포지트 패턴은 객체를 계층적으로 구성하고, 클라이언트가 개별 요소와 복합 요소를 동일하게 처리할 수 있게 해줍니다. 특히 Unity3D와 같은 게임 엔진에서 UI, 씬 관리 등에 매우 유용하게 활용됩니다. 하지만 설계 단계에서 계층 구조의 복잡성을 신중히 고려해야 합니다.

6. 관련 링크 🔗

반응형

 

1. 상태 패턴이란? 🤔

상태 패턴(State Pattern)은 객체가 상태에 따라 다른 동작을 하도록 설계하는 디자인 패턴입니다. 객체의 내부 상태를 캡슐화하고, 상태 전환 시 행동이 달라지도록 구현할 수 있습니다. 이를 통해 코드의 복잡도를 줄이고 유지보수성을 높일 수 있습니다.

출처: 위키


2. 상태 패턴의 구성 요소

상태 패턴은 다음 세 가지 주요 구성 요소로 이루어져 있습니다:

  1. Context: 상태를 관리하는 주체로, 상태 변경에 따라 동작을 위임합니다.
  2. State: 상태를 나타내는 인터페이스로, 구체적인 행동을 정의하는 메서드가 포함됩니다.
  3. ConcreteState: 특정 상태를 구현한 클래스입니다. 각 상태마다 서로 다른 동작을 제공합니다.

3. 상태 패턴 예제 💻

Unity3D에서 게임 캐릭터의 무기 상태(예: 기본 무기, 활, 마법지팡이)를 상태에 따라 다르게 처리하는 예제를 만들어 보겠습니다.

// State 인터페이스 정의
public interface IWeaponState
{
    void Attack();
}

// ConcreteState: 기본 무기 상태
public class DefaultWeaponState : IWeaponState
{
    public void Attack()
    {
        Debug.Log("기본 무기로 공격합니다.");
    }
}

// ConcreteState: 활 상태
public class BowState : IWeaponState
{
    public void Attack()
    {
        Debug.Log("활로 원거리 공격을 합니다.");
    }
}

// ConcreteState: 마법지팡이 상태
public class MagicWandState : IWeaponState
{
    public void Attack()
    {
        Debug.Log("마법지팡이로 마법 공격을 합니다.");
    }
}

// Context 클래스 정의
public class Character
{
    private IWeaponState _currentWeaponState;

    public Character()
    {
        _currentWeaponState = new DefaultWeaponState(); // 초기 상태 설정
    }

    public void SetWeaponState(IWeaponState newWeaponState)
    {
        _currentWeaponState = newWeaponState;
        Debug.Log("무기가 변경되었습니다.");
    }

    public void Attack()
    {
        _currentWeaponState.Attack();
    }
}

// Unity에서 사용 예
public class CharacterController : MonoBehaviour
{
    private Character _character;

    void Start()
    {
        _character = new Character();
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Alpha1))
        {
            _character.SetWeaponState(new DefaultWeaponState());
        }
        else if (Input.GetKeyDown(KeyCode.Alpha2))
        {
            _character.SetWeaponState(new BowState());
        }
        else if (Input.GetKeyDown(KeyCode.Alpha3))
        {
            _character.SetWeaponState(new MagicWandState());
        }

        if (Input.GetKeyDown(KeyCode.Space))
        {
            _character.Attack();
        }
    }
}

4. 상태 패턴의 사용 시 고려할 점 📌

  1. 상태 추가 용이성: 새로운 상태를 추가할 때 기존 코드를 수정하지 않고 새로운 클래스만 추가하면 됩니다.
  2. 상태 전환 관리: 상태 간 전환이 많아질 경우 복잡도가 증가할 수 있으므로 설계를 잘해야 합니다.
  3. 적용 범위: 상태 패턴은 상태가 명확히 구분되고, 행동이 상태에 따라 크게 달라질 때 적합합니다.

5. 결론 ✅

상태 패턴은 객체의 행동을 상태에 따라 다르게 구현할 때 유용합니다. Unity3D와 같은 게임 개발에서는 캐릭터의 무기 상태 관리, AI 상태 전환 등에 자주 사용됩니다. 적절히 활용하면 코드의 가독성과 유지보수성을 크게 향상시킬 수 있습니다.


6. 관련 링크 🔗

반응형

1. 플라이웨이트 패턴이란?

플라이웨이트 패턴은 많은 객체를 생성해야 할 때 메모리를 절약하기 위해 공유할 수 있는 객체를 재사용하는 디자인 패턴입니다. 동일한 데이터는 공유하고, 개별 객체는 고유한 상태만 유지하도록 설계합니다. 주로 게임, 그래픽 애플리케이션 등에서 효율적인 리소스 관리에 사용됩니다.

출처: 위키


2. 플라이웨이트 패턴의 구성 요소

  1. Flyweight (공유 객체): 공유 가능한 객체를 정의합니다.
  2. Intrinsic State (내재 상태): 공유 객체가 공통적으로 가지는 불변 상태입니다.
  3. Extrinsic State (외재 상태): 객체별로 고유한 가변 상태이며, 외부에서 제공됩니다.
  4. Flyweight Factory (팩토리): Flyweight 객체를 관리하며, 중복 생성 방지를 위해 기존 객체를 반환하거나 새로 생성합니다.

3. 플라이웨이트 패턴 예제 (C#)

캐릭터 글자 렌더링 최적화 예제

플라이웨이트 패턴은 텍스트 렌더링에서 자주 사용됩니다. 같은 글자를 여러 번 렌더링할 때, 동일한 글자 객체를 재사용해 메모리를 절약합니다.

using System;
using System.Collections.Generic;

// Flyweight (공유 객체)
public class Character
{
    public char Symbol { get; private set; }  // Intrinsic State
    public string Font { get; private set; }  // Intrinsic State

    public Character(char symbol, string font)
    {
        Symbol = symbol;
        Font = font;
    }

    public void Display(int positionX, int positionY) // Extrinsic State
    {
        Console.WriteLine($"Character: {Symbol}, Font: {Font}, Position: ({positionX}, {positionY})");
    }
}

// Flyweight Factory
public class CharacterFactory
{
    private readonly Dictionary<string, Character> _characters = new();

    public Character GetCharacter(char symbol, string font)
    {
        string key = $"{symbol}_{font}";
        if (!_characters.ContainsKey(key))
        {
            _characters[key] = new Character(symbol, font);
        }
        return _characters[key];
    }
}

// Client Code
class Program
{
    static void Main(string[] args)
    {
        CharacterFactory factory = new CharacterFactory();

        // Reusing 'A' with the same font
        var charA1 = factory.GetCharacter('A', "Arial");
        var charA2 = factory.GetCharacter('A', "Arial");

        charA1.Display(10, 20);
        charA2.Display(30, 40);

        Console.WriteLine(Object.ReferenceEquals(charA1, charA2)); // True
    }
}

4. 플라이웨이트 패턴의 사용 시 주의점 ✅

  1. 상태 구분: 내재 상태와 외재 상태를 명확히 분리해야 합니다. 잘못된 설계는 객체의 불변성을 깨뜨릴 수 있습니다.
  2. 팩토리 관리: Flyweight Factory에서 객체의 생명 주기를 잘 관리해야 메모리 누수를 방지할 수 있습니다.
  3. 적합한 상황: 객체 생성 비용이 크고, 동일한 상태의 객체가 반복될 때 효과적입니다. 객체가 항상 고유 상태를 가진다면 이 패턴은 적합하지 않을 수 있습니다.

5. 결론

플라이웨이트 패턴은 메모리 사용을 최적화하고 객체 생성 비용을 줄이는 강력한 도구입니다. 특히 대규모 애플리케이션에서 성능 병목을 해결하는 데 유용합니다. 하지만 상태 관리와 사용 환경에 주의해야 제대로 된 효과를 볼 수 있습니다. 😊


6. 관련 링크

반응형

1. 개요

템플릿 메서드 패턴은 알고리즘의 뼈대를 정의하고, 세부 구현은 서브클래스에서 결정하도록 만드는 디자인 패턴입니다. 동일한 구조를 유지하면서도 일부 기능을 커스터마이징할 수 있어 코드 재사용성과 유지보수성을 높입니다. 📜

2. 개념

이 패턴의 핵심은 상위 클래스에서 템플릿 메서드를 정의하고, 하위 클래스가 이 메서드에서 호출되는 세부 작업을 구현하도록 강제하는 것입니다.

구성 요소:

  1. Abstract Class: 템플릿 메서드를 정의하고 공통 동작(선택적으로 구현)을 제공합니다.
  2. Concrete Class: 구체적인 작업을 구현하며, 템플릿 메서드에서 호출됩니다.

템플릿 메서드를 사용하면 중복 코드를 최소화하면서도 공통된 알고리즘 구조를 보장할 수 있습니다. 🛠️

3. 예제

C#에서 템플릿 메서드 패턴을 사용한 간단한 예제입니다. "게임 캐릭터가 특정 행동을 수행"하는 상황을 가정합니다.

using System;

// Abstract Class
abstract class GameCharacter
{
    // Template Method
    public void PerformAction()
    {
        StartAction();
        ExecuteAction();
        EndAction();
    }

    protected abstract void StartAction();
    protected abstract void ExecuteAction();
    protected abstract void EndAction();
}

// Concrete Class 1
class Warrior : GameCharacter
{
    protected override void StartAction()
    {
        Console.WriteLine("전사가 무기를 준비합니다.");
    }

    protected override void ExecuteAction()
    {
        Console.WriteLine("전사가 적을 공격합니다.");
    }

    protected override void EndAction()
    {
        Console.WriteLine("전사가 무기를 정리합니다.");
    }
}

// Concrete Class 2
class Mage : GameCharacter
{
    protected override void StartAction()
    {
        Console.WriteLine("마법사가 주문을 외우기 시작합니다.");
    }

    protected override void ExecuteAction()
    {
        Console.WriteLine("마법사가 강력한 마법을 발사합니다.");
    }

    protected override void EndAction()
    {
        Console.WriteLine("마법사가 마법책을 닫습니다.");
    }
}

class Program
{
    static void Main(string[] args)
    {
        GameCharacter warrior = new Warrior();
        GameCharacter mage = new Mage();

        warrior.PerformAction();
        Console.WriteLine();
        mage.PerformAction();
    }
}

출력:

전사가 무기를 준비합니다.
전사가 적을 공격합니다.
전사가 무기를 정리합니다.

마법사가 주문을 외우기 시작합니다.
마법사가 강력한 마법을 발사합니다.
마법사가 마법책을 닫습니다.

이 코드에서 PerformAction 메서드는 알고리즘의 뼈대를 정의하며, 각 단계는 하위 클래스에서 구체화됩니다. 🛠️

4. 특이점 및 주의점

특이점

  • 알고리즘의 구조를 유지하면서도 각 단계의 구현을 유연하게 바꿀 수 있습니다.
  • 코드 중복을 방지하고, 알고리즘의 흐름을 명확하게 표현합니다.

주의점

  • 서브클래스가 너무 많아지면 관리가 어려워질 수 있습니다. 이를 방지하려면 적절한 계층 구조를 설계해야 합니다.
  • 알고리즘이 너무 단순하거나 변경 가능성이 적다면 오히려 과한 설계가 될 수 있습니다. 🚩

5. 결론

템플릿 메서드 패턴은 알고리즘의 뼈대를 유지하면서도 세부적인 구현을 유연하게 변경해야 할 때 유용합니다. 특히 게임, 웹 애플리케이션 등 다양한 도메인에서 반복적인 로직을 구조화하는 데 효과적입니다.

6. 관련 링크

반응형

1. 미디에이터 패턴이란?

객체들이 서로 직접 통신하지 않고, 중재자(미디에이터)를 통해 간접적으로 소통하도록 만드는 디자인 패턴입니다. 이를 통해 객체 간의 의존성을 줄이고, 시스템의 복잡도를 효과적으로 관리할 수 있습니다. 🌍

 

2. 어떻게 작동하나요?

객체들 간의 상호작용이 복잡해지면 코드 유지보수가 어려워질 수 있습니다. 미디에이터 패턴은 이런 문제를 해결하기 위해 중재자 역할의 객체를 도입하여 다음과 같은 구조를 만듭니다:

  • Colleague: 서로 상호작용하는 객체들.
  • Mediator: Colleague 간의 통신을 관리하는 중재자 객체.

Colleague 객체들은 Mediator를 통해서만 통신하며, 서로를 직접 참조하지 않습니다. 🔐

3. 실생활에서의 예

시나리오

채팅 애플리케이션에서 여러 사용자(User) 객체가 있고, 이들은 채팅룸(ChatRoom)을 통해 메시지를 주고받는 구조를 설계한다고 가정해봅시다.

코드 구현 (C#)

using System;
using System.Collections.Generic;

// Mediator 인터페이스
public interface IChatRoomMediator
{
    void SendMessage(string message, string userId);
    void RegisterUser(User user);
}

// Concrete Mediator
public class ChatRoom : IChatRoomMediator
{
    private Dictionary<string, User> _users = new Dictionary<string, User>();

    public void RegisterUser(User user)
    {
        if (!_users.ContainsKey(user.Id))
        {
            _users[user.Id] = user;
            user.SetChatRoom(this);
        }
    }

    public void SendMessage(string message, string userId)
    {
        if (_users.ContainsKey(userId))
        {
            _users[userId].Receive(message);
        }
    }
}

// Colleague
public class User
{
    public string Id { get; }
    public string Name { get; }
    private IChatRoomMediator _chatRoom;

    public User(string id, string name)
    {
        Id = id;
        Name = name;
    }

    public void SetChatRoom(IChatRoomMediator chatRoom)
    {
        _chatRoom = chatRoom;
    }

    public void Send(string message, string userId)
    {
        Console.WriteLine($"{Name} sends: {message}");
        _chatRoom.SendMessage(message, userId);
    }

    public void Receive(string message)
    {
        Console.WriteLine($"{Name} receives: {message}");
    }
}

// 사용 예
class Program
{
    static void Main(string[] args)
    {
        var chatRoom = new ChatRoom();

        var user1 = new User("1", "Alice");
        var user2 = new User("2", "Bob");

        chatRoom.RegisterUser(user1);
        chatRoom.RegisterUser(user2);

        user1.Send("Hello, Bob!", "2");
        user2.Send("Hi, Alice!", "1");
    }
}

결과

Alice sends: Hello, Bob!
Bob receives: Hello, Bob!
Bob sends: Hi, Alice!
Alice receives: Hi, Alice!

4. 이 패턴을 사용할 때 주의할 점

  • 단일 책임 원칙 준수: Mediator가 지나치게 많은 책임을 가지면 또 다른 복잡성을 초래할 수 있습니다.
  • 적합한 상황에 사용: 객체 간의 상호작용이 단순하다면, 굳이 미디에이터를 도입하지 않아도 됩니다.

5. 마무리

미디에이터 패턴은 객체 간의 의존성을 줄이고 복잡한 상호작용을 단순화하는 데 효과적입니다. 특히, 확장 가능하고 유지보수성이 높은 시스템을 설계할 때 유용합니다. 🚀

6. 참고 자료

반응형

1. Builder 패턴이란? 🛠️

Builder 패턴은 객체 생성 과정에서 복잡성을 줄이고, 유연성을 제공하는 디자인 패턴입니다. 특히 다양한 속성을 가진 객체를 생성해야 하거나, 동일한 생성 프로세스를 통해 여러 종류의 객체를 생성해야 할 때 유용합니다.

"객체를 생성하는 방식을 분리하여 코드의 가독성과 재사용성을 높이자!"


2. Builder 패턴의 구성 요소 ✨

Builder 패턴은 객체 생성 코드를 단계별로 분리하여, 클라이언트가 객체 생성 과정에 대해 더 큰 제어권을 갖도록 설계되었습니다. 핵심은 복잡한 객체의 생성 과정을 캡슐화하여 클라이언트와 분리하는 것입니다.

구성 요소:

  • Builder: 객체 생성 단계를 정의하는 인터페이스.
  • ConcreteBuilder: Builder를 구현하며, 생성 단계별 세부 구현을 담당.
  • Director: 객체 생성 과정을 제어하며, Builder를 사용하여 최종 객체를 생성.
  • Product: 최종적으로 생성되는 객체.

3. Builder 패턴 예제 🖥️

예제: 캐릭터 생성기 (Unity3D)

게임에서 플레이어 캐릭터를 생성할 때, 다양한 장비와 스탯을 가진 캐릭터를 만들어야 한다고 가정합니다.

// Product: 생성될 객체
public class Character {
    public string Name { get; set; }
    public string Weapon { get; set; }
    public string Armor { get; set; }
    public int Health { get; set; }

    public override string ToString() {
        return $"Character: {Name}, Weapon: {Weapon}, Armor: {Armor}, Health: {Health}";
    }
}

// Builder: 생성 과정 인터페이스
public interface ICharacterBuilder {
    void SetName(string name);
    void SetWeapon(string weapon);
    void SetArmor(string armor);
    void SetHealth(int health);
    Character GetCharacter();
}

// ConcreteBuilder: 구체적인 구현
public class WarriorBuilder : ICharacterBuilder {
    private Character _character = new Character();

    public void SetName(string name) {
        _character.Name = name;
    }

    public void SetWeapon(string weapon) {
        _character.Weapon = weapon;
    }

    public void SetArmor(string armor) {
        _character.Armor = armor;
    }

    public void SetHealth(int health) {
        _character.Health = health;
    }

    public Character GetCharacter() {
        return _character;
    }
}

// Director: 생성 제어
public class CharacterDirector {
    private readonly ICharacterBuilder _builder;

    public CharacterDirector(ICharacterBuilder builder) {
        _builder = builder;
    }

    public Character Construct(string name, string weapon, string armor, int health) {
        _builder.SetName(name);
        _builder.SetWeapon(weapon);
        _builder.SetArmor(armor);
        _builder.SetHealth(health);
        return _builder.GetCharacter();
    }
}

// 사용 예
public class Example {
    public static void Main(string[] args) {
        ICharacterBuilder builder = new WarriorBuilder();
        CharacterDirector director = new CharacterDirector(builder);

        Character warrior = director.Construct("Warrior", "Sword", "Steel Armor", 100);
        Console.WriteLine(warrior);
    }
}

출력:

Character: Warrior, Weapon: Sword, Armor: Steel Armor, Health: 100

4. Builder 패턴 사용 시 고려할 점 ⚠️

  1. 장점:
    • 복잡한 객체 생성 로직을 분리하여 코드 가독성 향상.
    • 동일한 생성 로직을 사용하여 다양한 객체를 생성 가능.
  2. 단점:
    • 클래스가 많아져 구조가 복잡해질 수 있음.
    • 단순한 객체 생성에는 오히려 과도한 설계가 될 수 있음.
  3. 유의점:
    • Director의 사용은 선택 사항입니다. 필요에 따라 Builder만으로 객체를 생성할 수 있습니다.

5. Builder 패턴 요약 🏁

Builder 패턴은 복잡한 객체 생성 과정을 단순화하고, 코드의 재사용성과 유지 보수성을 높이는 데 큰 도움을 줍니다. 특히 Unity3D 같은 프레임워크에서 다양한 요소를 조합하여 객체를 생성해야 할 때 강력한 도구가 됩니다. 하지만 모든 상황에서 적합한 것은 아니므로, 사용 시 목적과 필요성을 잘 판단해야 합니다.


6. Builder 패턴 참고 자료 🔗

반응형

1. Proxy 패턴이란?

Proxy 패턴은 객체에 접근하는 인터페이스를 대리 객체를 통해 제공하는 디자인 패턴입니다. 이를 통해 원래 객체의 역할을 수행하면서도 추가적인 제어나 기능을 부여할 수 있습니다. 주로 성능 최적화, 접근 제어, 또는 원격 호출을 단순화하는 데 활용됩니다. 🤖

 


2. Proxy 패턴의 구성 요소

Proxy는 진짜 객체(Real Subject)와 동일한 인터페이스를 구현하는 대리 객체(Proxy)를 제공합니다. 이를 통해 사용자는 진짜 객체에 직접 접근하지 않고도 동일한 방식으로 작업을 수행할 수 있습니다. Proxy는 진짜 객체에 추가적인 로직을 삽입할 수 있는 좋은 위치가 됩니다.

Proxy 패턴의 주요 구성 요소는 다음과 같습니다:

  • Subject: 원래 객체와 Proxy가 따르는 인터페이스.
  • Real Subject: 실제 작업을 수행하는 객체.
  • Proxy: Real Subject에 접근을 제어하거나 추가 로직을 제공하는 객체.

예: 게임에서 텍스처와 같은 리소스를 로드할 때, 로딩을 지연시키는 Proxy 객체를 사용해 효율성을 높일 수 있습니다. 🎮


3. Proxy 패턴 예제🖥️

Unity3D 예제: 리소스 로드 지연

게임에서 무거운 리소스를 효율적으로 관리하기 위해 Proxy 패턴을 사용할 수 있습니다. 아래는 텍스처 로딩을 지연시키는 Proxy 패턴의 예제입니다.

public interface ITexture
{
    void Display();
}

// Real Subject
public class RealTexture : ITexture
{
    private string _filePath;

    public RealTexture(string filePath)
    {
        _filePath = filePath;
        LoadTextureFromDisk();
    }

    private void LoadTextureFromDisk()
    {
        Debug.Log($"Loading texture from {_filePath}...");
    }

    public void Display()
    {
        Debug.Log("Displaying texture.");
    }
}

// Proxy
public class ProxyTexture : ITexture
{
    private string _filePath;
    private RealTexture _realTexture;

    public ProxyTexture(string filePath)
    {
        _filePath = filePath;
    }

    public void Display()
    {
        if (_realTexture == null)
        {
            _realTexture = new RealTexture(_filePath); // 실제 로드 지연
        }
        _realTexture.Display();
    }
}

// 사용 예시
public class TextureLoader : MonoBehaviour
{
    void Start()
    {
        ITexture texture = new ProxyTexture("/path/to/texture.png");

        Debug.Log("Texture is not loaded yet.");
        texture.Display(); // 이 시점에서 로드 및 표시
    }
}

이 코드는 리소스를 필요할 때만 로드하는 **지연 로딩(lazy loading)**을 구현합니다. 🌟


4. Proxy 패턴 사용 시 주의점

  • 추가 복잡성: Proxy 객체를 추가하면 설계가 복잡해질 수 있습니다. 필요한 경우에만 사용하는 것이 좋습니다.
  • 성능 비용: Proxy 자체가 추가 작업을 수행하기 때문에, 잘못 구현하면 성능이 저하될 수 있습니다.
  • 메모리 관리: Proxy와 Real Subject 간의 관계를 잘 관리해야 메모리 누수를 방지할 수 있습니다. 🧠

5. 결론

Proxy 패턴은 객체 접근을 제어하고, 성능 및 효율성을 최적화할 수 있는 유용한 도구입니다. 특히 Unity3D와 같은 환경에서 리소스 관리를 개선하는 데 큰 도움이 될 수 있습니다. 하지만, 설계의 복잡도를 높이지 않도록 적재적소에 사용하는 것이 중요합니다. 👍


6. 관련 링크

반응형

+ Recent posts