기획서를 읽고 직접 설계하는 C++ 연습 · PART1
기획의 기능과 규칙을 이해하고, 이를 만족시키는 객체와 클래스 구조를 직접 설계하는 훈련입니다. 제공 main()을 사용 예시로 읽으며 구현한 동작을 확인하고, 같은 기획을 반복해서 다시 설계합니다.
main() 동작 실험실 · PART1
문제를 고르고 기획의 사용 흐름이 어떻게 진행되는지 관찰하세요. 한 단계 실행·자동 실행·초기화·추가 요청으로 기능의 결과를 확인할 수 있습니다. 실제 클래스는 아래 기획과 제공 main()을 바탕으로 직접 설계합니다.
기획서를 읽고 설계하는 연습
각 문제는 작은 서비스의 기획입니다. 먼저 사용자가 무엇을 하려는지, 어떤 정보가 필요한지, 요청 후 어떤 결과를 받아야 하는지를 읽으세요. 그 요구를 만족시키는 클래스 구성과 책임 분담은 직접 정합니다.
1.
기획 이해: 기본 기능과 규칙, 확장 요청, 사용 흐름을 읽고 서비스를 자신의 말로 설명합니다.
2.
사용 예시 확인: 제공 main()의 생성과 요청을 살펴보며 기획이 코드에서 어떻게 사용되는지 확인합니다. 첫 구현에서는 제공 main()의 이름·호출 형식·기대값을 유지합니다.
3.
직접 설계: 등장 대상, 필요한 정보, 가능한 행동과 대상 사이의 관계를 스스로 정리합니다. 내부 멤버·보조 클래스·저장 방법은 직접 선택합니다.
4.
구현과 동작 확인: 설계한 내용을 구현하고 사용 흐름의 결과와 비교합니다. 기획과 다른 동작이 나오면 원인을 찾아 고칩니다.
5.
반복과 변경: 다음 회차에는 이전 구현을 닫고 같은 기획과 main()에서 다시 설계합니다. 이후 다음 기획 변경을 적용하고 바뀐 설계 판단을 기록합니다.
문서에서 정해 주는 범위
본문은 기능, 처리 조건, 사용자에게 보이는 결과를 정합니다. 어떤 클래스에 정보를 넣을지, 어떤 객체가 요청을 전달할지, 공통 기능을 어떻게 나눌지는 학습자의 설계입니다. 각 문제 마지막의 회고는 구현을 마친 뒤 참고하며 디자인 패턴 이름을 외우거나 특정 패턴을 적용할 필요는 없습니다.
사용 흐름 예시는 기획의 의미를 확인하기 위한 사례입니다. 이어서 일어나는 행동은 앞 행동의 결과에서 진행하며, 별도 사례는 설명한 시작 조건으로 다시 진행합니다. 예시 외에도 같은 규칙을 지키는 결과가 나와야 합니다.
위 실험실에서 실제 동작을 관찰할 수 있습니다. 제공 main()은 구현해야 할 사용 방식의 예시로 남겨 두었습니다. 기획을 읽고 자신의 설계를 만든 뒤 두 자료를 결과 확인에 활용하세요.
공통 운영 규칙
•
따로 정하지 않은 거절된 요청은 기존 정보를 유지하며 처리할 수 없음을 알립니다. 조회만으로 위치·체력·잔액·주문 내용이 바뀌지 않습니다.
•
PART2의 등록 목록과 기록은 별도 지정이 없으면 최대 8개입니다. 같은 대상의 중복 등록, 없는 대상의 제거, 정원 초과는 거절합니다. 옵션 중복처럼 문제에서 허용한 경우에는 해당 규칙을 따릅니다.
•
빈 목록은 처리할 대상이 없는 결과로 마칩니다. 요청에 필요한 대상이 없으면 받아들이지 않습니다. 하나의 목록에서 대상을 제외하는 일이 그 대상의 정보 자체를 없애는 일인지는 각 기획을 따릅니다.
•
금액·시간·개수의 단위는 문제에서 정합니다. 안내 문구의 모양은 자유지만 대상·값·순서·처리 결과를 분명하게 구별할 수 있어야 합니다.
•
실제 결제·인증·배송·파일 읽기는 하지 않습니다. 모든 기능은 연습용 수치와 콘솔 안내로 표현합니다.
개발 환경과 제공 코드
C++17 콘솔 프로그램으로 문제마다 따로 작성합니다. 제공 main() 전체와 기대값을 유지하며 필요한 클래스와 헤더를 추가하세요. 결과 검사를 건너뛰지 않도록 assert가 활성화된 상태로 실행합니다. 실수 결과는 오차 0.001 이내로 비교합니다.
1단계 · 같은 호출, 다른 동작 (1~10)
문제 1. 동물 소리 출력
기획 의도
동물 소개 프로그램입니다. 방문자가 목록을 재생하면 등록된 동물의 이름과 울음소리가 차례로 나타납니다. 같은 종류의 동물이 여러 마리 있어도 각각의 이름으로 소개해야 합니다.
기본 기능과 동작 규칙
•
처음에는 강아지 Baekgu와 고양이 Nabi를 소개합니다. 강아지의 울음소리는 barks!, 고양이는 meows!입니다. 한 번의 소개에 이름과 울음소리를 함께 보여 줍니다.
•
소개 순서는 운영자가 목록에 넣은 순서를 따릅니다. 소개를 다시 재생해도 이름이나 순서는 저절로 바뀌지 않습니다.
확장 요청
•
강아지 Bori를 추가한 뒤에도 목록 재생이라는 같은 기능으로 모든 동물을 소개할 수 있어야 합니다. 운영자는 동물마다 다른 소개 화면을 따로 실행하지 않습니다.
사용 흐름 예시
1.
Baekgu, Nabi, Bori 순으로 등록하면 Baekgu barks!, Nabi meows!, Bori barks! 순으로 소개됩니다.
2.
운영자가 Bori만 소개 대상으로 선택하면 Bori의 이름으로 소개됩니다. 같은 동물을 목록에서 두 번 선택했다면 그 동물의 소개가 두 번 재생됩니다.
다음 기획 변경
다음 기획에서는 오리와 양을 소개하려고 합니다. 새 동물의 이름과 울음소리를 정하고 같은 소개 기능을 제공하세요.
제공 main() · 기획을 사용하는 예시
#include <iostream>
#include <string>
// 이 위치에 필요한 클래스를 직접 구현하세요.
int main() {
Dog d1("Baekgu"), d2("Bori");
Cat c1("Nabi");
Animal* animals[] = { &d1, &c1, &d2 };
for (Animal* animal : animals) {
animal->Speak();
}
// Baekgu barks! / Nabi meows! / Bori barks!
}
C++
복사
풀고 나서 펼치기 · 설계 회고와 관련 개념
문제 2. 도형 면적 계산
기획 의도
여러 도형의 크기를 비교하는 면적 안내 화면입니다. 사용자는 원이나 직사각형의 치수를 입력하고, 각 도형의 면적과 목록 전체의 면적 합계를 확인합니다.
기본 기능과 동작 규칙
•
원에는 반지름, 직사각형에는 가로와 세로를 입력합니다. 길이는 0 이상이며, 원주율은 3.14159로 정합니다. 원의 면적은 원주율×반지름×반지름, 직사각형은 가로×세로입니다.
•
면적을 조회하는 기능과 목록을 보여 주는 기능을 제공합니다. 크기가 0인 도형의 면적은 0이며, 조회를 여러 번 해도 입력한 치수는 유지됩니다.
확장 요청
•
원과 직사각형을 한 목록에서 확인할 수 있어야 합니다. 전체 면적은 현재 목록에 있는 각 도형의 면적을 더한 값입니다.
사용 흐름 예시
1.
반지름 3인 원은 28.27431, 가로 4·세로 2인 직사각형은 8로 안내합니다. 크기가 0인 원도 함께 있으면 전체 면적은 36.27431입니다.
2.
같은 목록을 다시 열면 같은 면적과 합계를 보여 줍니다. 이전 조회의 합계가 다시 더해지지 않습니다.
다음 기획 변경
밑변과 높이로 면적을 구하는 삼각형을 목록에 추가하려고 합니다. 기존 면적 안내와 전체 합계 기능에서도 사용할 수 있게 하세요.
제공 main() · 기획을 사용하는 예시
#include <iostream>
#include <string>
#include <cassert>
#include <cmath>
// 이 위치에 필요한 클래스를 직접 구현하세요.
int main() {
Circle c(3.0), zero(0.0);
Rectangle r(4.0, 2.0);
const Shape* shapes[] = { &c, &r, &zero };
double total = 0.0;
for (const Shape* shape : shapes) {
std::cout << shape->Area() << '\n';
total += shape->Area();
}
assert(std::abs(total - 36.27431) < 0.001);
assert(zero.Area() == 0.0);
}
C++
복사
풀고 나서 펼치기 · 설계 회고와 관련 개념
문제 3. 직원 급여 계산
기획 의도
회사 급여 담당자가 직원별 지급액과 이번 지급 총액을 확인하는 프로그램입니다. 직원의 이름과 기본급은 같더라도 직무에 따라 추가 지급률이 다릅니다.
기본 기능과 동작 규칙
•
직원마다 이름과 기본급이 있습니다. 관리자는 기본급의 50%, 개발자는 기본급의 10%를 추가로 받습니다. 지급액은 기본급과 추가 지급액의 합입니다.
•
기본급은 0 이상이며 계산이 깔끔하도록 10의 배수로 주어집니다. 지급액을 조회하는 일은 기본급을 인상하거나 실제 송금하는 일이 아닙니다.
확장 요청
•
직원 목록에서 이름과 지급액을 함께 안내하고, 회사의 총 지급액을 보여 줍니다. 새 직무가 생겨도 급여 담당자는 같은 목록 화면에서 확인할 수 있어야 합니다.
사용 흐름 예시
1.
기본급 5000인 관리자는 7500, 기본급 3000인 개발자는 3300을 받으므로 두 사람의 총 지급액은 10800입니다.
2.
같은 급여표를 다시 조회해도 지급액은 동일합니다. 기본급이 0이면 추가 지급액도 0입니다.
다음 기획 변경
보너스를 받지 않는 인턴을 추가하려고 합니다. 인턴도 직원별 지급액과 회사 총액에 포함하세요.
제공 main() · 기획을 사용하는 예시
#include <iostream>
#include <string>
#include <cassert>
// 이 위치에 필요한 클래스를 직접 구현하세요.
int main() {
Manager m("Kim", 5000);
Developer d("Lee", 3000);
const Employee* staff[] = { &m, &d };
int total = 0;
for (const Employee* employee : staff) {
const int salary = employee->CalculateSalary();
std::cout << employee->GetName() << ": " << salary << '\n';
assert(employee->CalculateSalary() == salary);
total += salary;
}
assert(total == 10800);
}
C++
복사
풀고 나서 펼치기 · 설계 회고와 관련 개념
문제 4. 악기 연주
기획 의도
공연자가 연주할 악기를 순서대로 골라 짧은 합주를 재생합니다. 선택한 악기의 종류와 순서가 들리는 소리의 순서를 결정합니다.
기본 기능과 동작 규칙
•
피아노를 연주하면 ding ding!, 기타를 연주하면 strum strum!을 보여 줍니다. 실제 소리를 재생하는 대신 콘솔 문구로 연주를 표현합니다.
•
한 번 재생하면 선택 목록의 악기들을 앞에서부터 한 번씩 연주합니다. 같은 악기를 여러 번 선택할 수 있습니다.
확장 요청
•
악기의 순서를 바꾼 뒤에도 같은 재생 기능을 사용합니다. 목록을 뒤집으면 연주 순서도 그대로 뒤집힙니다.
사용 흐름 예시
1.
피아노 다음 기타를 선택하면 ding ding!, strum strum! 순서입니다. 기타 다음 피아노로 바꾸면 소리도 그 순서로 바뀝니다.
2.
피아노를 연속 두 번 선택하면 피아노 소리가 두 번 나옵니다. 선택하지 않은 악기를 임의로 연주하지 않습니다.
다음 기획 변경
드럼을 추가하려고 합니다. 선택한 악기 수가 늘거나 줄어도 모든 선택을 순서대로 재생하세요.
제공 main() · 기획을 사용하는 예시
#include <iostream>
#include <string>
// 이 위치에 필요한 클래스를 직접 구현하세요.
int main() {
Piano piano;
Guitar guitar;
Instrument* first[] = { &piano, &guitar };
Instrument* second[] = { &guitar, &piano, &piano };
for (Instrument* instrument : first) {
instrument->Play();
}
for (Instrument* instrument : second) {
instrument->Play();
}
// 첫 목록: ding ding! / strum strum!
// 두 번째: strum strum! / ding ding! / ding ding!
}
C++
복사
풀고 나서 펼치기 · 설계 회고와 관련 개념
문제 5. 게임 캐릭터 공격
기획 의도
게임의 캐릭터 소개 화면에서 각 인물의 대표 공격 연출을 보여 줍니다. 이번 기획은 공격 문구를 보여 주는 데까지이며 적의 체력이나 실제 피해 판정은 다루지 않습니다.
기본 기능과 동작 규칙
•
기사 Arthur는 Arthur slashes with sword!, 마법사 Merlin은 Merlin casts a spell!을 표시합니다. 이름과 직업별 공격 연출을 함께 보여 줍니다.
•
같은 직업의 캐릭터도 서로 다른 이름을 가질 수 있습니다. 공격 연출을 재생해도 이름이 바뀌거나 다른 캐릭터의 정보가 섞이지 않습니다.
확장 요청
•
기사 Lancelot를 추가하고 세 인물의 연출을 한 목록에서 재생합니다. 같은 직업이라고 같은 이름으로 소개하면 안 됩니다.
사용 흐름 예시
1.
Arthur, Merlin, Lancelot 순으로 선택하면 각자의 이름으로 검 공격, 마법, 검 공격 연출이 나옵니다.
2.
Lancelot만 선택한 경우에도 Arthur가 아니라 Lancelot의 이름을 보여 줍니다. 반복 재생해도 캐릭터 정보는 같습니다.
다음 기획 변경
활을 쏘는 직업을 추가하려고 합니다. 이름과 대표 공격 연출을 정하고 기존 재생 기능에서 함께 사용하세요.
제공 main() · 기획을 사용하는 예시
#include <iostream>
#include <string>
// 이 위치에 필요한 클래스를 직접 구현하세요.
int main() {
Knight arthur("Arthur"), lancelot("Lancelot");
Mage merlin("Merlin");
Character* party[] = { &arthur, &merlin, &lancelot };
for (Character* character : party) {
character->Attack();
}
// 기사 둘은 각자의 이름과 slashes with sword! 출력
// 마법사는 Merlin casts a spell! 출력
}
C++
복사
풀고 나서 펼치기 · 설계 회고와 관련 개념
문제 6. 스마트기기 명령
기획 의도
스마트홈 화면에서 조명과 스피커의 전원을 관리합니다. 사용자는 기기 하나를 선택하거나 등록된 기기 전체에 같은 전원 명령을 보낼 수 있습니다.
기본 기능과 동작 규칙
•
조명과 스피커는 처음에 모두 꺼져 있습니다. on은 켜기, off는 끄기이며 다른 명령은 받아들이지 않습니다.
•
기기 종류와 현재 전원을 안내합니다. 이미 켜진 기기에 켜기를 요청하거나 꺼진 기기에 끄기를 요청해도 요청은 정상 처리되며 상태는 유지됩니다.
확장 요청
•
전체 켜기와 전체 끄기를 제공합니다. 잘못된 명령이 들어오면 기기 전원을 바꾸지 않고 처리할 수 없음을 알려 줍니다.
사용 흐름 예시
1.
전체 켜기 후에는 조명과 스피커가 모두 켜집니다. 이어 전체 끄기를 하면 둘 다 꺼집니다.
2.
켜진 조명에 on을 다시 요청해도 켜져 있습니다. blink처럼 지원하지 않는 명령은 거절하고 직전 전원을 유지합니다.
다음 기획 변경
선풍기를 세 번째 기기로 등록하려고 합니다. 기존 전체 전원 제어에 포함하세요.
제공 main() · 기획을 사용하는 예시
#include <iostream>
#include <string>
#include <cassert>
// 이 위치에 필요한 클래스를 직접 구현하세요.
int main() {
SmartLight light;
SmartSpeaker speaker;
SmartDevice* devices[] = { &light, &speaker };
for (SmartDevice* device : devices) {
assert(!device->IsOn());
bool ok = device->Execute("on");
assert(ok && device->IsOn());
ok = device->Execute("on");
assert(ok && device->IsOn());
ok = device->Execute("blink");
assert(!ok && device->IsOn());
ok = device->Execute("off");
assert(ok && !device->IsOn());
ok = device->Execute("");
assert(!ok && !device->IsOn());
}
}
C++
복사
풀고 나서 펼치기 · 설계 회고와 관련 개념
문제 7. 유닛 이동
기획 의도
게임에서 여러 유닛에게 전진 명령을 내립니다. 같은 전진 명령을 받아도 유닛의 이동 특성에 따라 실제 이동 거리가 다릅니다.
기본 기능과 동작 규칙
•
전사와 궁수는 모두 x좌표 0에서 시작합니다. 전진 한 번에 전사는 1, 궁수는 2만큼 x좌표가 증가합니다.
•
화면에는 유닛의 이름과 현재 위치를 보여 줍니다. 두 번째 전진은 이미 이동한 위치에서 이어서 진행합니다.
확장 요청
•
선택한 유닛 전체에 전진 명령을 세 번 보냅니다. 위치 안내만 다시 요청했을 때는 추가로 움직이지 않습니다.
사용 흐름 예시
1.
첫 전진 뒤 전사는 1, 궁수는 2입니다. 세 번 전진한 뒤에는 각각 3과 6입니다.
2.
세 번 전진한 후 위치 화면을 다시 열어도 각각 3과 6입니다. 새 전진 명령을 보내야 다음 위치로 이동합니다.
다음 기획 변경
한 번에 3만큼 이동하는 유닛을 추가하려고 합니다. 같은 전진 명령으로 함께 이동시키세요.
제공 main() · 기획을 사용하는 예시
#include <iostream>
#include <string>
#include <cassert>
// 이 위치에 필요한 클래스를 직접 구현하세요.
int main() {
Warrior warrior("Thor");
Archer archer("Robin");
Unit* units[] = { &warrior, &archer };
for (int turn = 0; turn < 3; ++turn) {
for (Unit* unit : units) {
unit->Move();
std::cout << unit->GetName() << ": " << unit->GetX() << '\n';
}
}
assert(warrior.GetX() == 3);
assert(archer.GetX() == 6);
}
C++
복사
풀고 나서 펼치기 · 설계 회고와 관련 개념
문제 8. 알림 시스템
기획 의도
공지 내용을 이메일과 문자로 전달하는 모의 알림 서비스입니다. 공지 담당자는 보낼 내용을 정하고, 각 수신처에 어떤 채널로 전달되는지 확인합니다.
기본 기능과 동작 규칙
•
이메일 알림에는 수신 이메일 주소, 문자 알림에는 수신 전화번호가 필요합니다. 전송 결과에는 채널·수신자·내용을 함께 표시합니다.
•
실제 이메일이나 문자를 보내지는 않습니다. 수신자가 없거나 내용이 비어 있으면 전송을 거절하며 전송 완료 표시도 남기지 않습니다.
확장 요청
•
등록된 여러 수신처에 같은 공지를 전달합니다. 각 알림은 자신의 채널과 수신처를 사용하며 한 수신처의 정보가 다른 알림에 섞이지 않습니다.
사용 흐름 예시
1.
Hello라는 공지를 정상 이메일과 전화번호로 보내면 각 채널과 해당 수신처, Hello가 표시됩니다.
2.
내용이 없는 공지를 보내거나 수신처 없이 전송하려고 하면 전송되지 않습니다. 이전에 정상 전송한 다른 알림은 영향을 받지 않습니다.
다음 기획 변경
앱 안에서 공지를 보여 주는 채널을 추가하려고 합니다. 공지 담당자는 같은 전송 흐름으로 사용할 수 있어야 합니다.
제공 main() · 기획을 사용하는 예시
#include <iostream>
#include <string>
#include <cassert>
// 이 위치에 필요한 클래스를 직접 구현하세요.
int main() {
EmailNotification email("kim@example.com");
SMSNotification sms("010-1234-5678");
EmailNotification emptyRecipient("");
Notification* notices[] = { &email, &sms };
for (Notification* notice : notices) {
bool ok = notice->Send("Hello");
assert(ok);
ok = notice->Send("");
assert(!ok); // 성공 전송 출력도 없어야 함
}
const bool ok = emptyRecipient.Send("Hello");
assert(!ok);
}
C++
복사
풀고 나서 펼치기 · 설계 회고와 관련 개념
문제 9. 몬스터 전투 준비
기획 의도
전투 화면에서 몬스터의 공격 연출과 피해 후 생존 여부를 확인합니다. 살아 있는 몬스터만 공격할 수 있으며, 쓰러진 몬스터는 다시 공격하지 않습니다.
기본 기능과 동작 규칙
•
고블린은 체력 50, 오크는 체력 100으로 시작합니다. 살아 있는 고블린은 throws rock!, 오크는 swings axe!를 이름과 함께 보여 줍니다.
•
양수 피해를 받으면 그만큼 체력이 줄어듭니다. 남은 체력보다 큰 피해를 받아도 체력은 0에서 멈춥니다. 0 이하 피해는 거절합니다.
확장 요청
•
피해를 받은 뒤에도 현재 체력에 맞게 공격 가능 여부를 안내합니다. 공격 연출 자체는 자신의 체력을 소비하지 않습니다.
사용 흐름 예시
1.
체력 50인 고블린에게 80의 피해를 주면 체력은 0이며 이후 공격 요청은 거절됩니다.
2.
피해를 받지 않은 오크는 체력 100으로 공격할 수 있습니다. 공격 연출을 반복해도 체력 100을 유지하며 0 이하 피해도 체력을 바꾸지 않습니다.
다음 기획 변경
시작 체력과 공격 연출이 다른 몬스터를 추가하려고 합니다. 같은 전투 진행에서 함께 사용할 수 있게 하세요.
제공 main() · 기획을 사용하는 예시
#include <iostream>
#include <string>
#include <cassert>
// 이 위치에 필요한 클래스를 직접 구현하세요.
int main() {
Goblin goblin("Goblin", 50);
Orc orc("Orc", 100);
Monster* monsters[] = { &goblin, &orc };
for (Monster* monster : monsters) {
const bool ok = monster->Attack();
assert(ok);
}
bool ok = goblin.TakeDamage(20);
assert(ok && goblin.GetHP() == 30);
ok = goblin.TakeDamage(40);
assert(ok && goblin.GetHP() == 0);
ok = goblin.Attack();
assert(!ok);
ok = orc.TakeDamage(-1);
assert(!ok && orc.GetHP() == 100);
}
C++
복사
풀고 나서 펼치기 · 설계 회고와 관련 개념
문제 10. 결제 시스템
기획 의도
여러 결제 요청을 승인하고 승인된 금액의 합계를 안내하는 연습용 결제 창구입니다. 실제 카드사나 은행에 연결하지 않습니다.
기본 기능과 동작 규칙
•
카드와 현금의 결제 요청에는 각각 결제 금액이 있습니다. 양수 금액만 승인하며 승인 결과에 채널과 금액을 보여 줍니다.
•
0 또는 음수 금액은 거절합니다. 거절된 금액은 승인 합계에 포함하지 않습니다.
확장 요청
•
한 목록의 결제 요청을 차례로 처리하고 이번 처리의 승인 총액을 안내합니다. 같은 결제를 다시 요청하면 새로운 승인 시도로 취급하며, 과거 결제 이력 관리나 중복 결제 방지는 이번 범위에 없습니다.
사용 흐름 예시
1.
카드 10000과 현금 5000이 승인되면 이번 처리 총액은 15000입니다. 0원 결제 요청은 승인되지 않습니다.
2.
결제 목록을 새로 처리하면 그 처리에 해당하는 총액을 구합니다. 이전 처리의 합계를 이번 합계에 자동으로 덧붙이지 않습니다.
다음 기획 변경
계좌이체 결제를 추가하려고 합니다. 같은 승인 목록과 합계 안내에 포함하세요.
제공 main() · 기획을 사용하는 예시
#include <iostream>
#include <string>
#include <cassert>
// 이 위치에 필요한 클래스를 직접 구현하세요.
int main() {
CreditCard card(10000), zero(0);
Cash cash(5000), negative(-100);
Payment* payments[] = { &card, &cash, &zero, &negative };
int total = 0;
for (Payment* payment : payments) {
const bool ok = payment->Process();
if (ok) {
total += payment->GetAmount();
}
}
assert(total == 15000);
}
C++
복사
풀고 나서 펼치기 · 설계 회고와 관련 개념
2단계 · 상태 변화와 여러 행동 (11~20)
문제 11. 운동 기록
기획 의도
운동 기록 화면에서 여러 활동의 소모 칼로리를 확인합니다. 기록에 적힌 시간은 운동별로 다르며, 이번 실습에서는 정해진 분당 소모량으로 계산합니다.
기본 기능과 동작 규칙
•
달리기는 분당 10kcal, 자전거는 분당 6kcal입니다. 기록 시간은 0분 이상이며 소모량은 운동 시간×분당 소모량입니다.
•
각 활동의 종류·시간·소모 칼로리를 안내합니다. 조회는 기록을 읽는 기능이며 운동 시간을 늘리지 않습니다.
확장 요청
•
달리기와 자전거를 한 기록 목록에 표시하고 전체 소모 칼로리를 합산합니다. 0분 기록도 목록에 표시할 수 있으며 소모량은 0입니다.
사용 흐름 예시
1.
달리기 30분은 300kcal, 자전거 60분은 360kcal이며 합계는 660kcal입니다.
2.
기록 화면을 다시 열어도 시간과 소모량이 같습니다. 운동 시간 0분의 기록을 추가해도 기존 합계는 늘지 않습니다.
다음 기획 변경
분당 8kcal를 사용하는 수영 기록을 추가하려고 합니다. 같은 기록 화면과 합계에 포함하세요.
제공 main() · 기획을 사용하는 예시
#include <iostream>
#include <string>
#include <cassert>
// 이 위치에 필요한 클래스를 직접 구현하세요.
int main() {
Running run(30), rest(0);
Cycling cycling(60);
const Exercise* logs[] = { &run, &cycling, &rest };
int total = 0;
for (const Exercise* exercise : logs) {
exercise->PrintCalories();
total += exercise->CalculateCalories();
}
assert(total == 660);
assert(rest.CalculateCalories() == 0);
}
C++
복사
풀고 나서 펼치기 · 설계 회고와 관련 개념
문제 12. 책 정보 출력
기획 의도
도서 목록에서 책을 구분하는 기본 정보와 종류별 상세 정보를 확인합니다. 독자는 전자책인지 종이책인지에 따라 구매 전에 확인해야 할 정보가 다릅니다.
기본 기능과 동작 규칙
•
모든 책은 제목과 저자를 보여 줍니다. 전자책은 파일 크기를 MB 단위로, 종이책은 페이지 수를 추가로 안내합니다.
•
한 책의 소개에서 제목과 저자가 빠지거나 두 번 반복되지 않아야 합니다. 조회로 책 정보가 바뀌지 않습니다.
확장 요청
•
서로 다른 종류의 책을 같은 목록에서 소개합니다. 사용자는 책마다 따로 다른 조회 화면을 실행하지 않아도 상세 정보를 볼 수 있습니다.
사용 흐름 예시
1.
Clean Code 전자책은 Robert Martin과 5.2MB를, Refactoring 종이책은 Martin Fowler와 450페이지를 제목과 함께 보여 줍니다.
2.
목록 순서를 바꾸면 소개 순서만 바뀝니다. 전자책에 페이지 수를 표시하거나 종이책에 파일 크기를 표시하지 않습니다.
다음 기획 변경
재생 시간을 분 단위로 보여 주는 오디오북을 추가하려고 합니다. 제목과 저자를 포함한 같은 도서 목록에 넣으세요.
제공 main() · 기획을 사용하는 예시
#include <iostream>
#include <string>
// 이 위치에 필요한 클래스를 직접 구현하세요.
int main() {
EBook ebook("Clean Code", "Robert Martin", 5.2);
PaperBook paper("Refactoring", "Martin Fowler", 450);
const Book* books[] = { &ebook, &paper };
for (const Book* book : books) {
book->Display();
}
// 전자책: 제목 / 저자 / 5.2 MB
// 종이책: 제목 / 저자 / 450 pages
}
C++
복사
풀고 나서 펼치기 · 설계 회고와 관련 개념
문제 13. RPG 캐릭터의 정보 확장
기획 의도
RPG의 캐릭터 상태 창입니다. 플레이어가 캐릭터를 선택하면 이름, 현재 체력, 공격력을 한 화면에서 확인합니다. 여러 직업이 있어도 같은 상태 보기 기능을 사용합니다.
기본 기능과 동작 규칙
•
전사와 마법사는 각각 이름·체력·공격력을 가집니다. 체력은 생존 상태를, 공격력은 공격 능력을 나타내며 둘은 서로 다른 정보입니다.
•
상태 화면은 세 정보를 모두 보여 줍니다. 이번 문제에서는 실제 공격이나 피해 계산 없이 등록된 정보를 안내합니다.
확장 요청
•
여러 직업을 한 캐릭터 목록에서 조회합니다. 목록에 새 직업을 넣어도 기존 상태 화면의 표시 내용이 빠지지 않아야 합니다.
사용 흐름 예시
1.
Gorath는 체력 100·공격력 30, Azura는 체력 80·공격력 50으로 안내합니다. 이름과 수치가 다른 캐릭터에 섞이지 않습니다.
2.
같은 캐릭터의 상태를 다시 열면 같은 정보를 보여 줍니다. 조회 횟수 때문에 체력이나 공격력이 변하지 않습니다.
다음 기획 변경
세 번째 직업의 이름·체력·공격력을 정해 추가하려고 합니다. 상태 창에서 같은 정보 항목을 보여 주세요.
제공 main() · 기획을 사용하는 예시
#include <iostream>
#include <string>
// 이 위치에 필요한 클래스를 직접 구현하세요.
int main() {
Warrior warrior("Gorath", 100, 30);
Wizard wizard("Azura", 80, 50);
const Character* party[] = { &warrior, &wizard };
for (const Character* character : party) {
character->ShowStatus();
}
// Gorath / HP 100 / ATK 30
// Azura / HP 80 / ATK 50
}
C++
복사
풀고 나서 펼치기 · 설계 회고와 관련 개념
문제 14. 교통수단의 시동과 운행
기획 의도
운행 관리 화면에서 자동차와 오토바이의 시동을 제어하고 운행을 요청합니다. 시동을 켜는 일과 실제 운행은 서로 다른 동작입니다.
기본 기능과 동작 규칙
•
두 교통수단은 처음에 시동이 꺼져 있습니다. 시동을 켠 뒤에만 운행할 수 있으며 시동이 꺼져 있을 때의 운행은 거절합니다.
•
켜기·운행·끄기 결과에는 어떤 교통수단을 처리했는지 표시합니다. 시동을 이미 켰거나 껐더라도 같은 요청을 다시 받으면 정상 처리하고 현재 상태를 유지합니다.
확장 요청
•
여러 교통수단에 시동 켜기 → 운행 → 시동 끄기를 진행할 수 있어야 합니다. 한 교통수단의 시동 조작이 다른 교통수단의 시동을 바꾸지 않습니다.
사용 흐름 예시
1.
자동차의 시동을 켜고 운행을 요청하면 운행됩니다. 이후 시동을 끄고 운행을 요청하면 운행되지 않습니다.
2.
꺼진 오토바이에 끄기를 다시 요청해도 정상 처리됩니다. 켜기를 두 번 요청한 뒤에는 시동이 켜진 상태입니다.
다음 기획 변경
버스를 등록하려고 합니다. 같은 시동 조작과 운행 흐름에서 사용할 수 있게 하세요.
제공 main() · 기획을 사용하는 예시
#include <iostream>
#include <string>
#include <cassert>
// 이 위치에 필요한 클래스를 직접 구현하세요.
int main() {
Car car;
Motorcycle motorcycle;
Vehicle* vehicles[] = { &car, &motorcycle };
for (Vehicle* vehicle : vehicles) {
bool ok = vehicle->Drive();
assert(!ok);
ok = vehicle->StartEngine();
assert(ok);
ok = vehicle->StartEngine();
assert(ok);
ok = vehicle->Drive();
assert(ok);
ok = vehicle->StopEngine();
assert(ok);
ok = vehicle->Drive();
assert(!ok);
}
}
C++
복사
풀고 나서 펼치기 · 설계 회고와 관련 개념
문제 15. 상점 아이템 정보
기획 의도
게임 상점의 상품 목록입니다. 구매자는 이름과 가격뿐 아니라 무기나 물약을 사용했을 때 기대할 수 있는 효과를 확인합니다.
기본 기능과 동작 규칙
•
무기는 이름·가격·공격력을, 물약은 이름·가격·회복량을 안내합니다. 가격과 효과 수치는 모두 0 이상입니다.
•
상품을 조회하는 일은 구매하거나 사용하는 일이 아닙니다. 정보를 여러 번 확인해도 가격과 효과가 그대로 유지됩니다.
확장 요청
•
서로 다른 상품을 한 목록에서 보여 주고 목록 전체의 가격 합계도 안내합니다. 합계에는 각 상품의 가격만 포함하며 공격력이나 회복량을 더하지 않습니다.
사용 흐름 예시
1.
Iron Sword가 가격 100·공격력 25, Health Potion이 가격 50·회복량 30이면 두 상품 가격의 합계는 150입니다.
2.
상품 순서를 바꾸어도 합계는 150이며 각 상품의 상세 정보는 자신의 종류에 맞게 표시됩니다.
다음 기획 변경
가격과 방어력을 가진 갑옷을 추가하려고 합니다. 같은 상품 목록과 합계 기능에 포함하세요.
제공 main() · 기획을 사용하는 예시
#include <iostream>
#include <string>
#include <cassert>
// 이 위치에 필요한 클래스를 직접 구현하세요.
int main() {
Weapon sword("Iron Sword", 100, 25);
Potion potion("Health Potion", 50, 30);
const Item* items[] = { &sword, &potion };
int total = 0;
for (const Item* item : items) {
item->GetInfo();
total += item->GetPrice();
}
assert(total == 150);
sword.GetInfo(); // 다시 조회해도 같은 정보
potion.GetInfo();
}
C++
복사
풀고 나서 펼치기 · 설계 회고와 관련 개념
문제 16. 계좌 입출금과 이자
기획 의도
은행 창구에서 저축 계좌와 일반 입출금 계좌를 관리합니다. 고객은 입금·출금·잔액 조회를 같은 방식으로 요청하지만 계좌 종류에 따라 이자와 출금 수수료가 다릅니다.
기본 기능과 동작 규칙
•
저축 계좌는 잔액 1000, 이율 3%로 시작합니다. 양수 금액만 입금·출금할 수 있고 출금 수수료는 없습니다. 잔액보다 많은 출금은 거절합니다.
•
이자는 고객이 이자 적용을 요청한 때의 잔액을 기준으로 한 번 붙습니다. 이자 금액의 소수 부분은 버리며 잔액 조회만으로 이자가 붙지는 않습니다.
확장 요청
•
일반 입출금 계좌는 잔액 2000, 출금 수수료 15로 시작합니다. 출금액과 수수료를 모두 낼 수 있을 때만 출금됩니다. 입금에는 수수료가 없습니다.
•
거절된 거래에서는 돈이나 수수료가 일부만 빠져나가면 안 됩니다. 0 이하 금액 요청도 거절하며 거래 전 잔액을 유지합니다.
사용 흐름 예시
1.
저축 계좌에 500을 입금하고 200을 출금하면 1300이 남습니다. 이자를 한 번 적용하면 39가 추가되어 1339이며 조회를 다시 해도 1339입니다.
2.
일반 계좌에 500을 입금하고 200을 출금하면 수수료 15도 빠져 2285가 남습니다.
3.
일반 계좌의 잔액이 215이면 200을 출금할 수 있고 0이 남습니다. 잔액이 214인 경우에는 출금이 거절되며 214가 그대로 남습니다.
다음 기획 변경
일반 계좌의 수수료를 20으로 바꾸려 합니다. 새 수수료에서도 출금 가능 여부와 잔액 안내가 일치하도록 하세요.
제공 main() · 기획을 사용하는 예시
#include <iostream>
#include <string>
#include <cassert>
// 이 위치에 필요한 클래스를 직접 구현하세요.
int main() {
SavingsAccount savings(1000, 0.03);
CheckingAccount checking(2000, 15);
Account* accounts[] = { &savings, &checking };
for (Account* account : accounts) {
bool ok = account->Deposit(500);
assert(ok);
ok = account->Withdraw(200);
assert(ok);
account->ShowBalance();
}
assert(savings.GetBalance() == 1300);
assert(checking.GetBalance() == 2285);
savings.ApplyInterest();
assert(savings.GetBalance() == 1339);
CheckingAccount exact(215, 15), shortByOne(214, 15);
bool ok = exact.Withdraw(200);
assert(ok && exact.GetBalance() == 0);
ok = shortByOne.Withdraw(200);
assert(!ok && shortByOne.GetBalance() == 214);
ok = savings.Deposit(-100);
assert(!ok && savings.GetBalance() == 1339);
}
C++
복사
풀고 나서 펼치기 · 설계 회고와 관련 개념
문제 17. 음료 자판기
기획 의도
음료를 선택하고 돈을 넣으면 한 잔을 제공하는 자판기입니다. 구매자는 음료를 받거나 구매가 거절된 이유와 돌려받을 돈을 확인할 수 있어야 합니다.
기본 기능과 동작 규칙
•
Coke는 가격 1500·재고 2, Orange는 가격 2000·재고 1로 시작합니다. 투입액은 0 이상의 정수입니다.
•
선택한 음료의 재고가 있고 돈이 충분할 때만 한 잔을 제공합니다. 성공하면 재고가 1 줄며 가격을 제외한 나머지 금액을 거스름돈으로 알려 줍니다.
확장 요청
•
재고 부족 또는 금액 부족이면 음료를 제공하지 않습니다. 재고를 유지하고 투입금을 전부 반환합니다. 이번에는 가격 변경과 동전 재고 관리를 다루지 않습니다.
사용 흐름 예시
1.
Coke에 2000을 넣으면 한 잔이 나오고 500을 돌려받습니다. Coke 재고는 1이 됩니다.
2.
Orange에 2000을 넣으면 한 잔이 나오고 거스름돈은 0입니다. 이어 같은 음료를 다시 구매하면 품절이므로 2000을 전부 돌려받습니다.
3.
Coke에 1000을 넣으면 돈이 부족해 구매할 수 없으며 1000을 전부 돌려받습니다. 남아 있던 Coke 재고 1은 유지됩니다.
다음 기획 변경
새 음료와 처음부터 품절인 음료를 메뉴에 추가하려고 합니다. 같은 구매·환불 규칙을 적용하세요.
제공 main() · 기획을 사용하는 예시
#include <iostream>
#include <string>
#include <cassert>
// 이 위치에 필요한 클래스를 직접 구현하세요.
int main() {
Soda soda("Coke", 1500, 2);
Juice juice("Orange", 2000, 1);
Drink* drinks[] = { &soda, &juice };
for (Drink* drink : drinks) {
int returnedMoney = -1;
const bool ok = drink->Dispense(2000, returnedMoney);
assert(ok);
std::cout << returnedMoney << '\n'; // 500, 0
}
assert(soda.GetStock() == 1 && juice.GetStock() == 0);
int returnedMoney = -1;
bool ok = juice.Dispense(2000, returnedMoney);
assert(!ok && returnedMoney == 2000);
ok = soda.Dispense(1000, returnedMoney);
assert(!ok && returnedMoney == 1000 && soda.GetStock() == 1);
}
C++
복사
풀고 나서 펼치기 · 설계 회고와 관련 개념
문제 18. 교통수단 요금 계산
기획 의도
여행자가 이동 거리와 교통수단에 따른 예상 요금을 비교합니다. 이번 기획은 견적 안내까지이며 실제 운행이나 결제는 하지 않습니다.
기본 기능과 동작 규칙
•
거리는 0 이상의 정수 km입니다. 택시는 km당 1000원, 버스는 거리와 관계없이 1500원입니다.
•
0km도 유효한 견적 요청입니다. 따라서 택시는 0원, 버스는 1500원으로 안내합니다. 요금 조회로 거리나 요금 기준이 달라지지 않습니다.
확장 요청
•
서로 다른 교통수단의 예상 요금을 한 목록에서 확인하고 합계를 볼 수 있어야 합니다.
사용 흐름 예시
1.
5km 이동을 택시와 버스로 각각 조회하면 5000원과 1500원이며 합계는 6500원입니다.
2.
같은 견적을 다시 조회하면 같은 요금입니다. 0km의 버스 견적도 정액 요금 1500원이 적용됩니다.
다음 기획 변경
기본요금 1000원에 km당 200원을 더하는 지하철을 추가하려고 합니다. 기존 견적 비교 목록에 포함하세요.
제공 main() · 기획을 사용하는 예시
#include <iostream>
#include <string>
#include <cassert>
// 이 위치에 필요한 클래스를 직접 구현하세요.
int main() {
Taxi taxi(5), zeroTaxi(0);
Bus bus(5), zeroBus(0);
const Transport* transports[] = { &taxi, &bus };
int total = 0;
for (const Transport* transport : transports) {
std::cout << transport->CalculateFare() << '\n';
total += transport->CalculateFare();
}
assert(total == 6500);
assert(zeroTaxi.CalculateFare() == 0);
assert(zeroBus.CalculateFare() == 1500);
}
C++
복사
풀고 나서 펼치기 · 설계 회고와 관련 개념
문제 19. 학생 성적 판정
기획 의도
학교 성적표에서 학생의 이름·점수·등급을 확인합니다. 같은 점수라도 학부와 대학원은 서로 다른 등급 기준을 사용합니다.
기본 기능과 동작 규칙
•
학부생은 90점 이상 A, 80점 이상 B, 70점 이상 C, 나머지는 F입니다. 대학원생은 95점 이상 A, 85점 이상 B, 나머지는 F입니다.
•
점수는 0부터 100까지의 정수입니다. 성적표를 열면 해당 학생에게 적용되는 기준으로 등급을 안내합니다.
확장 요청
•
점수를 수정할 수 있어야 합니다. 수정된 점수의 등급은 다음 성적표 조회부터 반영됩니다. 범위 밖 점수는 받아들이지 않고 이전 점수를 유지합니다.
사용 흐름 예시
1.
학부생 90점과 대학원생 95점은 모두 A입니다. 대학원생 90점은 B이며 학부생과 기준이 다릅니다.
2.
학부생은 69점에서 F, 70점에서 C로 바뀝니다. 대학원생은 84점에서 F, 85점에서 B입니다.
3.
101점으로 수정하려 하면 거절됩니다. 성적표에는 수정 전 점수와 그 등급이 남아 있어야 합니다.
다음 기획 변경
학교가 등급 기준을 바꾸려 합니다. 새 기준표를 직접 정하고 성적표에 적용하세요.
제공 main() · 기획을 사용하는 예시
#include <iostream>
#include <string>
#include <cassert>
// 이 위치에 필요한 클래스를 직접 구현하세요.
int main() {
UndergraduateStudent undergrad("Kim", 92);
GraduateStudent graduate("Lee", 92);
Student* students[] = { &undergrad, &graduate };
for (const Student* student : students) {
std::cout << student->GetName() << ": " << student->GetGrade() << '\n';
}
assert(undergrad.GetGrade() == 'A');
assert(graduate.GetGrade() == 'B');
bool ok = undergrad.SetScore(89);
assert(ok && undergrad.GetGrade() == 'B');
ok = undergrad.SetScore(90);
assert(ok && undergrad.GetGrade() == 'A');
ok = graduate.SetScore(95);
assert(ok && graduate.GetGrade() == 'A');
ok = graduate.SetScore(101);
assert(!ok && graduate.GetScore() == 95);
}
C++
복사
풀고 나서 펼치기 · 설계 회고와 관련 개념
문제 20. 로봇 이동과 작업
기획 의도
작업 관리 화면에서 청소 로봇과 배달 로봇에 이동과 작업을 요청합니다. 배터리는 실제로 완료할 수 있는 동작에만 소비됩니다.
기본 기능과 동작 규칙
•
두 로봇은 배터리 10으로 시작합니다. 청소 로봇은 이동에 2·작업에 3, 배달 로봇은 이동에 1·작업에 4를 소비합니다.
•
필요한 배터리 이상이 남아 있어야 동작할 수 있습니다. 완료된 동작과 남은 배터리를 안내합니다. 배터리는 0 아래로 내려가지 않습니다.
확장 요청
•
각 로봇에서 이동 후 작업을 두 번 반복할 수 있어야 합니다. 배터리가 부족한 요청은 실행하지 않고 이전 배터리를 유지합니다.
사용 흐름 예시
1.
청소 로봇은 이동·작업 후 5, 다시 이동·작업 후 0이 남습니다. 배달 로봇도 같은 두 회차를 마치면 0입니다.
2.
배터리가 0인 로봇에 추가 이동이나 작업을 요청하면 거절됩니다. 거절되었다는 이유로 배터리가 음수가 되지 않습니다.
다음 기획 변경
이동에 3·작업에 2를 쓰는 점검 로봇을 추가하려고 합니다. 같은 작업 요청 화면에서 사용할 수 있게 하세요.
제공 main() · 기획을 사용하는 예시
#include <iostream>
#include <string>
#include <cassert>
// 이 위치에 필요한 클래스를 직접 구현하세요.
int main() {
CleaningRobot cleaner("Roomba", 10);
DeliveryRobot delivery("DeliveryBot", 10);
Robot* robots[] = { &cleaner, &delivery };
for (int round = 0; round < 2; ++round) {
for (Robot* robot : robots) {
bool ok = robot->Move();
assert(ok);
ok = robot->Work();
assert(ok);
std::cout << robot->GetBattery() << '\n';
}
}
for (Robot* robot : robots) {
assert(robot->GetBattery() == 0);
const bool moved = robot->Move();
const bool worked = robot->Work();
assert(!moved && !worked && robot->GetBattery() == 0);
}
}
C++
복사
풀고 나서 펼치기 · 설계 회고와 관련 개념
3단계 · 공통 약속과 역할 (21~25)
문제 21. 구체적인 도형의 면적 안내
기획 의도
면적 안내 서비스에서 실제 모양과 치수가 정해진 도형만 계산 대상으로 제공합니다. 종류가 정해지지 않은 막연한 “도형”에는 임의의 면적을 붙이지 않습니다.
기본 기능과 동작 규칙
•
기본 선택지는 원과 정사각형입니다. 원에는 반지름, 정사각형에는 한 변의 길이가 필요하며 길이는 0 이상입니다.
•
원의 면적은 3.14159×반지름×반지름, 정사각형은 한 변×한 변입니다. 어떤 도형을 선택하든 사용자는 면적 조회라는 같은 기능을 사용합니다.
확장 요청
•
선택한 여러 도형을 한 번에 조회할 수 있어야 합니다. 서비스에 새 도형이 들어오면 그 도형의 계산 기준이 마련된 후 선택할 수 있습니다.
사용 흐름 예시
1.
반지름 5인 원은 78.53975, 한 변 4인 정사각형은 16입니다. 크기가 0인 도형은 면적 0입니다.
2.
면적 조회를 반복해도 입력 치수와 결과는 같습니다. 구체적인 모양 없이 임의의 기본 면적을 안내하지 않습니다.
다음 기획 변경
면적 계산 기준이 있는 다른 도형을 선택지에 추가하려고 합니다. 같은 면적 조회에서 사용할 수 있게 하세요.
제공 main() · 기획을 사용하는 예시
#include <iostream>
#include <string>
#include <cassert>
#include <cmath>
// 이 위치에 필요한 클래스를 직접 구현하세요.
int main() {
// Shape impossible; // 이 줄만 켜면 추상 클래스 생성 오류가 나야 함
Circle circle(5.0);
Square square(4.0);
const Shape* shapes[] = { &circle, &square };
for (const Shape* shape : shapes) {
std::cout << shape->Area() << '\n';
}
assert(std::abs(circle.Area() - 78.53975) < 0.001);
assert(std::abs(square.Area() - 16.0) < 0.001);
}
C++
복사
풀고 나서 펼치기 · 설계 회고와 관련 개념
문제 22. 도형의 면적과 둘레
기획 의도
도형 제작 견적에서 면적과 둘레가 모두 필요합니다. 사용자는 도형별 치수를 입력하고 두 정보를 한 번에 확인하거나 필요한 정보 하나만 조회할 수 있습니다.
기본 기능과 동작 규칙
•
직사각형은 가로·세로, 원은 반지름, 삼각형은 세 변으로 크기를 정합니다. 원주율은 3.14159를 사용하고 삼각형에는 실제 삼각형이 되는 치수만 입력합니다.
•
삼각형의 둘레는 세 변의 합이며 면적은 헤론 공식을 사용합니다. 세 변을 a·b·c, 반둘레를 s=(a+b+c)/2라고 하면 면적은 s(s-a)(s-b)(s-c)의 제곱근입니다.
확장 요청
•
어떤 도형을 선택해도 면적과 둘레를 모두 안내해야 합니다. 한 값을 조회한다고 치수를 바꾸거나 다른 계산 결과를 소비하지 않습니다.
사용 흐름 예시
1.
가로 4·세로 5인 직사각형은 면적 20·둘레 18입니다. 반지름 3인 원은 면적 28.27431·둘레 18.84954입니다.
2.
세 변이 3·4·5인 삼각형은 면적 6·둘레 12입니다. 면적만 여러 번 조회한 뒤 둘레를 조회해도 둘레 12입니다.
다음 기획 변경
넓이만 필요한 안내 화면과 둘레만 필요한 안내 화면을 추가하려고 합니다. 같은 도형 정보로 각각 이용할 수 있게 하세요.
제공 main() · 기획을 사용하는 예시
#include <iostream>
#include <string>
#include <cassert>
#include <cmath>
// 이 위치에 필요한 클래스를 직접 구현하세요.
int main() {
Rectangle rectangle(4.0, 5.0);
Circle circle(3.0);
Triangle triangle(3.0, 4.0, 5.0);
const Shape* shapes[] = { &rectangle, &circle, &triangle };
for (const Shape* shape : shapes) {
std::cout << shape->Area() << " / " << shape->Perimeter() << '\n';
}
assert(std::abs(rectangle.Area() - 20.0) < 0.001);
assert(std::abs(rectangle.Perimeter() - 18.0) < 0.001);
assert(std::abs(circle.Area() - 28.27431) < 0.001);
assert(std::abs(circle.Perimeter() - 18.84954) < 0.001);
assert(std::abs(triangle.Area() - 6.0) < 0.001);
assert(std::abs(triangle.Perimeter() - 12.0) < 0.001);
}
C++
복사
풀고 나서 펼치기 · 설계 회고와 관련 개념
문제 23. 전자기기의 기본 동작
기획 의도
전자기기 관리 화면에서 모델 정보와 전원을 확인합니다. 전원을 끄는 결과는 같지만 제품별로 사용자에게 보여 주는 종료 안내가 다를 수 있습니다.
기본 기능과 동작 규칙
•
스마트폰과 스마트워치는 각각 모델명이 있으며 처음에는 꺼져 있습니다. 켜기·끄기·정보 조회를 제공합니다.
•
정보 화면에는 제품 종류·모델명·현재 전원을 보여 줍니다. 스마트폰의 종료 문구는 phone shutdown, 스마트워치는 모델명 뒤에 power off를 표시합니다.
확장 요청
•
같은 전원 요청을 반복해도 일관되게 처리합니다. 정보를 확인하는 것만으로 전원이 바뀌면 안 됩니다.
사용 흐름 예시
1.
두 기기를 켜면 각각 자신의 모델과 켜진 상태를 보여 줍니다. 종료 후에는 각 제품의 종료 안내와 꺼진 상태를 확인할 수 있습니다.
2.
꺼진 기기를 다시 끄거나 켜진 기기를 다시 켜도 해당 전원을 유지합니다. 다른 제품의 모델명으로 안내하지 않습니다.
다음 기획 변경
기본 종료 안내를 사용하는 태블릿을 추가하려고 합니다. 기존 전원 제어와 정보 화면에서 사용할 수 있게 하세요.
제공 main() · 기획을 사용하는 예시
#include <iostream>
#include <string>
#include <cassert>
// 이 위치에 필요한 클래스를 직접 구현하세요.
int main() {
Smartphone phone("Galaxy");
SmartWatch watch("Watch");
Device* devices[] = { &phone, &watch };
for (Device* device : devices) {
assert(!device->IsOn());
device->PowerOn();
device->ShowInfo();
assert(device->IsOn());
device->PowerOff();
assert(!device->IsOn());
device->PowerOff();
assert(!device->IsOn());
}
// phone shutdown / Watch power off
}
C++
복사
풀고 나서 펼치기 · 설계 회고와 관련 개념
문제 24. 그릴 수 있는 객체
기획 의도
간단한 그림 목록을 표시하는 화면입니다. 사용자가 원이나 직사각형을 배치하면 각 도형이 어느 위치에 어떤 크기로 표시될지 확인할 수 있습니다.
기본 기능과 동작 규칙
•
원은 중심 위치와 반지름, 직사각형은 시작 위치와 가로·세로로 표현합니다. 실제 그림 대신 콘솔에 위치와 크기 정보를 안내합니다.
•
그림 목록은 등록 순서대로 표시합니다. 한 번 더 표시한다고 도형이 이동하거나 커지지 않습니다.
확장 요청
•
사용자는 도형 종류마다 별도의 표시 기능을 실행하지 않고 목록 전체를 한 번에 볼 수 있어야 합니다.
사용 흐름 예시
1.
원과 직사각형을 등록하면 원의 중심·반지름, 직사각형의 시작점·가로·세로가 각각 안내됩니다.
2.
같은 목록을 두 번 표시하면 같은 위치와 크기입니다. 목록 순서를 바꾸면 표시 순서만 달라집니다.
다음 기획 변경
위치와 문구를 가진 텍스트 표시를 추가하려고 합니다. 도형과 텍스트를 같은 화면 목록에서 표시하세요.
제공 main() · 기획을 사용하는 예시
#include <iostream>
#include <string>
// 이 위치에 필요한 클래스를 직접 구현하세요.
int main() {
Circle circle(0, 0, 5);
Rectangle rectangle(1, 2, 4, 3);
const IDrawable* drawables[] = { &circle, &rectangle };
for (int pass = 0; pass < 2; ++pass) {
for (const IDrawable* drawable : drawables) {
drawable->Draw();
}
}
// 매회 circle / 0,0 / r=5
// rectangle / 1,2 / 4x3
}
C++
복사
풀고 나서 펼치기 · 설계 회고와 관련 개념
문제 25. 업데이트와 화면 표시
기획 의도
게임 화면의 한 장면을 진행하는 기획입니다. 먼저 모든 참여자의 움직임을 끝내고, 그 결과를 한 화면에 보여 줍니다. 화면을 새로 보여 주는 일만으로 게임 시간이 더 흐르지는 않습니다.
기본 기능과 동작 규칙
•
플레이어는 x좌표 0, 적은 10에서 시작합니다. 장면이 한 번 진행될 때 플레이어는 1 증가, 적은 1 감소합니다.
•
한 장면에서는 모든 참여자의 위치 변경을 먼저 마친 뒤 현재 위치를 표시합니다. 일부는 새 위치, 일부는 이전 위치인 화면을 보여 주지 않습니다.
확장 요청
•
장면 진행과 현재 화면 다시 보기를 각각 제공합니다. 화면 다시 보기는 위치를 유지합니다. 이후에는 화면에 보이지만 움직이지 않는 요소도 들어올 수 있습니다.
사용 흐름 예시
1.
첫 장면의 화면은 플레이어 1·적 9입니다. 다음 장면의 화면은 플레이어 2·적 8입니다.
2.
두 번째 장면 이후 화면만 다시 보면 2와 8입니다. 추가 이동은 다음 장면을 진행할 때 일어납니다.
서비스 흐름
flowchart LR A["장면 진행 요청"] --> B["모든 참여자 이동 완료"] B --> C["현재 위치로 화면 표시"] C --> D["다음 장면 대기"]
Mermaid
복사
다음 기획 변경
표시만 하는 배경을 추가하려고 합니다. 배경을 보여 주기 위해 불필요한 이동 동작을 요구하지 않도록 하세요.
제공 main() · 기획을 사용하는 예시
#include <iostream>
#include <string>
#include <cassert>
// 이 위치에 필요한 클래스를 직접 구현하세요.
int main() {
Player player("Hero", 0);
Enemy enemy("Goblin", 10);
IUpdatable* updatables[] = { &player, &enemy };
const IRenderable* renderables[] = { &player, &enemy };
for (int frame = 0; frame < 2; ++frame) {
for (IUpdatable* object : updatables) {
object->Update();
}
for (const IRenderable* object : renderables) {
object->Render();
}
}
assert(player.GetX() == 2 && enemy.GetX() == 8);
player.Render();
assert(player.GetX() == 2); // 표시는 이동을 일으키지 않음
}
C++
복사
풀고 나서 펼치기 · 설계 회고와 관련 개념
회차 기록과 완료 기준
문제·회차 | 읽은 핵심 규칙 | 자신의 설계 선택 | 실제 동작과 기획의 차이 | 추가 기획에 따른 변경 |
예: 16번·2회차 | 출금액과 수수료를 모두 낼 수 있어야 출금 | 선택한 구조와 그 이유를 본인이 기록 | 고객에게 보인 잔액이 기획과 일치했는지 기록 | 수수료 변경을 어디에 반영했는지 기록 |
첫 회차에는 기획을 이해하고 자신의 설계를 완성합니다. 두 번째 회차에는 이전 구현을 보지 않고 같은 기획과 main()에서 다시 만듭니다. 세 번째 회차에는 추가 기획을 적용하고 자신이 바꾼 판단을 기록합니다.
서비스의 기능과 규칙을 자신의 말로 설명할 수 있다.
기획을 바탕으로 대상·정보·행동·관계를 스스로 정했다.
제공 main()과 사용 흐름의 결과가 기획에 맞는다.
이전 구현을 보지 않고 다시 설계하고 구현했다.
추가 기획을 반영하면서 바꾼 설계와 이유를 설명할 수 있다.
완료 기준은 특정 패턴 이름이나 클래스 배치가 아니라 기획에 맞는 동작, 자신의 설계 이유, 반복해서 설계하는 능력입니다.
선택 참고 · C++ 문법 자료



