Q. 2자리 이상의 정수 number가 주어집니다. 주어진 코드는 이 수를 2자리씩 자른 뒤,

자른 수를 모두 더해서 그 합을 출력하는 코드입니다. 코드가 올바르게 작동하도록 한 줄을 수정해 주세요.

 

 

나는 number의 자릿수를 판단해서 그만큼 반복시키라는 의미인줄 알았는데

그저 조건식만 간단히 바꾸면 되는거였다...

 

너무 어렵게 생각하지 말자! 심플이즈베스트이다!

#include <iostream>

using namespace std;

int main(void) {
    int number;
    cin >> number;
    
    int answer = 0;
    
    // while로 한다면 while(number > 0) {
    for(int i = 0; number > 0; i++) {
        answer += number % 100;
        number /= 100;
    }
    cout << answer << endl;
    return 0;
}

 

기초부터 탄탄하게 하기 위해 초심으로 돌아가서 문제를 풀어보도록 했다.

 

1. 대소문자 바꿔서 출력하기: ToUpper(), ToLower() 사용

public class Example
{

    public static string SwitchChar(string input) 
    {
        char[] sArray = input.ToCharArray(); 

        for (int i = 0; i < sArray.Length; i++) 
        {
            if (char.IsUpper(sArray[i])) 
            {
                sArray[i] = char.ToLower(sArray[i]);
            } 
            else if (char.IsLower(sArray[i])) 
            {
                sArray[i] = char.ToUpper(sArray[i]);
            }
        }

        return new string(sArray);
    }


    public static void Main()
    {
        String s;

        Console.Clear();
        s = Console.ReadLine();

        s = SwitchChar(s);        
        Console.Write(s);
    }
}

2. 특수문자 출력해보기

!@#$%^&*(\'"<>?:;

이걸 출력해야 한다 ㅋㅋㅋㅋ

using System;
using System.Collections.Generic;

public class Example
{
    public static void Main()
    {
        List<int> asciis = new List<int> {33,64,35,36,37,94,38,42,40,92,39,34,60,62,63,58,59};
        foreach (int c in asciis)
        {
            Console.Write((char) (c));
        }
    }
}

 

C#에서는 asciis 라는 것도 있어서 신기했다.


3. 입력받자마자 분리해버리기

string[] input = Console.ReadLine().Split(' ');

4. 문자열을 문자 배열로

	char[] input = Console.ReadLine().ToCharArray();
 	string output = string.Join("\n", input);

5. 문자열 겹쳐쓰기 문제

https://school.programmers.co.kr/learn/courses/30/lessons/181943

public class Solution {
    public string solution(string my_string, string overwrite_string, int s) {
        string answer = "";
        answer = my_string.Remove(s,overwrite_string.Length);
        answer = answer.Insert(s, overwrite_string);
        return answer;
    }
}

내가 쓴 코드...

내가 작성한 코드를 보면 난 한참 부족한거 같다 ㅋㅋㅋ

올해 2025년 학위 수여식까지 마치고 지난 4년간 (군대 포함 6년) 대학시절 일을 회상해보면

 

나 정도면 결코 일생이나 학업을 소홀히 하지 않았다고 생각했지만 큰 오산이였던거 같다...

 

결국 맞이하고 말았다

사실 이 짤과 같이 우매함의 봉우리, 절망의 계곡은 고등학생 때부터 알고 있었다

 

그래서 절대 저 절망의 계곡에 빠지지 않도록 열심히 달렸지만.... 결국 계곡을 피할 순 없었던거 같다...

 

난 안오겠지 했는데 취업 준비하면서 결국, 이 순간이 오고야 말았다..

하지만 뭐 어쩌겠는가 모두 다 겪어야 한다면 기왕이면 즐겨야지!

 

대학시절 교수님이 왜 루틴을 꾸준히 유지하라는지 알 거 같다

 

결국, 절망의 계곡은 누구나 거쳐야 하는 과정이었다.

그렇다면 중요한 건 피하려는 게 아니라, 그 속에서도 나를 단단히 만드는 루틴을 지키는 것이 아닐까?


 

 

내가 게임 쪽으로 마음을 결정하게 된 계기는 오직 게임만이 플레이어에게 경험을 줄 수있는 무언가가 있기 때문이였다

 

내 일생에서 영상 쪽과 게임 쪽을 선택해야 하는 순간이 왔는데

 

영상은 오직 보면서 들을 수 밖에 없지만 게임은 직접 내가 무엇을 주도할 수 있기 때문에 이 매력에 빠진 것이다

 

교수님이 그랬다. 영화는 '주인공과 내가(감상하고 있는 나 자신) 같이 여행하는 것'이라고

 

거기서 난 생각했다. '그럼 게임은 같이 여행하는 것 뿐만 아니라 내가 직접 느낄 수 있는 것이구나!'

 


네 실패를 마주하라

 

자신이 게임을 즐기는 사람이라면 인생 게임이 하나씩은 있을 것이다.

 

난 거기서 당당하게 말할 수 있는 게임이 있다. 바로 Darkest Dungeon 시리즈이다.

 

이 게임의 캐릭터들은 저마다 실패와 과오를 짊어지고 있으며 심지어

자신의 과거를 후회하는 범죄자 캐릭터들도 존재한다.

 

하지만 적과의 전투를 주도하는 이 순간만큼은 누구든 영웅이 될 수 있다. 이 부분에서 나를 완전히 사로잡았다.

 

특히 게임의 최종보스가 사용하는 기술인 네 실패를 마주하라(Face Your Failure) 라는 기술은

 

표면상으로는 자신의 실패를 직시함으로써 절망과 무력감을 주는 듯 하지만

 

진정한 의미는 오히려 정반대이다. 실패를 직시하는 순간,

 

이를 극복함으로써 비로소 성공할 기회를 얻는다. 그리고 그것이야말로 최고의 순간이다.


 

실패는 성공의 어머니, 오늘의 내가 내일의 나를 만든다. 라는 말은 수도 없이 들은 말이지만

 

이런식으로 보스의 전투 기술을 통해서 실패와 성공을 직접적으로 체험할 수 있기 때문에

 

커다란 충격과 감동과 인간찬가를 느꼈다.

 

실패를 두려워하지 말자. 항상 완벽해질려고 하지말자. 실패한다는 것만으로도 인간이란 아름다운 것이다.

 

- 오브젝트 풀링(Object Pooling)이란?

반복적으로 사용되는 객체를 효율적으로 관리하는 생성 패턴으로

게임을 예시로 들자면 슈팅, FPS, TPS 게임 같이 같은 탄환이 여러 개가 발사되는 경우

탄환 객체 생성을 최적화 하기 위해 적용하는 객체 재사용 기법이다!


- 오브젝트 풀링 예시 [Unity]

- 탄환 오브젝트 풀링 코드

public class BulletPool : MonoBehaviour
{
    [SerializeField]
    private GameObject bulletPrefab;
    [SerializeField]
    private int poolSize = 10;  // 풀 사이즈

    [SerializeField] 
    private Transform parentTransform;  // 부모 오브젝트의 Transform
    [SerializeField] List<GameObject> bulletPool;


    void Awake()
    {
        bulletPool = new List<GameObject>();

        for (int i = 0; i < poolSize; i++)
        {
            // GameObject bullet = Instantiate(bulletPrefab);
            GameObject bullet = Instantiate(bulletPrefab, parentTransform);  // 부모를 지정하여 총알 생성
            bullet.SetActive(false);
            bulletPool.Add(bullet);
        }
    }

    public GameObject GetBullet()
    {
        foreach (var bullet in bulletPool)
        {
            if (!bullet.activeInHierarchy)
            {
                bullet.SetActive(true);
                return bullet;
            }
        }
        GameObject newBullet = Instantiate(bulletPrefab);
        bulletPool.Add(newBullet);
        return newBullet;
    }

    public void ReturnBullet(GameObject bullet)
    {
        bullet.SetActive(false);
    }
}

오브젝트 풀링을 적용한 부분

1. 미리 총알을 프리팹으로 저장한다!  -   Awake( ) 부분

    void Awake()
    {
        bulletPool = new List<GameObject>();

        for (int i = 0; i < poolSize; i++)
        {
            // GameObject bullet = Instantiate(bulletPrefab);
            GameObject bullet = Instantiate(bulletPrefab, parentTransform);  // 부모를 지정하여 총알 생성
            bullet.SetActive(false);
            bulletPool.Add(bullet);
        }
    }

 

poolSize만큼 Instantiate()를 호출해서 총알 프리팹을 미리 생성해 리스트에 저장시킨다!


2. 비활성화 된 총알을 재사용 시킨다!  -   GetBullet() 부분

    public GameObject GetBullet()
    {
        foreach (var bullet in bulletPool)
        {
            if (!bullet.activeInHierarchy)
            {
                bullet.SetActive(true);
                return bullet;
            }
        }
        GameObject newBullet = Instantiate(bulletPrefab);
        bulletPool.Add(newBullet);
        return newBullet;
    }

 

foreach 문을 통해 Pool에서 비활성화된 총알을 찾아 활성화 후 반환시킨다!


3. 사용이 끝난 총알을 다시 비활성화 시킨다!   -   ReturnBullet() 부분

    public void ReturnBullet(GameObject bullet)
    {
        bullet.SetActive(false);
    }

SetActive(false)를 호출시켜서 다시 Pool에 반환한다는 뜻이다!


 

총알뿐만 아니라 등장하는 적이 같은 종류일때 오브젝트 풀링을 적용시킬 수 있다!

 

오브젝트 풀링 예시 테스트

 

- 프로토타입(Prototype) 패턴이란?

초간단 프로토타입 예시

 

게임으로 예시를 들자면 몬스터 원형이 있을거고 이 원형에서 변종인 녀석이 있다 치자

(몬스터 기본형, 정예형 몬스터)

 

정예형 몬스터일 경우 사용되는 패턴이나 기술 등은 유사하지만 능력치가 좀 더 강화된 특징을 갖는다면

새로운 몬스터 클래스를 만들필요없이 기본 객체를 복사해서 사용하는 방식이다!


- 프로토타입(Prototype)의 특징

 기존 객체를 기본값으로 활용하기에 매번 DB에서 불러오지 않아도 되서 개발 비용을 줄일 수 있다!

단 객체 간의 의존성이 클경우 관리하기 어려울 수 있다.

 


- 프로토타입(Prototype) 예제 만들어보자! (C#)

1. 먼저 몬스터 프로토타입 인터페이스를 정의한다.

    // 몬스터 프로토타입
    public abstract class MonsterPrototype 
    { 
        public string Name { get; set; }
        public int MaxHealth { get; set; }
        public int CurHealth { get; set; }
        public int Strength { get; set; }

        public abstract MonsterPrototype Clone();
    }

2. 몬스터 클래스를 만든다.

    public class Monster : MonsterPrototype
    {

        public Monster(string name, int health, int strength) 
        { 
            Name = name;
            MaxHealth = health;
            CurHealth = MaxHealth;
            Strength = strength;
        }

        public override MonsterPrototype Clone()
        {
            return (Monster)this.MemberwiseClone();
        }

        public void Show() {
            Console.WriteLine($"[몬스터] {Name} - 체력: {CurHealth}, 공격력: {Strength}");
        }
    }

3. Main에서 몬스터 프로토타입과 정예형 몬스터를 만든다.

    internal class Program
    {
        static void Main(string[] args)
        {
            // 기본 몬스터 원형
            Monster goblin = new Monster("고블린", 100, 15);
            goblin.Show();

            // 고블린 정예폼
            Monster goblin_Elite = (Monster)goblin.Clone();
            goblin_Elite.Name = "정예 고블린";  // 이름 변경 가능
            goblin_Elite.Strength += 10;        // 공격력 증가

            goblin_Elite.Show();
        }
    }

 

이 부분이 프로토타입 패턴을 사용한 예시이다!

Monster goblin_Elite = (Monster)goblin.Clone();

 

기존 고블린 객체복제해서 새로운 정예 고블린 객체(goblin_Elite)를 만들었다!

기존 객체를 복제해서 재사용하는 기법이다!

[Prototype 테스트 결과]

Builder를 간단하게 이해해보자!

 

 

RPG 게임에서 캐릭터가 가진 속성을 생각해보자

체력, 방어, 속도, 마나게이지 뿐만 아니라 가지고 있는 기술, 아직 해금되지 않는 기술들도

전부 캐릭터가 가진 속성이다. 이 캐릭터생성자로 만든다고 생각해보자.

 

- 생성자로 캐릭터를 만들 경우

new 캐릭터1("전사", "근접", 100, 50, 20, 150, ...);

 

이런식으로 생성할텐데, 일단 생성자에 너무 많은 매개변수가 포함되고, 속성의 순서를 기억하기가 어렵다!

이 코드를 만든 당사자면 이해할 순 있겠지만 프로그래밍에선 협업이 중요하다!

 

다른 사람이 내 코드를 봤을때 이 코드가 무엇인지 확실히 알아야 되는데 저 생성자 코드만 보고

캐릭터를 만들 때 무슨 속성을 설정할지 단번에 파악할 수 있을까?

 

- Builder 패턴을 적용할경우

Character warrior = new CharacterBuilder()
    .SetName("전사")
    .SetType("근접")
    .SetHealth(100)
    .SetDefense(50)
    .SetSpeed(20)
    .SetMana(150)
    .Build();

 

이 방법은 속성의 순서를 기억할 필요가 없고, 해당 속성이 무엇인지 명확해 보인다!

 


- Builder란 무엇인가?

복잡한 인스턴스를 조립하는 방식!

객체를 생성하는 방법(How) 객체를 구현하는 방법(What)분리하여,
동일한 생성 절차에서 다양한 표현 결과를 만들 수 있도록 하는 패턴이다.

 

복잡한 객체를 생성과 구현을 분리해서 관리해주는 느낌으로 생각하면 된다!


- Builder 패턴을 사용해보자! (c#)

1. 먼저 캐릭터 클래스를 설계한다!

캐릭터는 이름, 직업, 무기, 체력, 마나, 속도 속성이 있다!

    public class PlayerCharacter
    {
        public string Name { get; set; }
        public string Job { get; set; }
        public string Weapon { get; set; }
        public int Health { get; set; }
        public int Mana { get; set; }
        public int Speed { get; set; }

        public void PlayerShow()
        {
            Console.WriteLine($"[캐릭터 생성 완료] 이름: {Name}, 직업: {Job}, 무기: {Weapon}, " +
                $"체력: {Health}, 마나: {Mana}, 속도: {Speed}");
        }
    }

2. 빌더 인터페이스를 정의한다.

    // 빌더 인터페이스
    public interface ICharaBuilder
    {
        ICharaBuilder SetName(string name);
        ICharaBuilder SetJob(string job);
        ICharaBuilder SetWeapon(string weapon);
        ICharaBuilder SetHealth(int health);
        ICharaBuilder SetMana(int mana);
        PlayerCharacter Build();
    }

 


3. 생성하는 방법(How)을 정의한다!

    // 빌더 구현 방법
    public class CharacterBuilder : ICharaBuilder
    {
        private PlayerCharacter player = new PlayerCharacter();

        public ICharaBuilder SetName(string name)
        {
            player.Name = name;
            return this;
        }

        public ICharaBuilder SetJob(string job)
        {
            player.Job = job;
            return this;
        }

        public ICharaBuilder SetWeapon(string weapon)
        {
            player.Weapon = weapon;
            return this;
        }

        public ICharaBuilder SetHealth(int health)
        {
            player.Health = health;
            return this;
        }

        public ICharaBuilder SetMana(int mana)
        {
            player.Mana = mana;
            return this;
        }

        public PlayerCharacter Build()
        {
            return player;
        }
    }

 

생성하는 방법 설계가 끝났다면 이제 구현할 준비가 다 끝났다는 소리다!

마지막으로 Main에서 구현해서 실행시켜보자!


3. Main에서 구현(What)한다!

    internal class Program
    {
        static void Main(string[] args)
        {
            PlayerCharacter player = new CharacterBuilder()
                .SetName("전사")
                .SetJob("Warrior")
                .SetWeapon("대검")
                .SetHealth(150)
                .SetMana(50)
                .Build();

            player.PlayerShow();
        }
    }

 

테스트 결과1


- 어? 속도 (Speed) 속성을 추가해야해요!

추후 게임이 개발되거나 운영할 때 새로운 속성이 필요하는 경우가 발생할 수 있다!

새롭게 추가될 때 기존에 작성했던 코드들을 다 바꿔줘야 하는가? 이러면 너무 힘들다....ㅠㅠ

 

이 부분이 Builder 패턴이 유지보수가 용이하다는 점이다!

Speed 관련된 속성과 메서드만 추가하고 기존에 미리 만들었던 메서드들은 일절 건들지 않기 때문이다! (OCP 법칙)

ICharaBuilder SetSpeed(int speed);

먼저 빌더 인터페이스 부분에 Speed를 추가하고

        public ICharaBuilder SetSpeed(int speed)
        { 
            player.Speed = speed;
            return this;
        }

그 다음 빌더 코드에서 속도 코드를 새로 추가한다!

Main에서 속도 추가하기

마지막으로 Main에서 캐릭터를 생성할 때 속도만 추가하면

 

테스트 결과2

 

이렇게 기존 코드는 건들지 않으면서 속도 속성만 추가된 것을 확인 할 수 있다!

 

 

- 최종 정리

  코드 설명
객체를 생성하는 방법 CharacterBuilder 클래스 객체를 어떻게 생성할지(How)를 정의한다!
객체를 구현하는 방법 new CharacterBuilder().SetName()... 부분 빌더를 사용해서 실제 객체를 구현한다!

 

 

객체지향 프로그래밍(OOP)에 있어 항상 등장하는 개념들... 클래스인터페이스

이 둘의 차이점은 한번에 이해해보자!

 

Q1. 클래스(Class)인터페이스(Interface)의 차이

클래스와 인터페이스의 차이

 

클래스객체를 만들려는 설계도, 틀이라고 하면

인터페이스 클래스가 지켜야할 규칙을 정의한다.

 

예시로 자동차 클래스자동차 속성(색깔, 속도, 연료 등)이 있고,

자동차 인터페이스자동차가 반드시 있어야 하는 기능(시동 on / off, 이동기능들)들을 정의하는 것!

 


- 코드로 보는 클래스의 특징 (c#)

예시 코드는 모든 게임 유닛의 Base가 되는 BaseUnit 클래스가 있고

BaseUnit을 상속받아서 각각 플레이어 캐릭터적 캐릭터를 만드는 간단한 예시이다.

 

상황은 간단하다. 플레이어랑 적 캐릭터가 서로를 향해 공격을 한다!

    // 기본적인 유닛 클래스
    class BaseUnit
    {
        public string Name { get; set; }
        public int maxHp { get; set; }
        public int curHp;

        public BaseUnit(string name, int health)
        {
            Name = name;
            maxHp = health;
            curHp = maxHp;
        }

        public virtual void TakeAction(BaseUnit target)
        {
            Console.WriteLine($"{Name}의 행동");
        }

        public void TakeDamage(int damage)
        {
            curHp -= damage;
            Console.WriteLine($"{Name}가 {damage}의 피해를 입었다. {Name}의 체력: {curHp}\n");
        }
    }


    // 플레이어 클래스 (BaseUnit 상속)
    class Player : BaseUnit
    {
        public Player(string name, int health) : base(name, health) { }

        public override void TakeAction(BaseUnit target) 
        {
            Attack(target);
        }

        public void Attack(BaseUnit target)
        {
            Console.WriteLine($"{Name}가 {target.Name}을 공격했다!");
            target.TakeDamage(10);
        }
    }

    // 적 캐릭터 클래스 (BaseUnit 상속)
    class Enemy : BaseUnit
    {
        public Enemy(string name, int health) : base(name, health) { }

        public override void TakeAction(BaseUnit target)
        {
            Console.WriteLine($"{Name}의 공격! {target.Name}는 피해를 입었다.");
            target.TakeDamage(5);
        }
    }

    internal class Program
    {
        static void Main(string[] args)
        {
            Player player = new Player("주인공", 100);
            Enemy enemy = new Enemy("고블린", 50);

            player.TakeAction(enemy);
            enemy.TakeAction(player);
        }
    }

 

클래스속성과 메서드를 만들 수 있다.

사용할 때는 객체를 생성해서 사용하며, 상속을 통해서 클래스를 확장시킬 수 있다.


- 코드로 보는 인터페이스의 특징

이번에는 피해를 받는 인터페이스를 정의하고, 피해 인터페이스BaseUnit이 상속받았다.

 

상황은 이번에도 똑같이 서로를 향해 공격을 가하는데 적 캐릭터가 가하는 공격은 방패가 피해를 받는다!

namespace Class_Interface
{

    // 공격을 받는 인터페이스 정의
    interface IAttackable
    {
        void TakeDamage(int damage);
    }

    // 기본적인 유닛 클래스
    class BaseUnit : IAttackable
    {
        public string Name { get; set; }
        public int maxHp { get; set; }
        public int curHp;

        public BaseUnit(string name, int health)
        {
            Name = name;
            maxHp = health;
            curHp = maxHp;
        }

        public virtual void TakeAction(IAttackable target)
        {
            Console.WriteLine($"{Name}의 행동");
        }

        public void TakeDamage(int damage)
        {
            curHp -= damage;
            Console.WriteLine($"{Name}가 {damage}의 피해를 입었다. {Name}의 체력: {curHp}\n");
        }
    }


    // 플레이어 클래스 (BaseUnit 상속)
    class Player : BaseUnit
    {
        public Player(string name, int health) : base(name, health) { }

        public override void TakeAction(IAttackable target) 
        {
            Attack(target);
        }

        public void Attack(IAttackable target)
        {
            Console.WriteLine($"{Name}의 공격!");
            target.TakeDamage(10);
        }
    }

    // 적 캐릭터 클래스 (BaseUnit 상속)
    class Enemy : BaseUnit
    {
        public Enemy(string name, int health) : base(name, health) { }

        public override void TakeAction(IAttackable target)
        {
            Console.WriteLine($"{Name}의 공격!");
            target.TakeDamage(5);
        }
    }

    // 방어구 클래스 (IAttackable 인터페이스 구현)
    class Shield : IAttackable
    {
        public int Durability { get; set; }

        public Shield(int durability)
        {
            Durability = durability;
        }

        public void TakeDamage(int damage)
        {
            Durability -= damage;
            Console.WriteLine($"방패가 {damage}의 피해를 흡수했습니다! 남은 내구도: {Durability}");
        }
    }

    internal class Program
    {
        static void Main(string[] args)
        {
            Player player = new Player("주인공", 100);
            Enemy enemy = new Enemy("고블린", 50);
            Shield shield = new Shield(30);

            player.TakeAction(enemy);
            enemy.TakeAction(shield);
        }
    }
}

인터페이스의 실행 결과

 

클래스와 다른점은 인터페이스는 공격 선언만 하고, 내용은 없다.

하지만 클래스인터페이스를 구현 (implement) 한다면공격 기능은 반드시 구현시켜야 한다!


 

상속(Extends)구현(Implements)의 차이점만 이해한다면 좀 더 알기 쉬워진다

  클래스 상속 인터페이스 구현
관계 부모-자식 관계 (is-a 관계) 능력을 받는다 (Can-do 관계)
개수 단일 상속만 가능 (1개) 다중 구현이 가능 (여러개)
내용 부모의 속성/메서드를 물려받음 메서드 선언만 약속하고
구현은 하위에서 담당!
목적 코드 재사용성 (확장성) 공통된 행위 약속

 

 

아주 간단하게 정리하자면

 

  • 클래스는 "나는 누구?이다." (플레이어, 적 개체를 생성한다)
  • 인터페이스는 "나는 이 무언가?를 할 수 있다." ('공격을 받을 수 있다' 정의를 설정한다)

 

 

- 추상 팩토리(Abstract Factory)란?

 

추상 팩토리 게임 예시

 

RPG 게임을 예시로 들자면 각 스테이지 별로 등장하는 몬스터가 다르다고 하자

몬스터를 생성하는 곳한군데에 몰아넣고

이곳에 있는 팩토리 중 1개를 골라서 생성하는 것!

 

스테이지에 따라 몬스터를 관리하는 공장이 있고(팩토리1, 팩토리2...)

이러한 몬스터를 생성하는 공장들동시에 관리하는 게 추상 팩토리라고 생각하면 된다!

 


- 추상 팩토리 사용 예시(C#)

1. 먼저 몬스터 인터페이스를 정의한다

    // 몬스터 인터페이스
    interface IMonster
    {
        void Attack();	// 공격 정의
    }

2. 인터페이스를 상속받은 몬스터들을 만든다

    // 초원 스테이지에 등장하는 몬스터들
    class Slime : IMonster
    {
        public void Attack() => Console.WriteLine("슬라임의 점프 공격!");
    }

    // 동굴 스테이지에 등장하는 몬스터들
    class Goblin : IMonster
    {
        public void Attack() => Console.WriteLine("고블린이 단검을 던졌다!");
    }

    class Troll : IMonster
    {
        public void Attack() => Console.WriteLine("트롤이 격분했다!");
    }


3. 추상 팩토리 인터페이스를 정의한다

// 추상 팩토리
interface IStageFactory
{
    IMonster CreateMonster();
    IItem CreateItem();
}

4. 추상 팩토리를 상속받아 각 스테이지별 팩토리를 만든다

    // 초원 스테이지 팩토리
    class StageGrass_Factory : IStageFactory
    {
        public IMonster CreateMonster() => new Slime();
    }

    // 동굴 스테이지 팩토리
    class StageCave_Factory : IStageFactory
    {
        private Random random = new Random();
        public IMonster CreateMonster()
        {
            if (random.Next(2) == 0)
                return new Goblin();
            else
                return new Troll();
        }
    }

5. 만든 예시코드로 게임을 실행해본다

    // 게임 매니저
    class GameManager
    {
        private IMonster monster;

        public GameManager(IStageFactory factory)
        {
            monster = factory.CreateMonster();
        }

        public void Play()
        {
            monster.Attack();
        }
    }


    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("초원 스테이지 시작!");
            GameManager grassGame = new GameManager(new StageGrass_Factory());
            grassGame.Play();

            Console.WriteLine("\n동굴 스테이지 시작!");
            GameManager caveGame = new GameManager(new StageCave_Factory());
            caveGame.Play();
        }
    }

실행결과

 

 

 

추상 팩토리 패턴연관된 객체(몬스터 + 아이템 등)를 세트로 생성할 때 사용되는

디자인 패턴이다. 게임에서 스테이지, 종족, 장비 등의 세트를 한 번에 생성해야 할 때 사용된다.

(예를 들어 게임 클리어 시 드랍되는 장비)


하지만 '새로운 객체 유형을 추가해야 될 때'유지보수가 어려울 수 있으며,

특히 객체를 대량으로 생성해야 할 때 는 다른 방식을 설계해야 한다.

+ Recent posts