F-lab Java 1์ฃผ์ฐจ / Phase 1 / Unit 1.1 ๋ณธ๊ฒฉ ํ์ต ์๋ฃ
9-์น์ ๋ง์คํฐ ํ๋กฌํํธ ํ์์ผ๋ก ๊น์ด ํํค์น๋ค.์ ์ ์ง์: ์์ (ํ์ต ์ฌ์ ์ ์ถ๋ฐ์ )
๋ค์ Unit: 1.2 โ ํด๋์ค์ ๊ฐ์ฒด์ ๋ณธ์ง
๊ฐ์ฅ ๊ทผ๋ณธ์ ์ธ ์ฐจ์ด๋ "๋ฐ์ดํฐ์ ์ฃผ์ธ์ด ๋๊ตฌ๋" ์ด๋ค.
์ด ์ฐจ์ด๋ ์ฌ๊ณ ๋ฐฉ์์์๋ ๋๋ฌ๋๋ค:
๊ฐ์ฒด์งํฅ์ ์ ์ฐจ์งํฅ์ 3๊ฐ์ง ํฐ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ฑ์ฅํ์ต๋๋ค.
๊ฐ์ ์ผ์ ๋ ๋ฐฉ์์ผ๋ก ํํํด๋ณด์.
์ํฉ: ๊ฐ์์ง๋ฅผ ์ง๊ฒ ํ๊ณ ์ถ๋ค.
์ง๊ฒ_ํ๋ค(๊ฐ์์ง)
โ "์ง๋ค" ๋ผ๋ ๋์ฌ(ํจ์) ๊ฐ ์ฃผ์ธ๊ณต
โ ๊ฐ์์ง๋ ๊ทธ๋ฅ ์ฌ๋ฃ(๋ฐ์ดํฐ)
โ ์ฌ๊ณ ํ๋ฆ: "์ง๋ ํ๋์ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ ๊น?"
๊ฐ์์ง.์ง๋ค()
โ "๊ฐ์์ง" ๋ผ๋ ๋ช
์ฌ(๊ฐ์ฒด) ๊ฐ ์ฃผ์ธ๊ณต
โ ์ง๋ ๋ฅ๋ ฅ์ ๊ฐ์์ง๊ฐ ์์
โ ์ฌ๊ณ ํ๋ฆ: "๋๊ฐ ์ง๋ ์ฑ
์์ ๊ฐ์ง๊น?"
| ์ ์ฐจ์งํฅ (๋์ฌ ์ค์ฌ) | ๊ฐ์ฒด์งํฅ (๋ช ์ฌ ์ค์ฌ) | |
|---|---|---|
| ์ฝ๋ ํํ | ์ง๊ฒ_ํ๋ค(๊ฐ์์ง) | ๊ฐ์์ง.์ง๋ค() |
| ์ฃผ์ธ๊ณต | ๋์ฌ (ํ๋) | ๋ช ์ฌ (๊ฐ์ฒด) |
| ์ฌ๊ณ | "์ด๋ป๊ฒ ์ฒ๋ฆฌํ ๊น" | "๋๊ฐ ์ฑ ์์ง๊น" |
| ๋ฐ์ดํฐ | ํจ์์ ์ ๋ฌ๋จ | ๊ฐ์ฒด๊ฐ ์์ ํจ |
| ๋ช ๋ น ๋ฐฉ์ | "์ด ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํด" | "๋ ์ด๊ฑฐ ํด" |
์ด ์ฌ๊ณ ๋ฐฉ์์ ์ฐจ์ด๊ฐ ๋ง์์ ์งํ ๋น์ ๋ก ํ์ด๋ณด๋ฉด ๋ ๋ช ํํด์ง๋ค.
์๋ ํ ๋ง์์ด ์์๋ค. ์ฒ์์ ์์ ๋ง์์ด์์ง๋ง ์ ์ ์ปค์ก๋ค. ๊ทธ๋ฌ๋ฉด์ ๋ฌธ์ ๊ฐ ์๊ฒผ๊ณ , ๋ง์์ ์ด์ ๋ฐฉ์์ด ์งํํ๋ค.
์ด ๋ง์์ ์ด์ผ๊ธฐ๊ฐ ๊ณง ์ ์ฐจ์งํฅ์์ ๊ฐ์ฒด์งํฅ์ผ๋ก์ ์งํ ์ด์ผ๊ธฐ๋ค.
๋ง์ ํ๊ฐ์ด๋ฐ์ ๊ณต๋ ์ฐฝ๊ณ ๊ฐ ์๋ค. ๋ชจ๋ ๋ง์ ์ฌ๋์ ๋ฌผ๊ฑด์ด ์ฌ๊ธฐ์ ๋ชจ์ฌ์๋ค.
๋ง์ ์ฌ๋๋ค์ "์ผ๊พผ" ๋ค. ๋๊ฐ ๋ฌด์์ ํ ์ง๋ ์ ํด์ ธ ์๋ค:
๋ง์์ด ์์ ๋ ์ด ๋ฐฉ์์ด ์ ๊ตด๋ฌ๊ฐ๋ค.
[๊ณต๋ ์ฐฝ๊ณ : ์, ์ท, ๋๊ตฌ, ...] โ ๋ฐ์ดํฐ (๋ช
์ฌ)
โ (๊ฐ์ ธ๊ฐ)
[์ผ๊พผ A] โ ์๋ฆฌ โ ํจ์ (๋์ฌ)
[์ผ๊พผ B] โ ๋นจ๋ โ ํจ์ (๋์ฌ)
[์ผ๊พผ C] โ ์ฒญ์ โ ํจ์ (๋์ฌ)
โ ์ฌ๊ธฐ์ ์ฃผ์ธ๊ณต์ ์ผ๊พผ(๋์ฌ) ์ด๋ค. ์ฐฝ๊ณ ๋ ๊ทธ๋ฅ ์ฌ๋ฃ ๋ณด๊ด์.
โ ์ฝ๋๋ ๋ง์ฐฌ๊ฐ์ง: ์๋ฆฌํ๋ค(์), ๋นจ๋ํ๋ค(์ท) โ ๋์ฌ๊ฐ ์ฃผ์ธ๊ณต.
// ๊ณต๋ ์ฐฝ๊ณ = ๊ตฌ์กฐ์ฒด์ ๋ชจ๋ ๋ฐ์ดํฐ ๋ชจ์ (๋ช
์ฌ โ ๋จ์ ์ฌ๋ฃ)
struct Warehouse {
int chulsuRice; // ์ฒ ์์ ์
char younghuiClothes[100]; // ์ํฌ์ ์ท
char minsuTools[100]; // ๋ฏผ์์ ๋๊ตฌ
};
// ์ผ๊พผ๋ค = ๋
๋ฆฝ๋ ํจ์๋ค (๋์ฌ โ ์ฃผ์ธ๊ณต)
void cook(struct Warehouse* w) {
// ์ฐฝ๊ณ ์์ ์ ๊ฐ์ ธ๊ฐ์ ๋ฐฅ ํจ
w->chulsuRice -= 10;
printf("๋ฐฅ ์์ฑ\n");
}
void wash(struct Warehouse* w) {
// ์ฐฝ๊ณ ์์ ์ท ๊ฐ์ ธ๊ฐ์ ๋นจ๋
printf("%s ๋นจ๋ ์๋ฃ\n", w->younghuiClothes);
}
void clean(struct Warehouse* w) {
// ์ฐฝ๊ณ ์์ ๋๊ตฌ ๊ฐ์ ธ๊ฐ์ ์ฒญ์
printf("%s๋ก ์ฒญ์\n", w->minsuTools);
}
// ์ฌ์ฉ โ ๋์ฌ(ํจ์)๊ฐ ์ฃผ์ธ๊ณต, ๋ช
์ฌ(๋ฐ์ดํฐ)๋ ์ธ์๋ก ๋๊ฒจ์ง
int main() {
struct Warehouse warehouse = { 50, "์
์ธ ", "๋น์๋ฃจ" };
cook(&warehouse); // "์๋ฆฌํ๋ค(์ฐฝ๊ณ )" โ ๋์ฌ ์ค์ฌ
wash(&warehouse); // "๋นจ๋ํ๋ค(์ฐฝ๊ณ )" โ ๋์ฌ ์ค์ฌ
clean(&warehouse); // "์ฒญ์ํ๋ค(์ฐฝ๊ณ )" โ ๋์ฌ ์ค์ฌ
}
โ ์ด๊ฒ ์ ์ฐจ์งํฅ: ๋ฐ์ดํฐ(์ฐฝ๊ณ ๊ตฌ์กฐ์ฒด) ์ ํ๋(ํจ์) ์ด ๋ถ๋ฆฌ๋จ. ํจ์(๋์ฌ)๋ ๋ฐ์ดํฐ๋ฅผ ์ธ์๋ก ๋ฐ์์ ์ฒ๋ฆฌ.
๋ง์์ด ์ปค์ง๋ฉด์ ๋ฌธ์ ๊ฐ ํญ๋ฐํ๊ธฐ ์์ํ๋ค.
์ํฉ: ๊ณต๋ ์ฐฝ๊ณ ์ ๋๊ตฌ๋ ์ ๊ทผ ๊ฐ๋ฅํ๋ค.
์ ๊ทธ๋ด๊น? ๋ฐ์ดํฐ๋ ์ฃผ์ธ ์๋ ๋ช ์ฌ ๋ผ์, ๋์ฌ(ํจ์)๋ค์ด ๋ง์๋๋ก ๋ง์ง ์ ์๊ธฐ ๋๋ฌธ.
struct Warehouse warehouse = { 50, "์
์ธ ", "๋น์๋ฃจ" };
// ๋๊ตฌ๋ ์ง์ ๋ฐ์ดํฐ๋ฅผ ๋ง์ง ์ ์์ โ ๋ช
์ฌ๊ฐ ๋ณดํธ๋ฐ์ง ๋ชปํจ
warehouse.chulsuRice = -99999; // ์์? ๊ฐ๋ฅ โ
strcpy(warehouse.younghuiClothes, ""); // ๋น ๋ฌธ์์ด? ๊ฐ๋ฅ โ
strcpy(warehouse.minsuTools, "๐คก"); // ์ด์ํ ๊ฐ? ๊ฐ๋ฅ โ
// ๊ฒ์ฆ ํจ์๋ฅผ ๋ง๋ค์ด๋, ํธ์ถ ์ ํ๋ฉด ๊ทธ๋ง
void validateRice(int rice) {
if (rice < 0) printf("์๋ฌ!\n");
}
// โ ๋๊ตฐ๊ฐ validateRice ํธ์ถ ์ ํ๊ณ ์ง์ ์์ ํ๋ฉด ๋ โ
// ๋๊ฐ ๋ง๊ฐ๋จ๋ ธ๋์ง ์ถ์ ๋ถ๊ฐ
// 100๊ฐ ํจ์ ์ค ์ด๋ ํจ์๊ฐ chulsuRice๋ฅผ ์์๋ก ๋ง๋ค์๋? ๋ชจ๋ฆ
โ ๋ฐ์ดํฐ(๋ช ์ฌ)์ ์ฃผ์ธ์ด ์์ผ๋, ๋๊ตฌ๋ ๋ง๊ฐ๋จ๋ฆผ.
์ํฉ: ๋ง์์ ์ธ๊ตญ์ธ ๊ฐ์กฑ์ด ์ด์ฌ ์๋ค. ์ด ๊ฐ์กฑ์ ๋ค๋ฅธ ์์์ ๋จน๊ณ , ๋ค๋ฅธ ์ท์ ์ ๋๋ค.
์ด์ ๋ชจ๋ ์ผ๊พผ์๊ฒ ์ ๊ท์น์ ๊ฐ๋ฅด์ณ์ผ ํ๋ค:
๋ ๋ค๋ฅธ ์ธ๊ตญ์ธ ๊ฐ์กฑ์ด ์ค๋ฉด? ๋ชจ๋ ์ผ๊พผ์ ๋ ๋ค ๊ฐ๋ฅด์ณ์ผ ํจ.
์ ๊ทธ๋ด๊น? ๋์ฌ(ํจ์)๊ฐ ์ฃผ์ธ๊ณต์ด๋ผ, ๊ฐ ๋์ฌ ์์ ๋ชจ๋ ์ข ๋ฅ์ ์ฒ๋ฆฌ๋ฅผ ๋ค ๋ฃ์ด์ผ ํ๊ธฐ ๋๋ฌธ. ์ ์ข ๋ฅ๊ฐ ์ถ๊ฐ๋๋ฉด ๋ชจ๋ ๋์ฌ์ ์ ๋ถ๊ธฐ ์ถ๊ฐ.
struct Person {
char name[50];
char nationality[20]; // "ํ๊ตญ", "์ธ๊ตญ", "์ผ๋ณธ", "์ค๊ตญ" ...
};
// ๋ชจ๋ ํจ์(๋์ฌ)์ if ๋ถ๊ธฐ๊ฐ ๋์ด๋จ
void cook(struct Person* p) {
if (strcmp(p->nationality, "ํ๊ตญ") == 0) {
printf("๋ฐฅ์ ํฉ๋๋ค\n");
} else if (strcmp(p->nationality, "์ธ๊ตญ") == 0) {
printf("๋นต์ ๊ตฝ์ต๋๋ค\n");
} else if (strcmp(p->nationality, "์ผ๋ณธ") == 0) { // ์ ๊ตญ์ ์ถ๊ฐ
printf("์ค์๋ฅผ ๋ง๋ญ๋๋ค\n");
}
// ์ ๊ตญ์ ์ถ๊ฐ๋ ๋๋ง๋ค ์ฌ๊ธฐ ์์ โ
}
void wash(struct Person* p) {
if (strcmp(p->nationality, "ํ๊ตญ") == 0) {
printf("ํ๊ตญ์ ๋นจ๋\n");
} else if (strcmp(p->nationality, "์ธ๊ตญ") == 0) {
printf("์ธ๊ตญ์ ๋นจ๋\n");
} else if (strcmp(p->nationality, "์ผ๋ณธ") == 0) { // ๋ ์ถ๊ฐ
printf("์ผ๋ณธ์ ๋นจ๋\n");
}
// ๋ ์์ โ
}
void clean(struct Person* p) {
if (strcmp(p->nationality, "ํ๊ตญ") == 0) { ... }
else if (strcmp(p->nationality, "์ธ๊ตญ") == 0) { ... }
else if (strcmp(p->nationality, "์ผ๋ณธ") == 0) { ... } // ๋๋ ์ถ๊ฐ โ
}
// "์ค๊ตญ์ธ" ๊ฐ์กฑ ์ถ๊ฐ โ ์ ๋ชจ๋ ํจ์ ์์ ํ์
// ํจ์๊ฐ 200๊ฐ๋ผ๋ฉด? 200๊ฐ ๋ค ์์ โ
// ํ ๊ตฐ๋ฐ๋ผ๋ ๋น ๋จ๋ฆฌ๋ฉด? ๋ฒ๊ทธ โ
โ ์ ์ข ๋ฅ(๋ช ์ฌ)๊ฐ ์ถ๊ฐ๋๋ฉด ๋ชจ๋ ๋์ฌ๋ฅผ ์์ ํด์ผ ํจ.
์ํฉ: ๋ง์ ์ธ๊ตฌ๊ฐ 1,000๋ช ์ด ๋์๋ค.
// ๊ฑฐ๋ํ ๊ณต์ฉ ๋ฐ์ดํฐ (์ฃผ์ธ ์๋ ๋ช
์ฌ)
struct VillageData {
int totalPopulation;
int totalRice;
int totalClothes;
char warehouseStatus[1000];
// ... 100๊ฐ ํ๋
};
// 5,000๊ฐ์ ํจ์(๋์ฌ)๊ฐ ์ด ๋ฐ์ดํฐ๋ฅผ ๋ง์ง
void functionA(struct VillageData* v) { v->totalRice += 10; }
void functionB(struct VillageData* v) { v->totalRice -= 5; }
void functionC(struct VillageData* v) { v->totalRice = v->totalRice * 2; }
// ... ํจ์ 5,000๊ฐ ...
void function4999(struct VillageData* v) { v->totalRice = 0; } // ๋๊ฐ ๋ง๋ ํจ์?
// ์ด๋ ๋ totalRice ๊ฐ์ด ์ด์ํจ
// โ 5,000๊ฐ ํจ์ ์ค ์ด๋ ๊ฒ ์๋ชป ๊ฑด๋๋ ธ๋?
// โ ์ถ์ ๊ฑฐ์ ๋ถ๊ฐ๋ฅ โ
// ์ ์
๊ฐ๋ฐ์๊ฐ ๋ค์ด์ด
// "totalRice ํ๋๋ฅผ ์ด๋ป๊ฒ ์ฌ์ฉํ๋ฉด ๋๋์?"
// โ "5,000๊ฐ ํจ์ ๋ค ๋ด์ผ ์ ์ ์์ด์" โ
// ํ ๊ฐ๋ฐ์๊ฐ totalRice๋ฅผ long์ผ๋ก ๋ฐ๊พธ๋ฉด
// โ 5,000๊ฐ ํจ์ ๋ชจ๋ ์์ ํ์ โ
โ ์์คํ ์ด ์ปค์ง๋ฉด ํ์ ์์ฒด๊ฐ ๋ถ๊ฐ๋ฅ.
๋ง์ ์ฌ๋๋ค์ด ๋ชจ์ฌ์ ๊ฒฐ์ ํ๋ค:
"๊ณต๋ ์ฐฝ๊ณ ๋ฅผ ์์ ์. ๊ฐ์ ์๊ธฐ ๋ฌผ๊ฑด์ ์๊ธฐ๊ฐ ๊ด๋ฆฌํ์."
๊ทธ๋์ ๊ฐ์์ ์ง ์ ์ง๊ณ , ๊ฐ์์ ๋ฌผ๊ฑด ์ ๊ฐ์์ ์ง ์ ๋๊ธฐ๋ก ํ๋ค.
[์ฒ ์์ ์ง] [์ํฌ์ ์ง] [๋ฏผ์์ ์ง]
- ๊ฐ์ง ๊ฒ: ์, ๋๊ตฌ - ๊ฐ์ง ๊ฒ: ์ท - ๊ฐ์ง ๊ฒ: ๋๊ตฌ
- ํ ์ค ์๋ ๊ฒ: - ํ ์ค ์๋ ๊ฒ: - ํ ์ค ์๋ ๊ฒ:
โข ๋ฐฅ ์ง๊ธฐ โข ๋นจ๋ํ๊ธฐ โข ์ฒญ์ํ๊ธฐ
โข ์ฒญ์ํ๊ธฐ โข ์ท ์ ๋ฆฌํ๊ธฐ โข ๋๊ตฌ ์ ๋น
ํต์ฌ ๋ณํ:
โ ์ด์ ์ฃผ์ธ๊ณต์ ๋ช
์ฌ(๊ฐ์ฒด) ๋ค.
โ ์ฝ๋๋ ๋ฐ๋๋ค: ์๋ฆฌํ๋ค(์) โ ์ฒ ์.๋ฐฅ์ง๋ค() โ ๋ช
์ฌ๊ฐ ์ฃผ์ธ๊ณต, ๋์ฌ๋ ๋ช
์ฌ์ ๋ฅ๋ ฅ.
โ ์ด๊ฒ ๊ฐ์ฒด์งํฅ: ๋ฐ์ดํฐ(๋ช ์ฌ)์ ํ๋(๋์ฌ)์ ๊ฐ์ฒด๊ฐ ํจ๊ป ์์ .
๋น์ : ์ํฌ์ ์ท์ฅ์๋ ์ํฌ๋ง ์๋ ์ ์๋ค.
Java ์ฝ๋๋ก:
public class Fare {
private int amount; // private โ ์ธ๋ถ ์ง์ ์ ๊ทผ X
public void changeAmount(int newAmount) {
if (newAmount < 0) {
throw new IllegalArgumentException("์์ ๋ถ๊ฐ"); // ์๊ธฐ๊ฐ ๊ฒ์ฆ
}
this.amount = newAmount;
}
}
// ์ฌ์ฉ โ ๋ช
์ฌ(fare)๊ฐ ์ฃผ์ธ๊ณต
Fare fare = new Fare();
fare.changeAmount(50000); // "fare๊ฐ ์๊ธฐ ๊ธ์ก์ ๋ฐ๊พผ๋ค" โ ๋ช
์ฌ ์ค์ฌ
fare.changeAmount(-99999); // ์ฆ์ ์์ธ! โ
โ fare ์์ ์ด ๊ฑฐ๋ถ
// fare.amount = -99999; // ์ปดํ์ผ ์๋ฌ! โ
โ ์ ์ด์ ์ ๊ทผ ๋ถ๊ฐ
โ ๋ฐ์ดํฐ(๋ช ์ฌ)์ ์ฃผ์ธ์ด ์๊ธฐ๋, ์ฃผ์ธ์ด ๋ณดํธํจ.
๋น์ : ์ธ๊ตญ์ธ ๊ฐ์กฑ์ด ์ด์ฌ ์๋ค.
Java ์ฝ๋๋ก:
public abstract class Customer {
public abstract int calculateDiscount(int amount);
}
public class VipCustomer extends Customer {
public int calculateDiscount(int amount) { return amount * 20 / 100; }
}
public class PartnerCustomer extends Customer {
public int calculateDiscount(int amount) { return amount * 30 / 100; }
}
// ์ ๋ฑ๊ธ ์ถ๊ฐ โ ์ ํด๋์ค(๋ช
์ฌ) 1๊ฐ๋ง ๋ง๋ค๋ฉด ๋ โ
public class StudentCustomer extends Customer {
public int calculateDiscount(int amount) { return amount * 15 / 100; }
}
// ์ฌ์ฉํ๋ ์ชฝ์ ํ ์ค๋ ์ ๋ฐ๋
customer.calculateDiscount(amount); // ๋ช
์ฌ๊ฐ ์์์ ์๊ธฐ ๋ฐฉ์์ผ๋ก
// โ "customer๊ฐ ์๊ธฐ ํ ์ธ์ ๊ณ์ฐํ๋ค" โ ๋ช
์ฌ๊ฐ ์ฃผ์ธ๊ณต
// โ if ๋ถ๊ธฐ ์์, ๊ฐ ๋ช
์ฌ๊ฐ ์๊ธฐ ํ๋ ์ฑ
์
โ ์ ๋ช ์ฌ(์ข ๋ฅ) ์ถ๊ฐ ์ ๊ธฐ์กด ์ฝ๋๋ ์ ๊ฑด๋๋ฆผ.
๋น์ : 1,000๋ช ์ด ์ฌ๋ ๋ง์์ด์ง๋ง ์ด์ ์๋ํ๋ค.
Java ์ฝ๋๋ก:
ํ๋ก์ ํธ ์ธ์: 50๋ช
- Aํ: Fare ํด๋์ค(๋ช
์ฌ) ๋ด๋น
- Bํ: Customer ํด๋์ค(๋ช
์ฌ) ๋ด๋น
- Cํ: Notification ํด๋์ค(๋ช
์ฌ) ๋ด๋น
๊ฐ ํ์ ์๊ธฐ ๋ช
์ฌ๋ง ์ฑ
์
๋ค๋ฅธ ํ์ ๋ด๋ถ ๊ตฌํ์ ์ ํ์ X
์ธํฐํ์ด์ค(๋ฉ์๋ ์๊ทธ๋์ฒ)๋ง ์ฝ์ํ๋ฉด ํ์
๊ฐ๋ฅ โ
โ ๋ช ์ฌ๋ณ๋ก ์ฑ ์์ด ๋ถ๋ฆฌ๋๋ ๋๊ท๋ชจ ํ์ ๊ฐ๋ฅ.
| ์์ ๋ง์ (์ ์ฐจ์งํฅ) | ํฐ ๋ง์ (๊ฐ์ฒด์งํฅ) | |
|---|---|---|
| ์ฌ๊ณ ๋ฐฉ์ | ๋์ฌ ์ค์ฌ | ๋ช ์ฌ ์ค์ฌ |
| ์ฝ๋ ํํ | ์ฒ๋ฆฌํ๋ค(๋ฐ์ดํฐ) | ๊ฐ์ฒด.์ฒ๋ฆฌํ๋ค() |
| ๋ฐ์ดํฐ ์์น | ๊ณต๋ ์ฐฝ๊ณ | ๊ฐ์์ ์ง |
| ์ฃผ์ธ | ์์ | ๊ฐ ์ง์ ์ฃผ์ธ |
| ์ ๊ทผ ๋ฐฉ์ | ๋๊ตฌ๋ ์ง์ | ์ฃผ์ธ์๊ฒ ๋ถํ |
| ์ ์ข ๋ฅ ์ถ๊ฐ | ๋ชจ๋ ์ผ๊พผ(๋์ฌ) ์ฌ๊ต์ก | ์ ์ง(๋ช ์ฌ) ์ง๊ธฐ๋ง |
| ๋ฌด๊ฒฐ์ฑ | ๊นจ์ง๊ธฐ ์ฌ์ | ์ฃผ์ธ์ด ๋ณดํธ |
| ํ์ฅ ๋น์ฉ | ํญ๋ฐ์ ์ฆ๊ฐ | ๋ฎ์ |
| ํ์ | ๋ถ๊ฐ๋ฅ | ๊ฐ๋ฅ |
์ ์ฐจ์งํฅ์ "๋์ฌ๊ฐ ์ฃผ์ธ๊ณต" ์ด๋ผ ๊ณต๋ ์ฐฝ๊ณ ์ ๋ชจ๋์ ๋ฐ์ดํฐ๊ฐ ๋ ๋ค๋ ๊ณ , ๋๊ตฌ๋ ๋ง๊ฐ๋จ๋ฆด ์ ์๊ณ , ์ ์ข ๋ฅ ์ถ๊ฐ ์ ๋ชจ๋ ๋์ฌ๋ฅผ ์ฌ๊ต์กํด์ผ ํ๋ค.
๊ฐ์ฒด์งํฅ์ "๋ช ์ฌ๊ฐ ์ฃผ์ธ๊ณต" ์ผ๋ก ๋ฐ๋์ด, ๊ฐ์์ ์ง์ ์๊ธฐ ๋ฐ์ดํฐ๋ฅผ ๋๊ณ ์ฃผ์ธ์ ํตํด์๋ง ์ ๊ทผํ๊ฒ ํจ์ผ๋ก์จ, ๋ฌด๊ฒฐ์ฑ ๋ณด์ฅ + ํ์ฅ ์ฉ์ด + ํ์ ๊ฐ๋ฅ์ด๋ผ๋ 3๊ฐ์ง ๋ฌธ์ ๋ฅผ ํ ๋ฒ์ ํด๊ฒฐํ๋ค.
์ปดํจํฐ ํ๋ก๊ทธ๋๋ฐ ์ด๊ธฐ์๋ ์ ์ฐจ์งํฅ์ด ์์ฐ์ค๋ฌ์ ๋ค. ์๋ํ๋ฉด:
๋ํ ์ธ์ด:
์ด ์๋์ ํ๋ก๊ทธ๋จ์ "ํจ์์ ๋ชจ์" ์ด์๋ค. ๋ฐ์ดํฐ๋ ๋ณ๋๋ก ๊ด๋ฆฌํ๊ณ , ํจ์๊ฐ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ์ฒ๋ฆฌํ๋ ๋ฐฉ์.
๋ฌธ์ ๊ฐ ์๊ธฐ๊ธฐ ์์ํ๋ค. ์ํํธ์จ์ด๊ฐ ์ปค์ง๋ฉด์:
์ด ์๊ธฐ๋ฅผ ํด๊ฒฐํ๋ ค๋ ์๋๋ก ๊ฐ์ฒด์งํฅ์ด ํ์:
์๋ฐ๋ "ํ๋ซํผ ๋ ๋ฆฝ์ฑ" ๊ณผ "์์ OOP" ๋ฅผ ๋ฌด๊ธฐ๋ก ํญ๋ฐ์ ์ผ๋ก ์ฑ์ฅ. 90๋ ๋ ์ธํฐ๋ท ํญ๋ฐ๊ณผ ํจ๊ป ํ์ค ์ธ์ด๊ฐ ๋จ.
"์ ์ฐจ์งํฅ์ ํ๋ ธ๋ค ๊ฐ ์๋๋ค. ํฐ ์์คํ ์์ ํ๊ณ ๊ฐ ์๋ค."
์๊ท๋ชจ ์คํฌ๋ฆฝํธ๋ ์ฌ์ ํ ์ ์ฐจ์งํฅ์ด ํจ์จ์ . ํ์ง๋ง ILIC ๊ฐ์ 102 ํ ์ด๋ธ, 431 API ๊ท๋ชจ์ ์์คํ ์ ๊ฐ์ฒด์งํฅ ์์ด๋ ๊ด๋ฆฌ ๋ถ๊ฐ๋ฅ.
๊ฐ์ฒด์งํฅ์ด ์๋ค๋ฉด? ์ ์ฐจ์งํฅ๋ง์ผ๋ก ํฐ ์์คํ ์ ๋ง๋ค๋ฉด ์ด๋ค ๋ฌธ์ ๊ฐ ์๊ธฐ๋์ง ๊ตฌ์ฒด์ ์ธ ์์ ๋ก ๋ณด์.
// ๋ฐ์ดํฐ (๊ตฌ์กฐ์ฒด)
struct Fare {
int id;
int customerId;
int amount;
char status[20];
char currency[10];
int createdAt;
// ... 30๊ฐ ํ๋
};
struct Customer {
int id;
char name[100];
char email[200];
int level; // 0=์ผ๋ฐ, 1=VIP
// ...
};
// ํจ์๋ค
int calculateFare(struct Fare* fare, int distance);
void applyDiscount(struct Fare* fare, struct Customer* customer);
void sendNotification(struct Fare* fare, struct Customer* customer);
void saveFare(struct Fare* fare);
void validateFare(struct Fare* fare);
void printFareReceipt(struct Fare* fare);
// ... 200๊ฐ ํจ์
struct Fare fare;
fare.amount = 50000;
fare.status = "DRAFT";
// ๊ฒ์ฆ ์ ํ๋๋ฐ ์ ์ฅ ๊ฐ๋ฅ โ
saveFare(&fare);
// ๋๊ตฐ๊ฐ ์ง์ ์์ ๊ฐ๋ฅ โ
fare.amount = -99999; // ์์! ๋ฌด๊ฒฐ์ฑ ๊นจ์ง
saveFare(&fare);
์ ์ํํ๊ฐ:
VIP ๊ณ ๊ฐ์ ์ํ ์ ๊ธฐ๋ฅ ์ถ๊ฐ ์๋๋ฆฌ์ค:
// ๋ชจ๋ ํจ์์ if ๋ถ๊ธฐ ์ถ๊ฐ โ ๏ธ
int calculateFare(struct Fare* fare, int distance) {
if (fare->customer->level == 1) { // VIP
return distance * 400; // ํ ์ธ
} else {
return distance * 500;
}
}
void applyDiscount(struct Fare* fare, struct Customer* customer) {
if (customer->level == 1) { // ๋ if!
// VIP ํ ์ธ
} else {
// ์ผ๋ฐ ํ ์ธ
}
}
void sendNotification(struct Fare* fare, struct Customer* customer) {
if (customer->level == 1) { // ๋ if!!
// VIP ์ ์ฉ ์๋ฆผ
} else {
// ์ผ๋ฐ ์๋ฆผ
}
}
// ... 200๊ฐ ํจ์์ if ์ถ๊ฐ โ
์ ๋์ฐํ๊ฐ:
void send(struct Email* email); // ์ด๋ฉ์ผ ๋ณด๋ด๊ธฐ
void send(struct SMS* sms); // ์ปดํ์ผ ์๋ฌ! โ
void send(struct Slack* slack); // C์์๋ ๊ฐ์ ์ด๋ฆ ํจ์ X
ํด๊ฒฐ์ฑ ์ผ๋ก ๋ง๋ค์ด์ง ์ด์ํ ์ด๋ฆ๋ค:
void sendEmail(...);
void sendSMS(...);
void sendSlackMessage(...);
void sendNotificationToCustomer(...);
void sendUrgentNotificationToVIPCustomer(...); // ์ ์ ๊ธธ์ด์ง
// A ํ๋ก์ ํธ์์ ๋ง๋ ์ด์ ๊ณ์ฐ ๋ก์ง
int calculateFare(struct Fare* fare) { ... }
// B ํ๋ก์ ํธ์์ ๊ฐ์ ธ๋ค ์ฐ๋ ค๋ฉด?
// โ struct Fare ์ ์๋ ๊ฐ์ ธ์์ผ ํจ
// โ ์์กดํ๋ ํจ์ 5๊ฐ๋ ๊ฐ์ ธ์์ผ ํจ
// โ ๊ทธ ํจ์๋ค์ด ์์กดํ๋ ํจ์ 10๊ฐ๋...
// โ ๊ฒฐ๊ตญ ๊ฑฐ์ ๋ค ๊ฐ์ ธ์์ผ ํจ โ
ํฐ ์์คํ
์์ ์ ์ฐจ์งํฅ์:
1. ๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑ ๋ณด์ฅ ์ด๋ ค์
2. ์์ ์ ์ํฅ ๋ฒ์ ํญ๋ฐ (ํ ๊ณณ ๊ณ ์น๋ฉด 100๊ณณ)
3. ํ์ฅ์ฑ ์ ๋ก (์ ๊ธฐ๋ฅ ์ถ๊ฐ๊ฐ ๋๋ ค์)
4. ์ฌ์ฌ์ฉ ๋ถ๊ฐ (๋ค๋ฅธ ํ๋ก์ ํธ ํ์ฉ X)
5. ํ์
์ง์ฅ (๋๊ฐ ๋ญ ๋ง์ง๋์ง ๋ชจ๋ฆ)
โ ์ด ๋ชจ๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ค๊ณ ๊ฐ์ฒด์งํฅ์ด ๋ฑ์ฅ.
๊ฐ์ฒด์งํฅ์ ์ ๋ฌธ์ ๋ค์ 4๊ฐ์ง ํต์ฌ ์์น ์ผ๋ก ํด๊ฒฐํ๋ค. (์ด๊ฑด ๋ชจ๋ ๋ฉด์ ์ ๋จ๊ณจ์ด๋ค โญ)
"๋ฐ์ดํฐ์ ๊ทธ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃจ๋ ํ๋์ ํ๋์ ๊ฐ์ฒด๋ก ๋ฌถ๊ณ , ์ธ๋ถ์์ ์ง์ ์ ๊ทผ์ ๋ง์"
์ ๋ฌธ์ 1 ํด๊ฒฐ (๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑ):
// ๊ฐ์ฒด์งํฅ ๋ฒ์
public class Fare {
private int amount; // private โ ์ธ๋ถ ์ง์ ์ ๊ทผ X
private String status;
// ๊ฒ์ฆ๋ ๋ฐฉ๋ฒ์ผ๋ก๋ง ๋ณ๊ฒฝ ๊ฐ๋ฅ
public void changeAmount(int newAmount) {
if (newAmount < 0) {
throw new IllegalArgumentException("์์ ๋ถ๊ฐ");
}
this.amount = newAmount;
}
public int getAmount() {
return this.amount;
}
}
// ์ฌ์ฉ
Fare fare = new Fare();
fare.changeAmount(50000); // OK
fare.changeAmount(-99999); // ์ฆ์ ์์ธ! โ
// fare.amount = -99999; // ์ปดํ์ผ ์๋ฌ! โ
ํจ๊ณผ:
"๊ธฐ์กด ํด๋์ค์ ์์ฑ๊ณผ ํ๋์ ๋ฌผ๋ ค๋ฐ์ ํ์ฅ"
์ ๋ฌธ์ 2 ํด๊ฒฐ (ํ์ฅ์ฑ):
public class Customer {
protected String name;
protected String email;
public int calculateDiscount(int amount) {
return 0; // ๊ธฐ๋ณธ ํ ์ธ ์์
}
}
public class VipCustomer extends Customer {
@Override
public int calculateDiscount(int amount) {
return amount * 20 / 100; // 20% ํ ์ธ
}
}
public class PartnerCustomer extends Customer {
@Override
public int calculateDiscount(int amount) {
return amount * 30 / 100; // 30% ํ ์ธ
}
}
ํจ๊ณผ:
name, email) ์ ๋ถ๋ชจ์ ํ ๋ฒ๋ง"๊ฐ์ ์ด๋ฆ์ ๋ฉ์๋๊ฐ ๊ฐ์ฒด์ ๋ฐ๋ผ ๋ค๋ฅด๊ฒ ๋์"
์ ๋ฌธ์ 2 ํด๊ฒฐ์ ํ์ฅ:
public class FareService {
public int calculate(Fare fare, Customer customer) {
int baseAmount = fare.getAmount();
int discount = customer.calculateDiscount(baseAmount); // โญ
return baseAmount - discount;
}
}
// ์ฌ์ฉ
Customer alice = new VipCustomer();
Customer bob = new PartnerCustomer();
Customer charlie = new Customer();
fareService.calculate(fare, alice); // 20% ํ ์ธ ์ ์ฉ
fareService.calculate(fare, bob); // 30% ํ ์ธ ์ ์ฉ
fareService.calculate(fare, charlie); // ํ ์ธ ์์
ํต์ฌ ํต์ฐฐ โญ :
FareService ์ฝ๋์ if ๋ถ๊ธฐ๊ฐ ์ฌ๋ผ์งFareService ๋ ์๋ ํ์ Xโ ์ด๊ฒ ๊ฐ์ฒด์งํฅ์ ์ง์ง ๋ง๋ฒ.
"๋ณต์กํ ๋ด๋ถ๋ฅผ ์จ๊ธฐ๊ณ ๋ณธ์ง๋ง ๋๋ฌ๋"
์ ๋ฌธ์ 3, 4 ํด๊ฒฐ:
// ์ธํฐํ์ด์ค๋ก ์ถ์ํ
public interface NotificationSender {
void send(String message, String recipient);
}
// ๋ค์ํ ๊ตฌํ์ฒด
public class EmailSender implements NotificationSender {
@Override
public void send(String message, String recipient) {
// ์ด๋ฉ์ผ ๋ฐ์ก ๋ก์ง
}
}
public class SMSSender implements NotificationSender {
@Override
public void send(String message, String recipient) {
// SMS ๋ฐ์ก ๋ก์ง
}
}
public class SlackSender implements NotificationSender {
@Override
public void send(String message, String recipient) {
// Slack ๋ฐ์ก ๋ก์ง
}
}
// ์ฌ์ฉ โ ์ถ์ํ์ ํ
public class NotificationService {
private NotificationSender sender; // ์ธํฐํ์ด์ค ํ์
!
public void notify(String message, String recipient) {
sender.send(message, recipient); // ์ด๋ค ๊ตฌํ์ด๋ OK
}
}
ํจ๊ณผ:
send) ์ผ๋ก ๋ค์ํ ๋์ ๊ฐ๋ฅ (์ด๋ฆ ์ถฉ๋ ํด๊ฒฐ)| ์์น | ์๋ฌธ | ํ ์ค ์ค๋ช |
|---|---|---|
| ์บก์ํ | Encapsulation | ๋ฐ์ดํฐ์ ํ๋์ ๋ฌถ๊ณ ์ธ๋ถ ์ ๊ทผ ํต์ |
| ์์ | Inheritance | ๋ถ๋ชจ์ ํน์ฑ์ ์์์ด ๋ฌผ๋ ค๋ฐ์ |
| ๋คํ์ฑ | Polymorphism | ๊ฐ์ ๋ฉ์์ง์ ๊ฐ์ฒด๋ง๋ค ๋ค๋ฅธ ์๋ต |
| ์ถ์ํ | Abstraction | ๋ณต์กํจ์ ์จ๊ธฐ๊ณ ๋ณธ์ง๋ง ๋ ธ์ถ |
์๊ธฐ ํ: "์บก์๋ค์ถ" (์บก์ํ-์์-๋คํ์ฑ-์ถ์ํ)
์ด๋ก ์ ์ฝ๋ ๋ ๋ฒจ์์ ์ด๋ป๊ฒ ๊ตฌํ๋๋์ง ๋ณด์.
// C ์ฝ๋
struct Fare {
int amount;
char status[20];
};
void calculateFare(struct Fare* fare) {
fare->amount = fare->amount * 2;
}
int main() {
struct Fare fare;
fare.amount = 50000;
calculateFare(&fare);
}
๋ฉ๋ชจ๋ฆฌ ๊ตฌ์กฐ:
[Stack]
fare (๊ตฌ์กฐ์ฒด) โโโโโโโโโ
โ
[Code ์์ญ] โ
calculateFare ํจ์ โโโโโ (ํฌ์ธํฐ๋ก ์ ๋ฌ๋ฐ์)
ํต์ฌ:
public class Fare {
private int amount;
public void doubleAmount() {
this.amount = this.amount * 2;
}
}
Fare fare = new Fare();
fare.doubleAmount<();
๋ฉ๋ชจ๋ฆฌ ๊ตฌ์กฐ (4์ฃผ์ฐจ JVM ๋ฉ๋ชจ๋ฆฌ ๋ชจ๋ธ ๋ฏธ๋ฆฌ๋ณด๊ธฐ):
[Stack]
fare (์ฐธ์กฐ) โโโโโโโ
โ
[Heap] โ
Fare ์ธ์คํด์ค โโโโ
- amount: 50000
- (๋ฉ์๋๋ ํฌํจ X)
[Method Area]
Fare ํด๋์ค ์ ๋ณด
- doubleAmount() ๋ฉ์๋ ์ฝ๋ โญ
ํต์ฌ ์ฐจ์ด โญ :
this ์ ์ ์ฒดpublic class Fare {
private int amount;
public void doubleAmount() {
this.amount = this.amount * 2;
// โ
// "์ด ๋ฉ์๋๋ฅผ ํธ์ถํ ๊ฐ์ฒด"
}
}
Fare fare1 = new Fare(); fare1.amount = 100;
Fare fare2 = new Fare(); fare2.amount = 200;
fare1.doubleAmount(); // this = fare1, fare1.amount = 200
fare2.doubleAmount(); // this = fare2, fare2.amount = 400
JVM ๋ด๋ถ:
doubleAmount(fare1) ๊ฐ์ ํํ๋ก ๋ณํ๋คํ์ฑ์ ์ด๋ป๊ฒ ๊ฐ๋ฅํ ๊น? Virtual Method Table ๋๋ถ.
class Customer {
public int calculateDiscount(int amount) { return 0; }
}
class VipCustomer extends Customer {
@Override
public int calculateDiscount(int amount) { return amount * 20 / 100; }
}
Customer c = new VipCustomer();
c.calculateDiscount(1000); // 200 ๋ฐํ โ ์ด๋ป๊ฒ?
JVM์ ๋ฉ์๋ ํธ์ถ ํ๋ฆ โญ :
1. c.calculateDiscount(1000) ํธ์ถ
2. JVM: "c๊ฐ ๊ฐ๋ฆฌํค๋ ๊ฐ์ฒด์ ์ค์ ํด๋์ค๋?"
โ VipCustomer ์ธ์คํด์ค
3. JVM: "VipCustomer์ VMT ํ์ธ"
Customer์ VMT: VipCustomer์ VMT:
[calculateDiscount โ Customer.calculateDiscount] [calculateDiscount โ VipCustomer.calculateDiscount] โญ
[์์๋ฐ์ ๋ค๋ฅธ ๋ฉ์๋ โ Customer.xxx]
4. VipCustomer์ VMT์์ calculateDiscount ์ฐพ์
5. VipCustomer.calculateDiscount(1000) ์คํ โ 200 ๋ฐํ
ํต์ฌ:
โ 4-5์ฃผ์ฐจ์ ๋ ๊น์ด ๋ค๋ฃฐ ์ฃผ์ . ์ง๊ธ์ "๋คํ์ฑ์ด ๋ง๋ฒ์ด ์๋ VMT ๋๋ถ" ๋ง ๊ธฐ์ต.
ILIC ๋๋ฉ์ธ์ผ๋ก ์ ์ฐจ์งํฅ๊ณผ ๊ฐ์ฒด์งํฅ์ ์ง์ ๋น๊ตํด๋ณด์.
public class FareService {
// ๋ฐ์ดํฐ ํด๋์ค (Anemic Domain Model โ ์ํฐํจํด โ ๏ธ)
public static class Fare {
public int amount;
public String status;
public int customerId;
}
public static class Customer {
public int id;
public String name;
public String level; // "NORMAL", "VIP", "PARTNER"
}
// ๋ชจ๋ ๋ก์ง์ด service์ (์ ์ฐจ์งํฅ)
public int calculateFinalAmount(Fare fare, Customer customer) {
int amount = fare.amount;
// โ if ์ง์ฅ ์์
int discount = 0;
if (customer.level.equals("VIP")) {
discount = amount * 20 / 100;
} else if (customer.level.equals("PARTNER")) {
discount = amount * 30 / 100;
} else if (customer.level.equals("NORMAL")) {
discount = 0;
}
// ์ ๋ฑ๊ธ ์ถ๊ฐ ์ ์ฌ๊ธฐ ์์ ํ์ โ
return amount - discount;
}
public String getNotificationTemplate(Customer customer) {
// โ ๋ if!
if (customer.level.equals("VIP")) {
return "VIP ๊ณ ๊ฐ๋, ์ด์์ด ๋ฑ๋ก๋์์ต๋๋ค.";
} else if (customer.level.equals("PARTNER")) {
return "ํํธ๋๋, ์ด์ ์ ๋ณด๋ฅผ ํ์ธํด์ฃผ์ธ์.";
} else {
return "์ด์์ด ๋ฑ๋ก๋์์ต๋๋ค.";
}
// ์ ๋ฑ๊ธ ์ถ๊ฐ ์ ์ฌ๊ธฐ๋ ์์ โ
}
public boolean canApplyExtraDiscount(Customer customer) {
// โ ๋๋ if!
if (customer.level.equals("VIP") || customer.level.equals("PARTNER")) {
return true;
}
return false;
}
public void changeAmount(Fare fare, int newAmount) {
// โ ๊ฒ์ฆ์ ํธ์ถ์๊ฐ ์์์ ํด์ผ ํจ
fare.amount = newAmount; // ์์๋ ๋ค์ด๊ฐ!
}
}
๋ฌธ์ ์ :
1. if ์ง์ฅ: ๋ฑ๊ธ๋ณ ๋ถ๊ธฐ๊ฐ ๊ณณ๊ณณ์ ํฉ์ด์ง
2. ํ์ฅ ๋ถ๊ฐ: ์ ๋ฑ๊ธ ์ถ๊ฐ ์ ๋ชจ๋ ๋ฉ์๋ ์์
3. ๋ฌด๊ฒฐ์ฑ X: fare.amount = -99999 ๊ฐ๋ฅ
4. ์์ง๋ โ: ์ด์ ๊ด๋ จ ๋ก์ง์ด service์ ๋ค ๋ชจ์
// 1. Customer๋ ์๊ธฐ ํ ์ธ์ ์๋ค (๋คํ์ฑ)
public abstract class Customer {
private final String name;
private final String email;
public Customer(String name, String email) {
this.name = name;
this.email = email;
}
// ์ถ์ ๋ฉ์๋ โ ์์์ด ๊ตฌํ
public abstract int calculateDiscountRate();
public abstract String getNotificationGreeting();
// ๊ณตํต ๋ก์ง
public int applyDiscount(int amount) {
return amount * calculateDiscountRate() / 100;
}
public boolean canApplyExtraDiscount() {
return calculateDiscountRate() > 0;
}
// getter
public String getName() { return name; }
public String getEmail() { return email; }
}
public class NormalCustomer extends Customer {
public NormalCustomer(String name, String email) {
super(name, email);
}
@Override
public int calculateDiscountRate() { return 0; }
@Override
public String getNotificationGreeting() {
return "์๋
ํ์ธ์";
}
}
public class VipCustomer extends Customer {
public VipCustomer(String name, String email) {
super(name, email);
}
@Override
public int calculateDiscountRate() { return 20; }
@Override
public String getNotificationGreeting() {
return "VIP ๊ณ ๊ฐ๋";
}
}
public class PartnerCustomer extends Customer {
public PartnerCustomer(String name, String email) {
super(name, email);
}
@Override
public int calculateDiscountRate() { return 30; }
@Override
public String getNotificationGreeting() {
return "ํํธ๋๋";
}
}
// 2. Fare๋ ์๊ธฐ ๋ฐ์ดํฐ์ ๋ฌด๊ฒฐ์ฑ์ ์ฑ
์์ง๋ค (์บก์ํ)
public class Fare {
private int amount;
private FareStatus status;
private final Customer customer;
public Fare(int initialAmount, Customer customer) {
validateAmount(initialAmount);
this.amount = initialAmount;
this.status = FareStatus.DRAFT;
this.customer = customer;
}
public void changeAmount(int newAmount) {
validateAmount(newAmount);
this.amount = newAmount;
}
private void validateAmount(int amount) {
if (amount < 0) {
throw new IllegalArgumentException("์ด์์ ์์์ผ ์ ์์ต๋๋ค");
}
}
public int calculateFinalAmount() {
int discount = customer.applyDiscount(amount);
return amount - discount;
}
// getter
public int getAmount() { return amount; }
public Customer getCustomer() { return customer; }
}
// 3. Service๋ ํ๋ฆ๋ง ์กฐ์จ (์์์ง)
public class FareService {
public int calculateFinalAmount(Fare fare) {
return fare.calculateFinalAmount(); // ๊ฐ์ฒด์ ์์ โญ
}
public String createNotification(Fare fare) {
Customer customer = fare.getCustomer();
return String.format("%s, ์ด์์ด ๋ฑ๋ก๋์์ต๋๋ค (%d์)",
customer.getNotificationGreeting(),
fare.calculateFinalAmount());
}
}
ํจ๊ณผ โญ :
1. if ์ง์ฅ ์ฌ๋ผ์ง โ ๋คํ์ฑ์ด ์ฒ๋ฆฌ
2. ํ์ฅ ์ฌ์ โ ์ ๋ฑ๊ธ ์ถ๊ฐ ์ Customer ์์๋ง ๋ง๋ค๋ฉด ๋
3. ๋ฌด๊ฒฐ์ฑ ๋ณด์ฅ โ Fare ๊ฐ ์๊ธฐ ๊ฒ์ฆ
4. ์ฑ
์ ๋ถ๋ฆฌ โ ๊ฐ ํด๋์ค๊ฐ ์๊ธฐ ์ผ๋ง ํจ
์ ์ฐจ์งํฅ:
// 5๊ฐ ๋ฉ์๋ ๋ชจ๋ ์์ ํ์
if (level.equals("STUDENT")) { ... } // calculateFinalAmount
if (level.equals("STUDENT")) { ... } // getNotificationTemplate
if (level.equals("STUDENT")) { ... } // canApplyExtraDiscount
// ... ๋ชจ๋ ๋ถ๊ธฐ์ ์ถ๊ฐ
๊ฐ์ฒด์งํฅ:
// ์ ํด๋์ค 1๊ฐ๋ง ๋ง๋ค๋ฉด ๋!
public class StudentCustomer extends Customer {
public StudentCustomer(String name, String email) {
super(name, email);
}
@Override
public int calculateDiscountRate() { return 15; }
@Override
public String getNotificationGreeting() {
return "ํ์ ๊ณ ๊ฐ๋";
}
}
// ๊ธฐ์กด ์ฝ๋๋ ํ ์ค๋ ์ ๊ฑด๋๋ฆผ โญ
โ ์ด๊ฒ ๊ฐ์ฒด์งํฅ์ ์ง์ง ๊ฐ์น. SOLID์ OCP(Open-Closed Principle)์ ์ง๊ฒฐ.
๋นํ ๋๋ฉ์ธ ๋ชจ๋ธ = ๊ฐ์ฒด์งํฅ์ ํ์ ์ด ์ ์ฐจ์งํฅ
Service ๊ฐ์ ์ธ๋ถ ํด๋์ค์ ๋ชจ์ฌ์์์ด๋ฆ์ ์ ๋: "๋นํ(Anemic)" = ํผ(์๋ช ๋ ฅ=ํ๋)๊ฐ ์๋ ๊ฐ์ฒด๋ผ๋ ๋ป. Martin Fowler๊ฐ 2003๋ ๋ช ๋ช .
์ฌ๋์ ๋ชธ์ ๋น์ ํ๋ฉด:
๊ฑด๊ฐํ ์ฌ๋ (Rich Domain Model):
๋นํ ํ์ (Anemic Domain Model):
โ ๊ฐ์ฒด์ ์๊ธฐ ์ฑ ์์ด ์์ผ๋ฉด ๋นํ.
์๋ TV๋ฅผ ์์ํ์ธ์:
โ TV๋ ์๊ธฐ ํ๋์ด ์์. ์ฌ์ฉ์๊ฐ ๋ชจ๋ ๊ฑธ ํจ.
public class TV {
private int channel; // ๋ฐ์ดํฐ๋ง
private int volume;
private int brightness;
public int getChannel() { return channel; }
public void setChannel(int channel) { this.channel = channel; }
// ... ๋ค๋ฅธ getter/setter๋ค
}
// ๋ชจ๋ ๋ก์ง์ ์ธ๋ถ์์
public class TVUserService {
public void changeChannel(TV tv, int newChannel) {
if (newChannel < 1 || newChannel > 999) return;
tv.setChannel(newChannel);
}
public void increaseVolume(TV tv) {
int current = tv.getVolume();
if (current < 100) tv.setVolume(current + 1);
}
}
โ TV๋ ์๊ธฐ ์ฑ๋/๋ณผ๋ฅจ/๋ฐ๊ธฐ๋ฅผ ๋ชจ๋ฆ. TVUserService๊ฐ ๋ค ์์์ผ ํจ.
ํ๋ ์ค๋งํธ TV:
โ TV๊ฐ ์๊ธฐ ์ผ์ ์๊ธฐ๊ฐ ํจ. ์ฌ์ฉ์๋ ๋ช ๋ น๋ง.
public class SmartTV {
private int channel;
private int volume;
private int brightness;
// ์๊ธฐ ์ฑ
์์ ๊ฐ์ง
public void changeChannel(int newChannel) {
if (newChannel < 1 || newChannel > 999) {
throw new IllegalArgumentException("์ ํจํ์ง ์์ ์ฑ๋");
}
this.channel = newChannel;
}
public void increaseVolume() {
if (this.volume >= 100) return; // ์๊ธฐ๊ฐ ํ๊ณ ๊ฒ์ฆ
this.volume += 1;
}
}
// โ ๋นํ ๋ชจ๋ธ
@Entity
public class Fare {
@Id
private Long id;
private int amount;
private String status; // "DRAFT", "SUBMITTED", "PAID", "CANCELLED"
private Long customerId;
private LocalDateTime createdAt;
private LocalDateTime paidAt;
// getter/setter๋ง ์๋ฉ
public Long getId() { return id; }
public int getAmount() { return amount; }
public void setAmount(int amount) { this.amount = amount; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
// ... 12๊ฐ getter, 11๊ฐ setter
}
// ๋ชจ๋ ๋น์ฆ๋์ค ๋ก์ง์ด Service์ ํญ์ฆ
@Service
public class FareService {
public void changeAmount(Fare fare, int newAmount) {
if (fare.getStatus().equals("PAID")) {
throw new IllegalStateException("๊ฒฐ์ ๋ ์ด์์ ๋ณ๊ฒฝ ๋ถ๊ฐ");
}
if (newAmount < 0) {
throw new IllegalArgumentException("์์ ๋ถ๊ฐ");
}
if (newAmount > 100_000_000) {
throw new IllegalArgumentException("์ต๋ ๊ธ์ก ์ด๊ณผ");
}
fare.setAmount(newAmount);
}
public void submit(Fare fare) {
if (!fare.getStatus().equals("DRAFT")) {
throw new IllegalStateException("DRAFT ์ํ๋ง ์ ์ถ ๊ฐ๋ฅ");
}
if (fare.getAmount() <= 0) {
throw new IllegalStateException("๊ธ์ก์ด 0 ์ดํ์ธ ์ด์์ ์ ์ถ ๋ถ๊ฐ");
}
fare.setStatus("SUBMITTED");
}
public void pay(Fare fare) {
if (!fare.getStatus().equals("SUBMITTED")) {
throw new IllegalStateException("SUBMITTED ์ํ๋ง ๊ฒฐ์ ๊ฐ๋ฅ");
}
fare.setStatus("PAID");
fare.setPaidAt(LocalDateTime.now());
}
public void cancel(Fare fare) {
if (fare.getStatus().equals("PAID")) {
throw new IllegalStateException("๊ฒฐ์ ๋ ์ด์์ ์ทจ์ ๋ถ๊ฐ");
}
if (fare.getStatus().equals("CANCELLED")) {
throw new IllegalStateException("์ด๋ฏธ ์ทจ์๋จ");
}
fare.setStatus("CANCELLED");
}
public boolean isModifiable(Fare fare) {
return fare.getStatus().equals("DRAFT");
}
public boolean isCancellable(Fare fare) {
return !fare.getStatus().equals("PAID") &&
!fare.getStatus().equals("CANCELLED");
}
// ... ์ด์ ๊ด๋ จ ๋ชจ๋ ๋ก์ง์ด ์ฌ๊ธฐ์ โ
}
๋ฌธ์ ์ ์ด ํ๋์ ๋ณด์ด์๋์?
Fare fare = fareRepository.findById(1L);
// ์ด๋์๋ ์ง์ setter ํธ์ถ ๊ฐ๋ฅ โ
fare.setAmount(-99999); // ์์ ๊ฐ๋ฅ!
fare.setStatus("๊ดด์ํ๊ฐ"); // ์๋ชป๋ ์ํ!
fare.setStatus("PAID"); // ๊ฒฐ์ ์ ํ๋๋ฐ ๊ฒฐ์ ๋จ์ผ๋ก!
// ๊ฒ์ฆ์ FareService ํธ์ถํ ๋๋ง ์ผ์ด๋จ
// โ setter๋ฅผ ์ง์ ๋ถ๋ฅด๋ฉด ๊ฒ์ฆ ์ฐํ
โ ๊ฐ์ฒด๊ฐ ์๊ธฐ ๋ฌด๊ฒฐ์ฑ์ ๋ณดํธ ๋ชปํจ. ๋๊ตฌ๋ ๋ง๊ฐ๋จ๋ฆผ.
public class FareService {
public void changeAmount(Fare fare, int newAmount) {
if (newAmount < 0) throw new ...; // ๊ฒ์ฆ 1
// ...
}
}
public class FareImportService {
public void importFare(int amount, ...) {
if (amount < 0) throw new ...; // ๊ฐ์ ๊ฒ์ฆ ๋ ์์ฑ
Fare fare = new Fare();
fare.setAmount(amount);
}
}
public class FareApiController {
public void create(@RequestBody FareDto dto) {
if (dto.getAmount() < 0) throw new ...; // ๋๋ ์์ฑ
}
}
โ ๋์ผ ๊ท์น์ด ์ฌ๋ฌ ๊ณณ์ ๋ถ์ฐ โ ํ ๊ณณ๋ง ์์ ํ๋ฉด ๋ค๋ฅธ ๊ณณ์ ๋ฒ๊ทธ.
@Service
public class FareService {
// ์ด์ ์์ฑ ๋ก์ง
// ์ด์ ์์ ๋ก์ง
// ์ด์ ๊ฒฐ์ ๋ก์ง
// ์ด์ ์ทจ์ ๋ก์ง
// ์ด์ ๊ฒ์ฆ ๋ก์ง
// ํ ์ธ ๊ณ์ฐ ๋ก์ง
// ์ํ ์ ์ด ๋ก์ง
// ... 50๊ฐ ๋ฉ์๋, 2000์ค โ
}
โ ๋จ์ผ ์ฑ ์ ์์น(SRP) ์๋ฐ. ํ ์คํธ ์ด๋ ค์. ๋ณ๊ฒฝ ์ํฅ ํผ.
"์ด์์ ๊ฒฐ์ ์ํ๋ก ๋ฐ๊พธ๋ ๊ท์น์ด ๋ญ์์?"
โ
"FareService์ ์์ด์"
โ
"FareService์ ๋ฉ์๋๊ฐ 50๊ฐ์ธ๋ฐ ์ด๋์..."
โ
"์... pay() ๋ฉ์๋ ๊ฐ์์. ๊ทผ๋ฐ PaymentService์๋ ์๊ณ ,
BatchService์๋ ๊ฒฐ์ ์ฒ๋ฆฌ ๋ก์ง์ด ์์ด์ ์ ํํ ๋ต์ ๋ชจ๋ฅด๊ฒ ๋ค์"
โ ๊ท์น์ด ํฉ์ด์ ธ์ ์ถ์ ๋ถ๊ฐ.
@Test
void ์ด์_๊ฒฐ์ _ํ
์คํธ() {
Fare fare = new Fare();
fare.setStatus("SUBMITTED");
fare.setAmount(50000);
// Service๋ฅผ mockํด์ผ ํจ
FareService service = new FareService(mockRepo, mockEventPublisher, mockAuditLogger, ...);
service.pay(fare);
assertThat(fare.getStatus()).isEqualTo("PAID");
}
vs
@Test
void ์ด์_๊ฒฐ์ _ํ
์คํธ() {
Fare fare = Fare.draft(50000);
fare.submit();
fare.pay(); // ๋จ์!
assertThat(fare.isPaid()).isTrue();
}
โ ๊ฐ์ฒด ์์ฒด๋ก ํ ์คํธ ๋ถ๊ฐ, Service ์์กด์ฑ ์ฃผ์ ์ง์ฅ.
์ ์
: "Fare๋ฅผ ์ด๋ป๊ฒ ์ฌ์ฉํ๋์?"
์ ์: "์, FareService๋ฅผ ๋ด์ผ ํด์"
์ ์
: "FareService์ 50๊ฐ ๋ฉ์๋๊ฐ ์๋๋ฐ..."
์ ์: "๊ทธ๋ฆฌ๊ณ PaymentService, FareImportService, FareBatchService๋ ๋ด์ผ ํด์"
์ ์
: "............"
vs
์ ์
: "Fare๋ฅผ ์ด๋ป๊ฒ ์ฌ์ฉํ๋์?"
์ ์: "Fare ํด๋์ค ์์ฒด๋ฅผ ๋ณด์ธ์. ๊ฐ๋ฅํ ํ๋์ด ๋ค ๊ฑฐ๊ธฐ ์์ด์"
์ ์
: "์, draft(), submit(), pay(), cancel() ... ๋ช
ํํ๋ค์!"
๊ฐ์ ILIC ์ด์ ์์คํ ์ ์ ์ ๋ชจ๋ธ๋ก:
@Entity
public class Fare {
@Id
private Long id;
private int amount;
@Enumerated(EnumType.STRING)
private FareStatus status;
private Long customerId;
private LocalDateTime createdAt;
private LocalDateTime paidAt;
// ์ ์ ํฉํ ๋ฆฌ ๋ฉ์๋ โ "์ด๋ป๊ฒ ๋ง๋๋๊ฐ" ๋ช
ํ
public static Fare draft(Long customerId, int amount) {
validateAmount(amount);
Fare fare = new Fare();
fare.customerId = customerId;
fare.amount = amount;
fare.status = FareStatus.DRAFT;
fare.createdAt = LocalDateTime.now();
return fare;
}
// ํ๋ 1: ๊ธ์ก ๋ณ๊ฒฝ (์๊ธฐ ๊ฒ์ฆ)
public void changeAmount(int newAmount) {
if (status != FareStatus.DRAFT) {
throw new IllegalStateException("DRAFT ์ํ์์๋ง ๊ธ์ก ๋ณ๊ฒฝ ๊ฐ๋ฅ");
}
validateAmount(newAmount);
this.amount = newAmount;
}
// ํ๋ 2: ์ ์ถ
public void submit() {
if (status != FareStatus.DRAFT) {
throw new IllegalStateException("DRAFT ์ํ์์๋ง ์ ์ถ ๊ฐ๋ฅ");
}
if (amount <= 0) {
throw new IllegalStateException("๊ธ์ก์ด 0 ์ดํ์ธ ์ด์์ ์ ์ถ ๋ถ๊ฐ");
}
this.status = FareStatus.SUBMITTED;
}
// ํ๋ 3: ๊ฒฐ์
public void pay() {
if (status != FareStatus.SUBMITTED) {
throw new IllegalStateException("SUBMITTED ์ํ์์๋ง ๊ฒฐ์ ๊ฐ๋ฅ");
}
this.status = FareStatus.PAID;
this.paidAt = LocalDateTime.now();
}
// ํ๋ 4: ์ทจ์
public void cancel() {
if (status == FareStatus.PAID) {
throw new IllegalStateException("๊ฒฐ์ ๋ ์ด์์ ์ทจ์ ๋ถ๊ฐ");
}
if (status == FareStatus.CANCELLED) {
throw new IllegalStateException("์ด๋ฏธ ์ทจ์๋จ");
}
this.status = FareStatus.CANCELLED;
}
// ์๊ธฐ ์ํ ์ง๋ฌธ
public boolean isModifiable() {
return status == FareStatus.DRAFT;
}
public boolean isCancellable() {
return status != FareStatus.PAID && status != FareStatus.CANCELLED;
}
public boolean isPaid() {
return status == FareStatus.PAID;
}
// private ๊ฒ์ฆ
private static void validateAmount(int amount) {
if (amount < 0) throw new IllegalArgumentException("์์ ๋ถ๊ฐ");
if (amount > 100_000_000) throw new IllegalArgumentException("์ต๋ ๊ธ์ก ์ด๊ณผ");
}
// setter๋ ์์ โ ์๋์ ์ผ๋ก
// getter๋ ํ์ํ ๊ฒ๋ง
public Long getId() { return id; }
public int getAmount() { return amount; }
public FareStatus getStatus() { return status; }
}
// Service๋ ํ๋ฆ๋ง ์กฐ์จ โ ์์์ง
@Service
@RequiredArgsConstructor
public class FareService {
private final FareRepository fareRepository;
private final NotificationService notificationService;
public Long createFare(Long customerId, int amount) {
Fare fare = Fare.draft(customerId, amount); // โ ๊ฐ์ฒด์ ์์
fareRepository.save(fare);
return fare.getId();
}
@Transactional
public void pay(Long fareId) {
Fare fare = fareRepository.findById(fareId).orElseThrow();
fare.pay(); // โ ๊ฐ์ฒด์ ์์
notificationService.sendPaymentConfirmation(fare);
}
}
ํจ๊ณผ:
์ด๊ฒ ์ํฐํจํด์ธ ๊ฑธ ์๋ฉด์๋ ์ ๋ง์ ํ๋ก์ ํธ๊ฐ ๋นํ ๋ชจ๋ธ์ผ๊น์?
์ ํ์ ์ธ Spring Boot ํํ ๋ฆฌ์ผ:
[Controller] โ [Service] โ [Repository] โ [Entity]
โ
๋ชจ๋ ๋ก์ง์ ์ฌ๊ธฐ์
์ด ๊ตฌ์กฐ๊ฐ "Entity๋ ๋ฐ์ดํฐ, Service๋ ๋ก์ง" ์ด๋ผ๋ ์๋ชป๋ ์ธ์์ ์ฌ์.
@Entity
@Getter
@Setter // โ ์ด๊ฑฐ ํ ์ค์ด ๋นํ ๋ชจ๋ธ ์ ๋
@NoArgsConstructor
@AllArgsConstructor
public class Fare {
private int amount;
private String status;
// ...
}
Lombok์ @Setter ๊ฐ ์์ผ๋ฉด "setter๋ก ๋ชจ๋ ํ๋ ๋ณ๊ฒฝ ๊ฐ๋ฅ" ์ด ๊ธฐ๋ณธ ๊ฐ์ ์ด ๋จ.
โ @Setter ๋ฅผ ๋ฌด์กฐ๊ฑด ๋ถ์ด๋ ์ต๊ด์ด ๋นํ ๋ชจ๋ธ์ ๋ง๋๋ ๊ฐ์ฅ ํฐ ์์ธ โ ๏ธ
์์ ํ๋ก์ ํธ, MVP ๋จ๊ณ์์๋:
๋ฌธ์ ๋ ํ๋ก์ ํธ๊ฐ ์ปค์ง๋ฉด์ ๋น์ฉ์ด ํญ๋ฐํ๋ค๋ ๊ฒ.
๋ํ/ํ์์์ ์๋ฐ๋ฅผ ๋ฐฐ์ธ ๋:
โ "๊ฐ์ฒด์งํฅ" ์ด๋ผ๋ ์ด๋ฆ๋ง ์๊ณ , ๋ณธ์ง์ ๋ชจ๋ฆ.
๋ณธ์ธ ILIC ์ฝ๋๋ฅผ ์ ๊ฒํด๋ณด์ธ์:
@Setter ๋ฅผ ๋ชจ๋ Entity์ ๋ถ์ธ๋ค3๊ฐ ์ด์ ํด๋น = ๋นํ ๋ชจ๋ธ ๊ฐ๋ฅ์ฑ ๋์.
pay(), cancel() ๋ฑ)@Setter ๋์ ์๋๊ฐ ๋ช
ํํ ๋ฉ์๋๋ฅผ ๋ง๋ ๋ค๊ท ํ ์กํ ์๊ฐ๋ ํ์ํฉ๋๋ค.
CRUD๊ฐ ์ ๋ถ์ธ ๋จ์ ์์คํ
DTO, VO
JPA Projection ๊ฒฐ๊ณผ
๋ณต์กํ ๋น์ฆ๋์ค ๊ท์น์ด ์๋ ๋๋ฉ์ธ
๋, ๊ฒฐ์ , ๊ณ์ฝ ๋ฑ ๋ฌด๊ฒฐ์ฑ์ด ์ค์ํ ๋๋ฉ์ธ
์ฅ๊ธฐ ์ ์ง๋ณด์ ํ๋ก์ ํธ
โ ILIC๋ ๋ช ๋ฐฑํ Rich Model์ด ํ์ํ ๋๋ฉ์ธ.
๋นํ ๋๋ฉ์ธ ๋ชจ๋ธ์ "๊ฐ์ฒด"๋ผ๋ ์ด๋ฆ๋ง ๋น๋ ธ์ ๋ฟ ์ฌ๊ณ ๋ฐฉ์์ ์ ์ฐจ์งํฅ์ด๋ค.
๋ฐ์ดํฐ(ํ๋)์ ํ๋(๋ฉ์๋)์ด ํจ๊ป ์์ด์ผ ์ง์ง ๊ฐ์ฒด์ด๊ณ , Setter ๋จ๋ฐ๊ณผ Service ๋น๋ํ๋ ๋นํ์ ๊ฐ์ฅ ํํ ์ฆ์์ด๋ค. ILIC์ฒ๋ผ ๋น์ฆ๋์ค ๊ท์น์ด ๋ณต์กํ ๋๋ฉ์ธ์ ๋ฐ๋์ Rich Domain Model๋ก ๊ฐ์ผ ์ฅ๊ธฐ ์ ์ง๋ณด์๊ฐ ๊ฐ๋ฅํ๋ค.
๋ฐ์น์ ๋์ด ์ด ์ฃผ์ ๋ฅผ ๋ ๊น์ด ํ๊ณ ์ถ๋ค๋ฉด:
์์์ "์ฝ๋ ์ฌ์ฌ์ฉ ๋๊ตฌ" ๊ฐ ์๋๋ผ "๊ด๊ณ๋ฅผ ํํํ๋ ๋๊ตฌ" ์ด๋ค.
์์์ ์๋ชป ์ฐ๋ฉด ์ ์ง๋ณด์ ์ง์ฅ ์ด ๋ฉ๋๋ค. ๊ทธ๋์ ํ๋ ์๋ฐ์ ๊ฒฉ์ธ์:
"์์๋ณด๋ค ํฉ์ฑ์ ์ ํธํ๋ผ" (Favor Composition over Inheritance)
โ Joshua Bloch, Effective Java
์์(extends)์ ๋จ์ํ ๋ฉ์๋๋ฅผ ๊ฐ์ ธ์ค๋ ๊ฒ ์๋๋๋ค. ์ ์ธ์
๋๋ค:
public class Stack extends ArrayList { ... }
โ ์ด ํ ์ค์ด ์๋ฐ์๊ฒ "Stack์ ArrayList์ ํ ์ข ๋ฅ๋ค" ๋ผ๊ณ ์ ์ธํ๋ ๊ฒ.
์๋ฐ์ ์ฌ์ฉํ๋ ๋ชจ๋ ์ฝ๋๊ฐ ์ด ์ ์ธ์ ๋ฏฟ๊ณ ํ๋ํจ:
โ ์ด๊ฒ ๊นจ์ง๋ฉด ์์คํ ์ ์ฒด๊ฐ ํ๋ค๋ฆผ.
์ด ๋น์ ๋ก ์์๊ณผ ํฉ์ฑ์ ์ฐจ์ด๋ฅผ ๋ช ํํ ๋ณด๊ฒ ์ต๋๋ค.
public class Doctor extends Human { ... }
โ "์์ฌ๋ ์ฌ๋์ ํ ์ข ๋ฅ๋ค" ๋ผ๋ ์ ์ธ
์ด๊ฑด ์์ฐ์ค๋ฝ์ต๋๋ค. ์์ฌ๋ ์ฌ๋์ ์ผ์ข ์ด๋๊น. is-a ๊ด๊ณ ๊ฐ ์ฑ๋ฆฝ.
public class Doctor extends Job { ... }
โ "์์ฌ๋ ์ง์ ์ ํ ์ข ๋ฅ๋ค" ๋ผ๋ ์ ์ธ
์... ์ผ๋จ ๋ง์ ๋ฉ๋๋ค. ์์ฌ๋ ์ง์ ์ ์ผ์ข ์ด๊ธด ํ๋๊น.
๊ทธ๋ฐ๋ฐ ๋ฌธ์ ๊ฐ ์์ด์:
์์์ ๊ณ ์ ๋ ๊ด๊ณ ์
๋๋ค. ํ ๋ฒ Doctor extends Job ์ผ๋ก ๋ง๋ค๋ฉด:
โ "is-a" ์ฒ๋ผ ๋ณด์ฌ๋ ์ฌ์ค "has-a" ์ธ ๊ฒฝ์ฐ๊ฐ ๋ง์.
public class Person {
private Job currentJob; // "์ง์
์ ๊ฐ์ง" (has-a)
private List<Job> sideJobs; // "์ฌ๋ฌ ์ง์
๊ฐ์ง ์ ์์"
public void changeJob(Job newJob) { // ์ง์
๋ฐ๊ฟ ์ ์์
this.currentJob = newJob;
}
public void retire() { // ์ง์
์์ ์๋ ์์
this.currentJob = null;
}
}
โ ์ ์ฐํจ. ์ฌ๋์ด ์ง์ ์ ๊ฐ์ง๊ณ , ๋ฐ๊พธ๊ณ , ์๊ณ , ์ถ๊ฐํ๊ณ โ ์์ .
Stack extends ArrayList ๊ฐ ์ ๋์ฐํ์ง ์ค์ ์ฝ๋ ์๋๋ฆฌ์ค ๋ก ๋ณด๊ฒ ์ต๋๋ค.
๋ด๊ฐ Stack์ ๋ง๋ ์ด์ :
push() ์ pop() ๋ง ๋
ธ์ถํ๊ณ ์ถ์public class Stack<E> extends ArrayList<E> {
public void push(E item) {
add(item);
}
public E pop() {
return remove(size() - 1);
}
}
์ด์ ์ฌ์ฉ์๊ฐ ์ด๋ ๊ฒ ํ ์ ์์ด์:
Stack<String> stack = new Stack<>();
stack.push("A");
stack.push("B");
stack.push("C");
// LIFO๋๋ก ์ฌ์ฉ โ OK
String top = stack.pop(); // "C"
// ๊ทธ๋ฐ๋ฐ... ArrayList์ ๋ฉ์๋๊ฐ ๋ค ๋
ธ์ถ๋ผ ์์ โ
stack.add(0, "X"); // ๋งจ ์์ ์ถ๊ฐ? Stack์ด ์๋๋ฐ!
stack.remove(1); // ์ค๊ฐ ์ ๊ฑฐ? Stack์ด ์๋๋ฐ!
stack.set(0, "Y"); // ์์ ์์น ์์ ? Stack์ด ์๋๋ฐ!
stack.subList(0, 2); // ๋ถ๋ถ ๋ฆฌ์คํธ? Stack์ด ์๋๋ฐ!
stack.sort(...); // ์ ๋ ฌ? Stack์ด ์๋๋ฐ!
๋ฌธ์ 1: Stack์ ์๋๊ฐ ๋ฌด๋์ง. "LIFO๋ง ํ์ฉ" ์ด๋ผ๋ ์ฝ์์ด ๊นจ์ง.
๋ฌธ์ 2: ์ฌ์ฉ์๊ฐ ์๋ชป ์ฐ๋ฉด ๋๋ฒ๊น ์ง์ฅ.
stack.add(0, "X"); // ์ค์๋ก 0๋ฒ์ ์ถ๊ฐ
String top = stack.pop(); // "C" (X๊ฐ ์๋๋ฐ?)
// โ ์ฌ์ฉ์: "์ด? ๋ด๊ฐ ๋ง์ง๋ง์ ์ถ๊ฐํ ๊ฑด X์ธ๋ฐ ์ C๊ฐ ๋์ค์ง?"
// โ ํ์ฐธ ๋๋ฒ๊น
ํ๋ค add(0, ...) ๋ฐ๊ฒฌ
๋ฌธ์ 3: API ์ฌ์ฉ์์๊ฒ "Stack์ ์ฌ์ค ArrayList์ ๋๋ค" ๋ผ๋ ๋ฉ์์ง๋ฅผ ์ค.
public class Stack<E> {
private final List<E> items = new ArrayList<>(); // ๊ฐ์ง (has-a)
public void push(E item) {
items.add(item);
}
public E pop() {
if (items.isEmpty()) {
throw new IllegalStateException("Stack is empty");
}
return items.remove(items.size() - 1);
}
public E peek() {
if (items.isEmpty()) {
throw new IllegalStateException("Stack is empty");
}
return items.get(items.size() - 1);
}
public int size() {
return items.size();
}
public boolean isEmpty() {
return items.isEmpty();
}
}
์ด์ ์ฌ์ฉ์๊ฐ ํ ์ ์๋ ๊ฑด:
Stack<String> stack = new Stack<>();
stack.push("A");
stack.push("B");
stack.push("C");
stack.pop(); // OK
stack.peek(); // OK
stack.size(); // OK
stack.isEmpty(); // OK
// ์ด๊ฑด ์ ๋จ โ
stack.add(0, "X"); // ์ปดํ์ผ ์๋ฌ! Stack์ add ์์
stack.remove(1); // ์ปดํ์ผ ์๋ฌ!
stack.sort(...); // ์ปดํ์ผ ์๋ฌ!
โ Stack์ ์ฝ์์ด ์ง์ผ์ง. ์ฌ์ฉ์๊ฐ ์๋ชป ์ธ ์ ์์.
ํฅ๋ฏธ๋ก์ด ์ฌ์ค: ์๋ฐ ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ java.util.Stack ๋ ๊ฐ์ ์ค์๋ฅผ ํ์ต๋๋ค.
// JDK์ Stack โ ์ํฐํจํด ์ฌ๋ก โ ๏ธ
public class Stack<E> extends Vector<E> {
public E push(E item) { addElement(item); return item; }
public E pop() { ... }
// ...
}
โ Vector๋ฅผ ์์๋ฐ์๊ธฐ ๋๋ฌธ์ Vector์ ๋ชจ๋ ๋ฉ์๋ ๋ ธ์ถ๋จ.
์๋ฐ ๊ณต์ ๋ฌธ์์์๋ ์ธ์ :
"A more complete and consistent set of LIFO stack operations is provided by the Deque interface and its implementations, which should be used in preference to this class."
(Deque ์ธํฐํ์ด์ค๊ฐ ๋ ์์ ํ๊ณ ์ผ๊ด๋ LIFO ์คํ ์ฐ์ฐ์ ์ ๊ณตํ๋ฏ๋ก, Stack ํด๋์ค ๋์ ์ฌ์ฉํด์ผ ํ๋ค.)
โ ์๋ฐ ์์ ๋ "์ด๊ฑด ์๋ชป ๋ง๋ค์์ผ๋ ์ฐ์ง ๋ง์ธ์" ๋ผ๊ณ ์ธ์ .
ํ๋ ์๋ฐ์ ๊ถ์ฅ ๋ฐฉ์:
Deque<String> stack = new ArrayDeque<>();
stack.push("A");
stack.pop();
๋ถ๋ชจ ํด๋์ค์ ๋ด๋ถ ๊ตฌํ์ด ๋ฐ๋๋ฉด ์์์ด ๊นจ์ง.
์์:
public class CountingArrayList<E> extends ArrayList<E> {
private int addCount = 0;
@Override
public boolean add(E e) {
addCount++;
return super.add(e);
}
@Override
public boolean addAll(Collection<? extends E> c) {
addCount += c.size();
return super.addAll(c); // ๋ด๋ถ์ ์ผ๋ก add()๋ฅผ ํธ์ถํจ!
}
public int getAddCount() { return addCount; }
}
ํ ์คํธ:
CountingArrayList<String> list = new CountingArrayList<>();
list.addAll(List.of("A", "B", "C"));
System.out.println(list.getAddCount()); // 6 โ (3์ด์ด์ผ ํ๋๋ฐ)
์?:
addAll() ์ด addCount += 3 ํadd() ๋ฅผ 3๋ฒ ํธ์ถ โ addCount ๊ฐ ๋ 3 ์ฆ๊ฐโ ๋ถ๋ชจ(ArrayList)์ ๋ด๋ถ ๊ตฌํ์ ๋ชจ๋ฅด๋ฉด ์์์ด ์ ํํ๊ฒ ๋์ ์ ํจ.
โ ์๋ฐ ๋ค์ ๋ฒ์ ์์ ArrayList.addAll() ์ ๊ตฌํ์ด ๋ฐ๋๋ฉด? ์์ ํด๋์ค๊ฐ ๊ฐ์๊ธฐ ๊นจ์ง.
์ด๋ฅผ "์ทจ์ฝํ ๊ธฐ๋ฐ ํด๋์ค ๋ฌธ์ (Fragile Base Class Problem)" ๋ผ๊ณ ๋ถ๋ฆ ๋๋ค.
์๋ฐ๋ ๋ค์ค ์์์ด ๋ถ๊ฐ๋ฅ ํฉ๋๋ค.
public class A { ... }
public class B { ... }
public class C extends A, B { ... } // โ ์ปดํ์ผ ์๋ฌ
์ ๋ฌธ์ ์ธ๊ฐ:
// ํฉ์ฑ์ผ๋ก๋ ์์ ๋ก์
public class C {
private final A a;
private final B b;
// ๋ ๊ฐ์ง ๋ฅ๋ ฅ ๋ชจ๋ ํ์ฉ ๊ฐ๋ฅ
}
public class Customer extends NormalCustomer { ... }
// โ ์ด Customer๋ ์์ํ NormalCustomer
// โ ๋ฐํ์์ VipCustomer๋ก ๋ฐ๊ฟ ์ ์์
ํฉ์ฑ์ด๋ผ๋ฉด ๊ฐ๋ฅ:
public class Customer {
private CustomerLevel level; // ๋ฐํ์์ ๋ฐ๊ฟ ์ ์์
public void upgradeToVip() {
this.level = new VipLevel(); // ๋ฑ๊ธ ๋ณ๊ฒฝ
}
}
โ ํ์ค์ ๋ณํ (์ฌ์ฉ์๊ฐ ์ผ๋ฐ โ VIP ์น๊ธ) ๋ฅผ ํํํ๋ ค๋ฉด ํฉ์ฑ์ด ํ์.
์ฒ์์๋ ๋จ์ํ๋ ๊ณ์ธต์ด ์ ์ ๊น์ด์ง๋๋ค.
Animal
โโ Mammal
โโ Carnivore
โโ Felidae
โโ Cat
โโ DomesticCat
โโ KoreanShortHair
๋ฌธ์ :
์์์ด ๋ฌด์กฐ๊ฑด ๋์ ๊ฑด ์๋๋๋ค. ๋ช ํํ ๊ธฐ์ค ์ด ์์ต๋๋ค.
๋ค์ ๋ชจ๋ ์กฐ๊ฑด ์ ๋ง์กฑํ ๋๋ง ์์:
๋ช ํํ is-a ๊ด๊ณ์ธ๊ฐ?
๋ถ๋ชจ๊ฐ ๋ณํ์ง ์์ ๊ฒ์ธ๊ฐ?
์์์ด ๋ถ๋ชจ์ ๋ชจ๋ ๋ฉ์๋๋ฅผ ์๋ฏธ ์๊ฒ ์ฌ์ฉํ ์ ์๋?
add(int, E) ๋ฅผ ์๋ฏธ ์๊ฒ ์ฌ์ฉ X โ ์์ X๋ถ๋ชจ-์์ ๊ด๊ณ๊ฐ ์๊ตฌ์ ์ธ๊ฐ?
"๊ณตํต ์ฝ๋ ์ฌ์ฌ์ฉ" ์ด ๋ชฉ์ ์ธ๊ฐ?
public abstract class Report {
public final void generate() { // ํ๋ฆ ๊ณ ์
loadData();
format();
save();
}
protected abstract void loadData();
protected abstract void format();
protected abstract void save();
}
public class FareReport extends Report { ... }
public class CustomerReport extends Report { ... }
โ "Report์ ํ ์ข ๋ฅ๋ค" + ํ๋ฆ ๊ณตํตํ โ ์์ OK.
public abstract class Customer {
// ๊ณตํต ์์ฑ๊ณผ ํ๋
}
public class IndividualCustomer extends Customer { ... }
public class CorporateCustomer extends Customer { ... }
โ ์ง์ง is-a ๊ด๊ณ โ ์์ OK.
public class MyController extends AbstractController { ... }
public class CustomFilter extends OncePerRequestFilter { ... }
โ Spring์ด ์๋ํ ํ์ฅ ๋ฐฉ์ โ ์์ OK.
์ด๋ฏธ ๋ง๋ ์์ ์ฝ๋๋ฅผ ํฉ์ฑ์ผ๋ก ๋ฆฌํฉํ ๋งํ๋ ํจํด.
public class CountingArrayList<E> extends ArrayList<E> {
private int addCount = 0;
@Override
public boolean add(E e) {
addCount++;
return super.add(e);
}
@Override
public boolean addAll(Collection<? extends E> c) {
addCount += c.size();
return super.addAll(c); // ๋ถ๋ชจ ๋ด๋ถ ๊ตฌํ ์์กด
}
public int getAddCount() { return addCount; }
}
public class CountingList<E> {
private final List<E> delegate; // ๊ฐ์ง (has-a)
private int addCount = 0;
public CountingList(List<E> delegate) {
this.delegate = delegate;
}
public boolean add(E e) {
addCount++;
return delegate.add(e);
}
public boolean addAll(Collection<? extends E> c) {
addCount += c.size();
return delegate.addAll(c); // ๋ถ๋ชจ ๋ด๋ถ ๊ตฌํ ๋ฌด๊ด
}
public int getAddCount() { return addCount; }
// ํ์ํ List ๋ฉ์๋๋ง ์์
public E get(int index) { return delegate.get(index); }
public int size() { return delegate.size(); }
// ...
}
ํจ๊ณผ:
CountingList<String> list = new CountingList<>(new ArrayList<>());
// ๋๋
CountingList<String> list = new CountingList<>(new LinkedList<>()); // ๋ค๋ฅธ ๊ตฌํ๋ OK
ILIC ์ด์์ Customer ๋ฑ๊ธ๋ณ ๊ณ์ฐ์ ์์์ผ๋ก:
public class Customer {
protected String name;
protected String email;
public int calculateDiscount(int amount) { return 0; }
}
public class VipCustomer extends Customer {
@Override
public int calculateDiscount(int amount) { return amount * 20 / 100; }
}
public class PartnerCustomer extends Customer {
@Override
public int calculateDiscount(int amount) { return amount * 30 / 100; }
}
๋ฌธ์ :
// ๋ฑ๊ธ์ ์ ์ฑ
๊ฐ์ฒด๋ก ๋ถ๋ฆฌ
public interface DiscountPolicy {
int calculate(int amount);
}
public class NoDiscountPolicy implements DiscountPolicy {
public int calculate(int amount) { return 0; }
}
public class VipDiscountPolicy implements DiscountPolicy {
public int calculate(int amount) { return amount * 20 / 100; }
}
public class PartnerDiscountPolicy implements DiscountPolicy {
public int calculate(int amount) { return amount * 30 / 100; }
}
// Customer๋ ์ ์ฑ
์ "๊ฐ์ง" (has-a)
public class Customer {
private String name;
private String email;
private DiscountPolicy discountPolicy; // ๊ฐ์ง
public int calculateDiscount(int amount) {
return discountPolicy.calculate(amount);
}
public void changeDiscountPolicy(DiscountPolicy newPolicy) { // ๋ฐํ์ ๋ณ๊ฒฝ ๊ฐ๋ฅ
this.discountPolicy = newPolicy;
}
}
ํจ๊ณผ:
customer.changeDiscountPolicy(new VipDiscountPolicy()) โ ๊ฐ์ฒด ๊ทธ๋๋กDiscountPolicy ๊ตฌํ์ฒด 1๊ฐ๋ง ์ถ๊ฐโ ์ด๊ฒ 17์ฃผ์ฐจ Spring Strategy Pattern๊ณผ ์ง๊ฒฐ๋๋ ์ฌ๊ณ ๋ฐฉ์.
[์ฝ๋๋ฅผ ์ฌ์ฌ์ฉํ๊ณ ์ถ๋ค]
โ
"์ด๊ฑด ๋ช
ํํ ๋ถ๋ชจ์ ํ ์ข
๋ฅ์ธ๊ฐ?" (is-a)
โ
YES โโ ์์ ๊ฒํ
| โ
| "๋ถ๋ชจ๊ฐ ์์ ์ ์ด๊ณ ๋ณํ์ง ์๋๊ฐ?"
| โ
| YES โโ ์์ OK
| NO โโ ํฉ์ฑ์ผ๋ก
|
NO โโโ ํฉ์ฑ (has-a)
ํ ์ค ๊ฒฐ์ :
ํ์ ์ด ์์ผ๋ฉด ํฉ์ฑ. ์๋ชป๋ ํฉ์ฑ์ ์์ ์ด ์ฝ์ง๋ง, ์๋ชป๋ ์์์ ์์ ์ด ๋งค์ฐ ์ด๋ ต๋ค.
"์์์ ์บก์ํ๋ฅผ ์๋ฐํ๋ค. ์์ ๋์ ํฉ์ฑ์ ์ฌ์ฉํ๋ผ."
"ํด๋์ค ์์๋ณด๋ค ๊ฐ์ฒด ํฉ์ฑ์ ์ ํธํ๋ผ."
"์์์ ๊ฐ์ฅ ๊ฐํ ๊ฒฐํฉ์ด๋ค. ๊ฐ๋ฅํ ์ฝํ๊ฒ ๊ฒฐํฉํ๋ผ."
โ ์ํํธ์จ์ด ๊ฑฐ์ฅ๋ค์ ์ผ๊ด๋ ๋ฉ์์ง: ์์์ ์กฐ์ฌํ๋ผ.
์์์ "์ฝ๋ ์ฌ์ฌ์ฉ ๋๊ตฌ" ๊ฐ ์๋๋ผ "is-a ๊ด๊ณ ์ ์ธ" ์ด๋ค.
"๊ณตํต ์ฝ๋ ์ฐ๊ณ ์ถ์ด์" ๋ผ๋ ๋๊ธฐ๋ก ์์์ ์ฐ๋ฉด ์บก์ํ ํ๊ดด, ์ทจ์ฝํ ๊ธฐ๋ฐ ํด๋์ค, ๋ณ๊ฒฝ ๋ถ๊ฐ, ๋จ์ผ ์์ ์ ์ฝ ๋ฑ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค. ํ์ ์ด ์์ผ๋ฉด ํฉ์ฑ โ ์ด๊ฒ ํ๋ ์๋ฐ์ ํฉ๊ธ๋ฅ ์ด๋ค.
"ํ ํด๋์ค๊ฐ ๋๋ฌด ๋ง์ ์ฑ ์์ ๊ฐ์ง"
public class FareManager { // โ ๋ชจ๋ ๊ฑธ ๋ค ํจ
public void createFare(...) { }
public void deleteFare(...) { }
public void calculateDiscount(...) { }
public void sendNotification(...) { }
public void generateReport(...) { }
public void backupData(...) { }
public void validateUser(...) { }
// ... 50๊ฐ ๋ฉ์๋
}
์ ๋ฌธ์ :
ํด๊ฒฐ:
FareService, NotificationService, ReportService...public class Fare {
public int amount; // โ ์ธ๋ถ์์ ์ง์ ์์ ๊ฐ๋ฅ
}
์ ์ํ:
ํด๊ฒฐ: private + ๋ฉ์๋๋ฅผ ํตํ ์ ๊ทผ
๊ฐ์ฒด์งํฅ์ด ํญ์ ์ข์ ๊ฑด ์๋๋ค โ ๏ธ
๊ฐ์ฒด์งํฅ์ด ์ ํฉํ์ง ์์ ๊ฒฝ์ฐ:
ํ๋ ํธ๋ ๋:
์ด Unit์ด ๋ค๋ฅธ ๊ฐ๋ ๊ณผ ์ด๋ป๊ฒ ์ฐ๊ฒฐ๋๋์ง ๋ณธ๋ค.
[Unit 1.1: ์ ์ฐจ์งํฅ vs ๊ฐ์ฒด์งํฅ] โ ์ง๊ธ ์ฌ๊ธฐ
โ
[Unit 1.2: ํด๋์ค์ ๊ฐ์ฒด์ ๋ณธ์ง] โ ๋ค์ ํ์ต
โ
[Phase 2: ํด๋์ค ๋ฌธ๋ฒ๊ณผ ๊ฐ์ฒด๊ฐ ๊ด๊ณ]
โ
[Phase 3: SOLID โ OOP๋ฅผ ๋ง๊ฐ๋จ๋ฆฌ์ง ์๋ 5๊ณ๋ช
]
1์ฃผ์ฐจ ๋ด:
๋ฏธ๋ ์ฃผ์ฐจ:
[์บก์ํ] โโโโโโ [SRP] (๋จ์ผ ์ฑ
์)
[DIP] (์์กด ์ญ์ )
[์์] โโโโโโโโ [LSP] (๋ฆฌ์ค์ฝํ ์นํ)
[ISP] (์ธํฐํ์ด์ค ๋ถ๋ฆฌ)
[๋คํ์ฑ] โโโโโโ [OCP] (๊ฐ๋ฐฉ-ํ์)
[DIP] (์์กด ์ญ์ )
[์ถ์ํ] โโโโโโ ๊ฑฐ์ ๋ชจ๋ SOLID ์์น
โ SOLID๋ 4๋ ์์น์ ์ ์ฐ๊ธฐ ์ํ 5๊ฐ์ง ๊ท์น.
[1960s] ์ ์ฐจ์งํฅ (C)
โ
[1990s] ๊ฐ์ฒด์งํฅ (Java) โญ ์ฐ๋ฆฌ๊ฐ ํ์ต ์ค
โ
[2000s] ํจ์ํ ๋ถํ (Scala, Haskell)
โ
[2010s] ๋ค์ค ํจ๋ฌ๋ค์ (Kotlin, Modern Java)
โ
[2020s+] ๋๊ตฌ๋ณ ์ ์ฌ์ ์ ์ฌ์ฉ
์๋ฐ๋ 8๋ฒ์ ๋ถํฐ ํจ์ํ ์์ (Lambda, Stream) ์ถ๊ฐ โ 3์ฃผ์ฐจ์์ ํ์ต.
| ์ง๋ฌธ | ์ด Unit์์์ ๋ต |
|---|---|
| "๊ฐ์ฒด์งํฅ์ด ๋ญ๊ฐ์?" | ๋ฐ์ดํฐ์ ํ๋์ ๊ฐ์ฒด๋ก ๋ฌถ๊ณ 4๋ ์์น์ผ๋ก ๊ด๋ฆฌ |
| "๊ฐ์ฒด์งํฅ์ 4๋ ์์น์?" | ์บก์ํ, ์์, ๋คํ์ฑ, ์ถ์ํ |
| "์ ๊ฐ์ฒด์งํฅ์ ์ฐ๋์?" | ํฐ ์์คํ ์์ ์ ์ง๋ณด์/ํ์ฅ/์ฌ์ฌ์ฉ์ ์ํด |
| "์ ์ฐจ์งํฅ๊ณผ ์ฐจ์ด๋?" | ๋ฐ์ดํฐ-ํจ์ ๋ถ๋ฆฌ vs ๊ฐ์ฒด๋ก ๋ฌถ์, ๊ทธ๋ก ์ธํ if ์ง์ฅ vs ๋คํ์ฑ |
1๏ธโฃ ๊ฐ์ฒด์งํฅ์ "ํฐ ์์คํ ์ ๋ณต์ก๋"๋ฅผ ๋ค๋ฃจ๊ธฐ ์ํ ํจ๋ฌ๋ค์์ด๋ค.
์ ์ฐจ์งํฅ์ด ํ๋ฆฐ ๊ฒ ์๋๋ผ, ์ฝ๋๊ฐ ์ปค์ง๋ฉด์ ๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑยทํ์ฅ์ฑยท์ฌ์ฌ์ฉ์ฑ์ ํ๊ณ๊ฐ ๋๋ฌ๋ฌ๊ณ , ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ฐ์ดํฐ์ ํ๋์ ๊ฐ์ฒด ๋ผ๋ ๋จ์๋ก ๋ฌถ๋ ์ฌ๊ณ ๋ฐฉ์์ด ๋ฑ์ฅํ๋ค.
2๏ธโฃ 4๋ ์์น(์บก์๋ค์ถ)์ ์งํฌ ๋๋ง ๊ฐ์ฒด์งํฅ์ ๊ฐ์น๊ฐ ์ด์๋๋ค.
์บก์ํ(๋ฐ์ดํฐ ๋ณดํธ), ์์(๊ณตํต ์ถ์ถ), ๋คํ์ฑ(if ์ง์ฅ ํด๊ฒฐ), ์ถ์ํ(๋ณธ์ง๋ง ๋ ธ์ถ) โ ์ด ๋ค ๊ฐ์ง๊ฐ ํจ๊ป ์๋ํ ๋ "์ ๊ธฐ๋ฅ ์ถ๊ฐ ์ ๊ธฐ์กด ์ฝ๋ ์ ๊ฑด๋๋ฆฌ๊ธฐ" ๊ฐ์ ๋ง๋ฒ์ด ๊ฐ๋ฅํ๋ค. ๋นํ ๋ชจ๋ธยทGod Class ๊ฐ์ ์ํฐํจํด์ ๊ฐ์ฒด์งํฅ์ ํ๋ง ์ด ์ ์ฐจ์งํฅ์ด๋ค.
3๏ธโฃ ๊ฐ์ฒด์งํฅ์ ๋๊ตฌ๋ค โ ๋ชจ๋ ๊ณณ์ ์ฐ๋ฉด ์ ๋๋ค.
ํฐ ์์คํ ยทํ์ ยท์ฅ๊ธฐ ์ ์ง๋ณด์์๋ ๊ฐ๋ ฅํ์ง๋ง, ๋จ์ ์คํฌ๋ฆฝํธ๋ ์ํ ๊ณ์ฐ์๋ ๊ณผํ ๋๊ตฌ๋ค. ์๋ฐ๋ ๊ฐ์ฒด์งํฅ์ ๊ธฐ๋ณธ์ผ๋ก ํ๋ ํจ์ํ(Lambda, Stream)๋ ํก์ํ ๋ค์ค ํจ๋ฌ๋ค์ ์ธ์ด ๋ก ์งํ ์ค์ด๋ค. ์ ์ฌ์ ์๊ฐ ํต์ฌ.