Post

2026-03-10 TIL (11일차)

2026-03-10 TIL (11일차)

SOLID 원칙

코드를 구현할 때 재사용성 있게 구현한다.
1) 유지보수성 및 확장성 향상
2) 변경에 유연한 설계 제공

1.단일 책임 원칙(SRP)

각 클래스는 하나의 책임만 가져야 한다는 원칙입니다.
ex) 점수 계산하는 클래스가 있으면 점수 계산 기능만 있어야지 점수를 세팅하고 체점하는 그런 다른 기능은 원칙에 어긋난다.

2.개방 폐쇄 원칙(OCP)

기존 코드를 최소한으로 변경하면서 새로운 기능을 추가할 수 있도록 설계해야 합니다.
ex) A클래스를 수정하고 변경해도 B라는 클래스에는 영향이 없어야 한다.

3.리스 코프 치환 원칙(LSP)

다형성을 보장해야한다. (상속받은 기능을 유지해야하다.)
프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.
ex) 부모클래스가 그리는 함수를 가상함수로 그리면 자식 클래스의 함수들은 그리는 기능만 구현해야하고 부모를 기대하고 짠 코드가 자식 때문에 에러가 나면 안 된다.

4.인터페이스 분리 원칙(ISP)

클래스가 사용하지도 않을 함수를 억지로 구현하지 않도록, 인터페이스를 구체적인 여러 개로 잘게 쪼개야 한다.
ex) 인터페이스가 ‘공격’과 ‘마법’ 기능을 모두 가지면, 마법을 못 쓰는 전사 클래스가 억지로 빈 마법 함수를 구현하지 않도록 인터페이스를 ‘공격’과 ‘마법’으로 각각 쪼개야 한다.

5.의존 역전 원칙(DIP)

구체적인 클래스가 아니라 추상적인 인터페이스(부모 클래스)에 의존하여, 코드 간의 결합을 느슨하게 만들어야 한다.
ex) 캐릭터 클래스가 ‘검’이라는 구체적인 클래스를 직접 가지게 하지 말고, ‘무기’라는 인터페이스를 들게 하여 나중에 ‘활’이나 ‘지팡이’로 갈아 끼워도 캐릭터 코드를 한 줄도 고칠 필요가 없게 만들어야 한다.

디자인 패턴

개발 시 반복적으로 등장하는 문제를 해결하기 위한 일반화된 솔루션
1.생성패턴
객체에 생성하는데 제한을 두거나 특정 방법으로 생성하고 싶을 때 해결책을 모아놓은게 생성패턴이다.

2.구조패턴
객체가 여러가지 있을 때 객체를 어떻게 연동을 시킬지 객체들의 구조를 어떻게 잡아갈지에 대한 초점을 맞춘거다.

3.행동패턴
객체의 상태가 변할 때 어떤식으로 다른 객체의 상태를 전달하거나 동작을 해야할 때 해결하는 방법이다.

1.싱글톤 패턴

반드시 객체가 하나만 존재해야하는 경우 사용하는 패턴이다.
ex)비행기 게임을 혼자하는 데 여러개의 비행기를 제어해야하는 경우는 없음

생성자,복사생성자,대입연산자를 다 private로 막아놓거나 삭제를 시켜 못사용하게 한다.
외부 생성을 원천 차단하고 내부에 단 하나의 static 객체를 만든 뒤, 오로지 공개된 static 함수(Getter)를 통해서만 그 객체에 접근하도록 통제한다.
싱글톤 패턴으로 만든 객체는 새로 할당받고 사용해도 전에 사용했던 값들이 남아있다.

2.데코레이턴 패턴

객체의 기본 구조는 유지하면서, 새로운 기능을 ‘포장지’처럼 덧씌워 상황에 따라 동적으로 기능을 확장하는 패턴이다.
ex)기본 ‘검’ 아이템이 있습니다. 여기에 ‘불 공격’, ‘얼음 공격’, ‘독 공격’ 기능을 추가할 때 ‘검’ 객체는 그대로 두고, 그 겉을 ‘불 장식(Decorator)’, ‘독 장식’으로 감싸버립니다.

3.옵저버 패턴

한 객체의 상태가 변하면, 그 객체에 의존하는 다른 객체들에게 자동으로 알림이 가서 상태를 갱신하게 만드는 ‘유튜브 구독-알림’ 같은 패턴이다.
ex) 플레이어의 ‘HP’가 깎였을 때 플레이어 코드가 직접 UI나 사운드 매니저를 조작하는 것이 아니라, 플레이어는 그저 “나 데미지 입었어!”라고 방송(Notify)만 하고, 이를 미리 구독(Subscribe)하고 있던 ‘HP 바 UI’와 ‘피격 효과음 매니저’가 알림을 듣고 알아서 체력바를 줄이고 소리를 냅니다.

2번 도전과제 기능

1.전투시스템

도전과제 체크리스트로만 하면 기본값 설정때문에 게임이 바로 종료된다. 기능이 다 잘되는지 확인하기 위해 플레이어와 몬스터의 값을 직접 넣고 전투시스템이 잘 되는지 확인되게 하였다.

Player의 스텟값을 입력하는 방법

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
void Player::setStatus()
{
    while (true)
    {
        std::cout << "Player HP를 입력해주세요 : ";
        cin >> this->HP;

        if (this->HP > 0) 
        {
            break; 
        }
        std::cout << "HP는 1 이상이어야 합니다. 다시 입력해주세요." << std::endl;
    }

    while (true)
    {
        std::cout << "Player 공격력을 입력해주세요 : ";
        cin >> this->atk;

        if (this->atk > 0) 
        {
            break;
        }
        std::cout << "공격력은 1 이상이어야 합니다. 다시 입력해주세요.\n" << std::endl;
    }

    while (true)
    {
        std::cout << "Player 방어력을 입력해주세요 : ";
        cin >> this->def;

        if (this->def >= 0) 
        {
            break;
        }
        std::cout << "방어력은 0 이상이어야 합니다. 다시 입력해주세요.\n" << std::endl;
    }
}

2. 오류발생 (능력치 입력)

하지만 문제발생 문자를 넣으면 무한루프가 발생한다.
문제를 해결하기 위해 검색하던 중 해결하는 방법인 사이트를 찾았다.
참고 사이트

[ 오류 상태 제거하기 ]
사용자가 잘못된 값을 입력했을 때 다시 값을 입력받지 못하고 계속 fail 함수가 true가 되는 이유는 cin 객체의 상태가 내부적으로 fail로 설정되어 있기 때문입니다.
따라서 이 상태를 제거해야지 다시 정상적으로 입력받을 수 있기 때문에 아래와 같이 clear 함수를 호출하여 cin의 내부 상태를 초기화합니다.

[ 입력된 데이터 제거하기 ]
clear 함수를 호출하여 cin 객체의 오류 상태를 초기화하더라도 입력 스트림 객체가 입력을 받지 못하고 다시 fail 상태가 되어 버립니다.
이것은 사용자가 잘못 입력했던 문자열이 입력 스트림 버퍼에 그대로 남아 있어서 그렇습니다.
따라서 사용자가 잘못 입력한 값을 ignore 함수를 사용하여 입력 스트림 버퍼에서 제거해야 합니다.

1
2
3
4
5
6
7
8
9
10
//문제를 해결해주는 코드
if (cin.fail())
{
    std::cout << "[경고] 숫자가 아닌 잘못된 값이 입력되었습니다!" << std::endl;

    cin.clear();
    cin.ignore(10000, '\n');

    continue;
}

3번과제 필수 기능

1.의문점 발생

템플릿 함수에 기능으로 제거를 넣었을 때 자료형에 포인터도 올수도있고 그냥 자료형이 올 수도 있는데 이때 null을 써야할까? nullptr을 써야할까?라는 의문점이 생겼다.
이 경우 클래스 템플릿 특수화를 사용하여 각각 자료형마다 생기는 변수를 따로 정의해야한다.
하지만 3번 과제에서는 특수화를 구현하면 번거로움이 더 크기때문에 자료가 삭제되면 배열을 통해 삭제한 부분을 접근만 안하면 상관없으므로 size만 줄어들게 해서 접근을 못하게 하였다.

2. 문제발생 (출력부분)

클래스 템플릿에서 만약 템플릿 매개변수가 클래스로 받게 되면 출력하는 방식이 문제가 생김

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//구현부분
template<typename T>
inline void Inventory<T>::PrintAllItems() const
{
	if (size_ != 0)
	{
		for (int i = 0; i < size_; i++)
		{
			std::cout << pItems_[i] << std::endl;
		}
	}
	else
	{
		std::cout << "비어있음" << std::endl;
	}
}

int,double같은 자료형은 상관없지만 class를 자료형으로 받게되면 과제에서 원하는 출력이 안된다.

과제 출력 에러 과제 출력 에러2

오류를 통해 연산자 오버로딩이 필요하다는 것을 확인하여 사용
Item 클래스에 연산자 오버로딩 “«” 구현

1
2
3
4
5
6
7
8
9
10
void Item::PrintInfo() const
{
	std::cout << "[이름: " << this->name_ << ", 가격: " << this->price_ << "G]" << std::endl;
}

std::ostream& operator<<(std::ostream& os, const Item& item)
{
	item.PrintInfo();
	return os;
}
This post is licensed under CC BY 4.0 by the author.