[CS/Design Pattern] GoF 디자인 패턴 | 구조 패턴(Structural Pattern) 7가지 정리

2024. 7. 15. 18:21·💾 Computer Science/Software Engineering

 

☁ 패턴들의 개념과 각 패턴을 적용해서 간단한 예시를 🕹게임 캐릭터를 만들 듯이 C#으로 구현해 보았다. 

 

 

 

모든 패턴이 SOLID 원칙과 연관이있다!

 

 

 

 

 참고 | GoF 디자인 패턴 총정리 

 

GoF 디자인 패턴의 생성, 구조, 행위 패턴에 대한 총정리는 아래 포스팅을 참고하자!

[CS/Design Pattern] GoF 디자인 패턴 정리

 

[CS/Design Pattern] GoF 디자인 패턴 정리

☁ 한창 정보처리기사를 준비할 때 공부했던 내용에 정보를 더 추가해서 알차게 정리해 보았다!   디자인 패턴(Design Pattern)이란?디자인 패턴은 소프트웨어 디자인에서 자주 발생하는 문제들에

mojing.tistory.com

 


 

구조 패턴 (Structural Pattern)

구조 패턴은 클래스와 객체를 조합하여 더 큰 구조를 만드는 방법에 대한 패턴이다.

 

구조 패턴에는 7가지 패턴이 있다.

  1. 어댑터 (Adapter) 패턴
  2. 브리지 (Bridge) 패턴
  3. 컴포지트 (Composite) 패턴
  4. 데코레이터 (Decorator) 패턴
  5. 퍼사드 (Facade) 패턴
  6. 플라이웨이트 (Flyweight) 패턴
  7. 프록시 (Proxy) 패턴

이 패턴들은 코드의 재사용성 및 유지보수성과 시스템의 효율성을 높여준다.

 

 

 

 

 

1. 어댑터 (Adapter)

어댑터 패턴은 호환성 없는 클래스들의 인터페이스를 다른 클래스가 이용할 수 있도록 변환하는 패턴이다.

 

즉, 인터페이스가 호환되지 않는 클래스들을 연결해 주므로, 기존 클래스의 인터페이스를 변경하지 않고 다른 인터페이스를 사용해야 할 때 유용하다.

 

 

 

장점

  • 기존 코드나 서드파티 라이브러리와의 호환성을 높인다.
  • 코드의 재사용성을 높여 개발 시간을 단축시킬 수 있다.
  • 단일 책임 원칙 (SRP)
    • 어댑터 클래스는 기존의 클래스와 새로운 인터페이스 간의 변환을 담당하여 단일 책임을 가진다.
  • 개방-폐쇄 원칙 (OCP)
    • 기존 코드를 수정하지 않고 새로운 인터페이스를 추가할 수 있다.

 

 

단점

  • 어댑터 클래스가 많아질 경우, 코드 복잡성이 증가한다.
  • 어댑터의 성능 오버헤드가 발생할 수 있다.

 

 

 

 예시 코드 | 어댑터 (Adapter) 

  • 게임에서 오래된 전투 시스템을 새로운 전투 시스템과 동시에 사용할 수 있게 코드에 적용해 보았다.
  • 어댑터 클래스인 BattleSystemAdapter는 INewBattleSystem을 IOldBattleSystem에 맞게 변환하여 호환성을 제공한다.
  • 클라이언트는 BattleSystemAdapter를 통해 새로운 전투 시스템을 사용한다.
//.# 오래된 전투 시스템 인터페이스
public interface IOldBattleSystem {
    void Attack();
}

//.# 오래된 전투 시스템 구현
public class OldBattleSystem : IOldBattleSystem {
    public void Attack() { Console.WriteLine("🥊오래된 전투 시스템: 공격!"); }
}

//.# 새로운 전투 시스템 인터페이스
public interface INewBattleSystem {
    void PerformAttack();
}

//.# 새로운 전투 시스템 구현
public class NewBattleSystem : INewBattleSystem {
    public void PerformAttack() { Console.WriteLine("🏹새로운 전투 시스템: 공격!"); }
}

//.# 어댑터 클래스 : 기존 전투시스템을 오래된 전투시스템에 맞게 변환
public class BattleSystemAdapter : IOldBattleSystem {
    private readonly INewBattleSystem _newBattleSystem;

    public BattleSystemAdapter(INewBattleSystem newBattleSystem) {
        _newBattleSystem = newBattleSystem;
    }

    public void Attack() {
        _newBattleSystem.PerformAttack();
    }
}

//.# 클라이언트
class Program {
    static void Main(string[] args) {
        IOldBattleSystem oldBattleSystem = new OldBattleSystem();
        oldBattleSystem.Attack();  // 출력: 🥊오래된 전투 시스템: 공격!

        INewBattleSystem newBattleSystem = new NewBattleSystem();
        IOldBattleSystem adapter = new BattleSystemAdapter(newBattleSystem);
        adapter.Attack();  // 출력: 🏹새로운 전투 시스템: 공격!
    }
}

 

 

 

 

 

2. 브리지 (Bridge)

브리지 패턴은 구현부에서 추상층을 분리하여 서로가 독립적으로 확장할 수 있도록 구현하는 패턴이다.

 

즉, 추상화와 구현을 각각 별도의 클래스 계층 구조로 분리하여, 두 계층 사이의 결합도를 낮추고 유연성을 확보한다.

 

 

 

장점

  • 추상화와 구현의 분리로 인해 각각의 변화에 대응하기 쉬워진다.
  • 개방-폐쇄 원칙 (OCP)
    • 추상화와 구현의 분리로 인해 확장에는 열려 있고 수정에는 닫혀 있다.
    • 확장성이 높아져 새로운 기능을 추가하거나 변경할 때 유연하게 대응할 수 있다.
  • 의존성 역전 원칙 (DIP)
    • 추상화에 의존하도록 설계되어 구현 세부사항에 대한 직접적인 의존을 줄일 수 있다.

 

 

단점

  • 설계가 복잡해질 수 있고, 필요 이상으로 추상화할 경우 오버헤드가 발생할 수 있다.

 

 

 

 예시 코드 | 브리지 (Bridge) 

  • 게임에서 다양한 캐릭터와 무기를 독립적으로 구현하는 코드에 적용해 보았다.
  • Character 클래스는 무기를 사용하여 전투를 수행하는 추상 클래스로, 구체적인 캐릭터(Warrior, Archer)는 이 클래스를 상속받아서 구현한다.
  • 무기와 캐릭터는 독립적으로 변경될 수 있어 유연성이 높다.
//.# 무기 인터페이스
public interface IWeapon {
    void Use();
}

//.# 구체적인 무기 구현
public class Sword : IWeapon {
    public void Use() { Console.WriteLine("🔸검을 사용했습니다!"); }
}

public class Bow : IWeapon {
    public void Use() { Console.WriteLine("🔹활을 사용했습니다!"); }
}

//.# 캐릭터 클래스
public abstract class Character {
    protected IWeapon weapon;

    public Character(IWeapon weapon) {
        this.weapon = weapon;
    }

    public abstract void Fight();
}

//.# 구체적인 캐릭터 구현 : Character를 상속받아 각각의 전투 방식을 정의
public class Warrior : Character {
    public Warrior(IWeapon weapon) : base(weapon) {}

    public override void Fight() {
        Console.Write("🔸전사: ");
        weapon.Use();
    }
}

public class Archer : Character {
    public Archer(IWeapon weapon) : base(weapon) {}

    public override void Fight() {
        Console.Write("🔹궁수: ");
        weapon.Use();
    }
}

//.# 클라이언트
class Program {
    static void Main(string[] args) {
        IWeapon sword = new Sword();
        IWeapon bow = new Bow();

        Character warrior = new Warrior(sword);
        warrior.Fight();  // 출력: 🔸전사: 🔸검을 사용했습니다!

        Character archer = new Archer(bow);
        archer.Fight();  // 출력: 🔹궁수: 🔹활을 사용했습니다!

        Character archer2 = new Archer(sword);
        archer2.Fight();  // 출력: 🔹궁수: 🔸검을 사용했습니다!
    }
}

 

 

 

 

 

3. 컴포지트 (Composite)

컴포지트 패턴은 복합 객체와 단일 객체를 구분 없이 다룰 때 사용하는 패턴이다.

 

이 패턴은 객체들을 트리 구조로 구성하므로, 단일 객체와 복합 객체를 동일하게 취급해야 할 때 유용하다.

즉, 클라이언트에서 개별 객체와 복합 객체를 구별하지 않고 사용할 수 있게 된다.

 

 

 

장점

  • 단일 객체와 복합 객체를 일관되게 처리할 수 있다.
  • 계층 구조를 관리하기 용이하고, 재귀적인 구조를 갖추어 복잡한 구조를 쉽게 구현할 수 있다.
  • 개방-폐쇄 원칙 (OCP)
    • 구성 요소의 추가나 변경이 쉽다.
  • 단일 책임 원칙 (SRP)
    • Leaf와 Composite의 역할을 명확히 분리하여 단일 책임을 부여할 수 있다.

 

 

단점

  • Leaf와 Composite 객체를 구별하기 위한 추가적인 로직이 필요할 수 있다.
  • 구성 요소마다 공통 인터페이스를 갖추어야 하므로 설계를 신중하게 해야 한다.

 

 

 

 예시 코드 | 컴포지트 (Composite) 

  • 게임의 퀘스트(복합 퀘스트)와 서브 퀘스트를 구현하는 코드에 적용해 보았다.
  • CompositeQuest는 IQuest를 구현하여 서브 퀘스트들을 트리 구조로 구성하고, Display()를 호출하여 출력할 때 모든 서브 퀘스트를 재귀적으로 출력한다.
  • 클라이언트에서 메인 퀘스트에 서브 퀘스트들을 포함시킨 다음, 메인 퀘스트의 이름을 출력시키면 포함되어 있는 서브 퀘스트들도 전부 동일하게 출력되는 모습을 볼 수 있다. 
//.# 퀘스트 인터페이스 : 퀘스트의 공통 동작 정의
public interface IQuest {
    void Display();
}

//.# 기본 퀘스트 클래스 : 단일 퀘스트
public class SimpleQuest : IQuest {
    private string name;

    public SimpleQuest(string name) {
        this.name = name;
    }

    public void Display() { Console.WriteLine(name); }
}

//.# 복합 퀘스트 클래스 : 여러 서브 퀘스트를 포함할 수 있음
public class CompositeQuest : IQuest {
    private string name;
    private List<IQuest> subQuests = new List<IQuest>();

    public CompositeQuest(string name) {
        this.name = name;
    }

    public void AddSubQuest(IQuest quest) {
        subQuests.Add(quest);
    }

    public void Display() {
        Console.WriteLine(name);
        foreach (var quest in subQuests) {
            quest.Display();
        }
    }
}

//.# 클라이언트
class Program {
    static void Main(string[] args) {
        IQuest mainQuest = new CompositeQuest("💾메인 퀘스트");

        IQuest subQuest1 = new SimpleQuest("👀서브 퀘스트 1");
        IQuest subQuest2 = new SimpleQuest("👀서브 퀘스트 2");

        ((CompositeQuest)mainQuest).AddSubQuest(subQuest1);
        ((CompositeQuest)mainQuest).AddSubQuest(subQuest2);

        mainQuest.Display();
        // 출력:
        // 💾메인 퀘스트
        // 👀서브 퀘스트 1
        // 👀서브 퀘스트 2
    }
}

 

 

 

 

 

4. 데코레이터 (Decorator)

데코레이터 패턴은 객체에 객체를 결합시켜 기능을 확장하는 패턴이다.

 

즉, 임의의 객체에 다른 객체들을 덧붙여서 부가적인 기능을 추가하는 방식으로, 기본 객체를 확장할 때 유용하다.

 

 

 

장점

  • 상속을 통해 제한되는 클래스의 수를 줄이고, 객체를 구성함으로써 기능을 추가할 수 있다.
  • 개방-폐쇄 원칙 (OCP)
    • 기본 객체의 수정 없이 새로운 기능을 추가할 수 있다.
    • 즉, 기본 객체에 동적으로 기능을 추가할 수 있어 유연성이 높다.
  • 단일 책임 원칙 (SRP)
    • 데코레이터는 추가적인 기능만을 담당하므로 단일 책임 원칙(SRP)을 지킨다.

 

 

단점

  • 많은 수의 데코레이터가 적용될 경우 복잡해질 수 있다.
  • 객체가 깊게 중첩될 경우 코드 가독성이 떨어진다.

 

 

 

 예시 코드 | 데코레이터 (Decorator) 

  • 게임에서 힘 버프와 민첩 버프를 중첩시키는 코드에 적용해 보았다.
  • StrengthBuff와 AgilityBuff는 CharacterDecorator를 상속받아 캐릭터에 힘과 민첩 버프를 추가한다.
  • 클라이언트는 기본 캐릭터에 다양한 버프를 동적으로 추가할 수 있다.
//.# 캐릭터 인터페이스
public interface ICharacter {
    void ShowStats();
}

//.# 기본 캐릭터 클래스
public class BasicCharacter : ICharacter {
    public void ShowStats() { Console.WriteLine("🔹기본 캐릭터"); }
}

//.# 데코레이터 추상 클래스 : 캐릭터에 새로운 기능(버프)을 추가하는 기능
public abstract class CharacterDecorator : ICharacter {
    protected ICharacter character;

    public CharacterDecorator(ICharacter character) {
        this.character = character;
    }

    public virtual void ShowStats() {
        character.ShowStats();
    }
}

//.# 구체적인 데코레이터 : 힘, 민첩
public class StrengthBuff : CharacterDecorator {
    public StrengthBuff(ICharacter character) : base(character) {}

    public override void ShowStats() {
        base.ShowStats();
        Console.WriteLine("🥊힘 증가");
    }
}

public class AgilityBuff : CharacterDecorator {
    public AgilityBuff(ICharacter character) : base(character) {}

    public override void ShowStats() {
        base.ShowStats();
        Console.WriteLine("⚡민첩 증가");
    }
}

//.# 클라이언트
class Program {
    static void Main(string[] args) {
        ICharacter basicCharacter = new BasicCharacter();
        ICharacter strongCharacter = new StrengthBuff(basicCharacter);
        ICharacter agileStrongCharacter = new AgilityBuff(strongCharacter);

        basicCharacter.ShowStats();
        // 출력: 🔹기본 캐릭터

        strongCharacter.ShowStats();
        // 출력: 🔹기본 캐릭터
        //       🥊힘 증가

        agileStrongCharacter.ShowStats();
        // 출력: 🔹기본 캐릭터
        //       🥊힘 증가
        //       ⚡민첩 증가
    }
}

 

 

 

 

 

5. 퍼사드(Facade)

퍼사드 패턴은 복잡한 서브 클래스들을 피해 더 상위에 인터페이스를 구성하는 패턴이다.

 

즉, 클라이언트가 복잡한 시스템의 일부분에 직접 접근하는 대신, 간단한 인터페이스를 통해 접근하도록 한다.

이를 통해 시스템 간의 의존성을 줄이고, 클라이언트 코드를 단순화한다.

 

 

 

장점

  • 복잡한 시스템을 단순화하여 클라이언트가 쉽게 사용할 수 있다.
  • 서브시스템 변경에 대한 영향을 최소화하고, 결합도를 낮춘다.
  • 인터페이스 분리 원칙 (ISP)
    • 클라이언트에 필요한 최소한의 인터페이스를 제공함으로써 인터페이스 분리 원칙(ISP)을 준수할 수 있다.
  • 의존성 역전 원칙 (DIP)
    • 클라이언트는 퍼사드 인터페이스에만 의존한다.

 

 

단점

  • 퍼사드 클래스가 너무 많은 기능을 갖게 되면 단일 책임 원칙을 위반할 수 있다.
  • 서브시스템의 세부 사항을 완전히 숨기기 어려운 경우가 생길 수 있다

 

 

 

 예시 코드 | 퍼사드 (Facade) 

  • 게임에서 복잡한 게임 시작 과정(에셋 로드, 설정 초기화, 게임 시작 등등)에 적용해 보았다.
  • 클라이언트는 GameFacade를 사용하여 복잡한 게임 시작 과정을 단순화한다.
//.# 하위 시스템 클래스 : "에셋 로드", "설정 초기화", "게임 시작"
public class GameLoader {
    public void LoadAssets() { Console.WriteLine("💾.1 게임 에셋 로드"); }
}

public class GameInitializer {
    public void InitializeSettings() { Console.WriteLine("💾.2 게임 설정 초기화"); }
}

public class GameStarter {
    public void StartGame() { Console.WriteLine("💾.3 게임 시작❕"); }
}

//.# 퍼사드 클래스 : 하위 시스템을 통합하여 게임 시작을 간소화
public class GameFacade {
    private GameLoader loader;
    private GameInitializer initializer;
    private GameStarter starter;

    public GameFacade() {
        loader = new GameLoader();
        initializer = new GameInitializer();
        starter = new GameStarter();
    }

    public void StartGame() {
        loader.LoadAssets();
        initializer.InitializeSettings();
        starter.StartGame();
    }
}

//.# 클라이언트
class Program {
    static void Main(string[] args) {
        GameFacade game = new GameFacade();
        game.StartGame();
        // 출력:
        // 💾.1 게임 에셋 로드
        // 💾.2 게임 설정 초기화
        // 💾.3 게임 시작❕
    }
}

 

 

 

 

 

6. 플라이웨이트 (Flyweight)

플라이웨이트 패턴은 유사 객체들 사이에 가능한 많은 데이터를 공유해 사용하는 패턴이다.

 

즉, 객체를 공유하여 메모리 사용량을 줄이므로, 많은 수의 유사한 객체들을 효율적으로 관리해야 할 때 유용하다.

 

 

 

장점

  • 메모리 사용량을 줄이고 성능 개선에 도움이 된다.
  • 객체의 공유로 인해 객체 생성 비용을 절감할 수 있다.
  • 개방-폐쇄 원칙 (OCP)
    • 객체의 공유로 인해 확장성이 좋다.
  • 단일 책임 원칙 (SRP)
    • 객체의 상태와 동작을 명확히 분리하기 때문에 단일 책임 원칙을 준수한다.

 

 

단점

  • 객체의 상태가 공유되기 때문에, 변경 시 모든 객체에 영향을 줄 수 있다.
  • 객체의 공유로 인해 다중 스레드 환경에서 동기화 문제가 발생할 수 있다.

 

 

 

 예시 코드 | 플라이웨이트 (Flyweight) 

  • 게임에서 동일한 종류의 몬스터를 공유하도록 코드에 적용해 보았다.
  • MonsterFactory 클래스는 몬스터를 생성하고, 동일한 타입의 몬스터를 공유하므로 메모리 사용을 줄인다.
  • 클라이언트는 MonsterFactory를 사용하여 동일한 몬스터 객체를 재사용할 수 있다.
//.# 몬스터 클래스 : 몬스터의 타입을 저장하고 위치를 출력함
public class Monster {
    public string Type { get; private set; }

    public Monster(string type) {
        Type = type;
    }

    public void Display(int x, int y) { Console.WriteLine($"👀몬스터 {Type} 위치: ({x}, {y})"); }
}

//.# 몬스터 팩토리 클래스 : 몬스터를 생성하고, 동일한 타입의 몬스터를 공유함
public class MonsterFactory {
    private Dictionary<string, Monster> monsters = new Dictionary<string, Monster>();

    public Monster GetMonster(string type) {
        if (!monsters.ContainsKey(type)) {
            monsters[type] = new Monster(type);
        }
        return monsters[type];
    }
}

//.# 클라이언트
class Program {
    static void Main(string[] args) {
        MonsterFactory factory = new MonsterFactory();

        Monster goblin1 = factory.GetMonster("💀고블린");
        Monster goblin2 = factory.GetMonster("💀고블린");
        Monster dragon = factory.GetMonster("🐉드래곤");

        goblin1.Display(1, 2);
        goblin2.Display(3, 4);
        dragon.Display(5, 6);

        // 출력:
        // 👀몬스터 💀고블린 위치: (1, 2)
        // 👀몬스터 💀고블린 위치: (3, 4)
        // 👀몬스터 🐉드래곤 위치: (5, 6)
    }
}

 

 

 

 

 

7. 프록시 (Proxy)

프록시 패턴은 접근이 어려운 객체에 대한 인터페이스 역할을 하는 패턴이다.

 

로딩을 지연시키거나 접근 제어를 추가해야 할 때 유용하다.

 

 

 

장점

  • 객체에 대한 접근을 제어하고, 필요할 때만 실제 객체를 생성한다.
  • 클라이언트에 대한 추가적인 기능을 제공할 수 있다! (예: 보안 검사, 캐싱 등).
  • 개방-폐쇄 원칙 (OCP)
    • 클라이언트와 실제 서비스 객체 사이의 의존성을 낮춘다.

 

 

단점

  • 프록시와 실제 객체의 인터페이스가 동일해야 하므로 설계가 복잡해질 수 있다.
  • 프록시의 사용이 과도할 경우 성능 저하가 발생할 수 있다.

 

 

 

 예시 코드 | 프록시 (Proxy) 

  • 게임에서 플레이어의 정보에 접근을 제어하는 코드에 적용해 보았다.
  • 클라이언트에서 player.DisplayInfo() 로 호출할 때, 객체가 생성이 되면서 RealPlayer 클래스의 생성자가 호출되는 모습을 볼 수 있다.
  • 프록시는 실제 객체를 필요할 때까지 생성하지 않으며, 클라이언트가 PlayerProxy를 통해 실제 플레이어 정보에 접근할 수 있도록 한다.
//.# 플레이어 인터페이스
public interface IPlayer {
    void DisplayInfo();
}

//.# 실제 플레이어 클래스 : 플레이어 정보를 관리하고 출력
public class RealPlayer : IPlayer {
    private string name;

    public RealPlayer(string name) {
        this.name = name;
        LoadFromDatabase(name);
    }

    private void LoadFromDatabase(string name) {
        Console.WriteLine($"💾플레이어 {name}의 정보를 데이터베이스에서 로드");
    }

    public void DisplayInfo() { Console.WriteLine($"🏹플레이어: {name}"); }
}

//.# 프록시 클래스 : RealPlayer에 대한 접근을 제어
public class PlayerProxy : IPlayer {
    private RealPlayer realPlayer;
    private string name;

    public PlayerProxy(string name) {
        this.name = name;
    }

    public void DisplayInfo() {
        if (realPlayer == null) {
            realPlayer = new RealPlayer(name);
        }
        realPlayer.DisplayInfo();
    }
}

//.# 클라이언트
class Program {
    static void Main(string[] args) {
        IPlayer player = new PlayerProxy("김프록시");
        player.DisplayInfo();
        // 출력:
        // 💾플레이어 김프록시의 정보를 데이터베이스에서 로드
        // 🏹플레이어: 김프록시

        player.DisplayInfo();
        // 출력:
        // 🏹플레이어: 김프록시
    }
}

 




❓ 뭔가 비슷한 패턴들이 많은데 차이점이 정확히 뭘까?

 

예를 들어, 데코레이터와 프록시 패턴은 구조가 매우 비슷하다. 두 패턴 모두 한 객체가 일부 작업을 다른 객체에 위임한다는 점에서 거의 일치한다고 생각할 수 있다. 

하지만, 의도 관점에서 차이점이 있다.

데코레이터의 객체 간의 합성은 항상 클라이언트에서 제어가 되지만, 프록시는 자체적으로 자신의 서비스 객체의 수명 주기를 관리하는 게 일반적이다.

 

💣이런 식으로 구조가 매우 비슷해 보이는 패턴이 여러 가지일 수 있다. 

그때는 🔽아래의 링크("리팩토링 구루") 🔽 를 참고하면 더 자세히 알 수 있다!! 

 

 

 

 참고 

 

https://refactoring.guru/ko/design-patterns/structural-patterns

 

구조 디자인 패턴

 

refactoring.guru

 

저작자표시 비영리 변경금지 (새창열림)

'💾 Computer Science > Software Engineering' 카테고리의 다른 글

[CS/Design Pattern] GoF 디자인 패턴 | 행위 패턴(Behavioral Pattern) 11가지 정리  (0) 2024.07.18
[CS/Design Pattern] GoF 디자인 패턴 | 생성 패턴(Creational Pattern) 5가지 정리  (0) 2024.07.10
[CS/Design Pattern] GoF 디자인 패턴 정리  (0) 2024.07.09
'💾 Computer Science/Software Engineering' 카테고리의 다른 글
  • [CS/Design Pattern] GoF 디자인 패턴 | 행위 패턴(Behavioral Pattern) 11가지 정리
  • [CS/Design Pattern] GoF 디자인 패턴 | 생성 패턴(Creational Pattern) 5가지 정리
  • [CS/Design Pattern] GoF 디자인 패턴 정리
  • [CS/OOP] 객체지향 프로그래밍과 절차지향 프로그래밍의 차이
Mojing_
Mojing_
매일 매일 경험치를 쌓는 모징이의 개발 블로그입니다 :) This is Mojing’s Dev Blog where she gain experience points every day. :)
  • Mojing_
    모징이의 개발 경험치
    Mojing_
  • 전체
    오늘
    어제
    • 분류 전체보기 (143)
      • 👻 Unity (5)
        • 🔧 기능 구현 (0)
        • 💡 유니티 팁 (0)
        • 📘 Unity 노트 (2)
      • 💻 Programming (14)
        • C (3)
        • C++ (9)
        • C# (0)
        • Swift (2)
      • 💾 Computer Science (16)
        • Algorithm (9)
        • Software Engineering (7)
      • 🐸 Problem Solving (108)
        • Programmers (41)
        • BOJ (67)
      • 🔋 ETC (0)
      • 💡 Quest Log (0)
  • 인기 글

  • 공지사항

  • 태그

    algorithm
    Unity
    Problem Solving
    티스토리챌린지
    C++
    오블완
    dynamic programming
    BOJ
    programmers
    프로그래머스
    sort
    DFS/BFS
    CS
    backtracking
    탐색
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Mojing_
[CS/Design Pattern] GoF 디자인 패턴 | 구조 패턴(Structural Pattern) 7가지 정리
상단으로

티스토리툴바