Favor Composition over inheritance
"์์ ๋์ ์ ์ปดํฌ์ง์ (๋ป: ๊ตฌ์ฑ์์๋ค, ๊ตฌ์ฑ)์ ๋ ์ ํธํ์ธ์."
ํ์ ์คํฌ๋ฆฝํธ OOP์ ๊ฝ, composition์ ๋ค๋ฃจ๊ธฐ ์ ์ ์์์ ๋ฌธ์ ์ ์ ๋ํด์ ๋จผ์ ์ ํ์๊ฐ ์๋ค.
๋ฏธ๋ฆฌ ๋งํ์๋ฉด, composition์ ๋ณต์กํ๊ฒ ์์์ ์์ง ๊ตฌ์กฐ๊ฐ ๊ธธ์ด์ง๋ ๋์ ์ ์์์ ๋ ๋ฒจ์ ํ ๋จ๊ณ๋ก๋ง ์ ์งํ ์ ์๋๋ก ๋์์ฃผ๋ ๊ธฐ๋ฒ์ด๋ค.
๋ฌธ์ ์ ๋ ๋ฏธ๋ฆฌ ๋งํ์๋ฉด, composition์ฉ์ผ๋ก ๊ฐ์ ธ๋ค ์ฐ๋ ํด๋์ค์ ๊ฐ์ ธ์ค๋ ํด๋์ค ์ฌ์ด์ ์ปคํ๋ง์ด ๋๋ฌด ์ฌํ๋ค๋ ์ ์ด ์๋ค.( ๊ฐ์ ธ๋ค ์ฐ๋ ํด๋์ค๋ฅผ ๋ณ๊ฒฝํ๊ฑฐ๋ ๋ค๋ฅธ ํด๋์ค๋ก ๋ฐ๊พธ๊ณ ์ถ๋ค๋ฉด, ๊ฐ์ ธ๋ค ์ฐ๋ ํด๋์ค์ ์ฐ๊ฒฐ๋ ๋ชจ๋ ํด๋์ค์ ๋ค์ด๊ฐ์ ์ฝ๋๋ฅผ ์ ๋ฐ์ดํธํด์ผ ํ๋ค.-> ๋ ํ์ฅ์ด ์ฝ๊ณ ์ ์ฐํ ํ ํฌ๋์ ๋ค์ ํฌ์คํ ์ ๋ณด๋ฉด๋๋ค(๊ฐ๋ ฅํ ์ธํฐํ์ด์ค๋ฅผ ์ด์ฉํ ์ ๋ตํจํด))
<์์์ ๋ฌธ์ ์ >
์กฑ๋ณด๊ฐ ๊ผฌ์ธ๋ค...
์์์ ๊น์ด๊ฐ ๋๋ฌด ๊น์ด์ ธ์ ์กฐ์ง๋๊ฐ ์ปค์ง๋ค๋ณด๋ฉด ๊ด๊ณ์ฑ์ด ๋ณต์กํด์ง ์ ์๋ค.
์ปคํผ๋จธ์ ์ ์์๋ฐ์ ์นดํ๋ผ๋ผ๋จธ์ ๊ณผ ์๊ธ์ปคํผ๋จธ์ ์ด ์๋ค.
๊ทธ๋ฐ๋ฐ ์๊ธ์นดํ๋ผ๋ผ๋จธ์ ์ด ํ์ํด์ง๋ฉด?
์์ ๊ฐ์ ์ฃฝ์์ ๋ค์ด์๋ชฌ๋ ์์๊ตฌ์กฐ๊ฐ ๋งํ ์ด์ ๋ ๋ณธ ํฌ์คํ ์ฐธ๊ณ (https://rainbowcode.tistory.com/174)(c#์์ ์ด๊ธด ํ๋, ์ด์ฐจํผ ํ์ ์คํฌ๋ฆฝํธ ๊ฐ๋ฐ์ฃผ์ถ์ด C#๊ฐ๋ฐ์ฃผ์ถ์ด๋ผ์ OOP ์ธ์ด ์ฒ ํ์ด ์๋นํ ์ ์ฌ๋ค๊ณ ๋ณด๋ฉด ๋๋ฏ๋ก, ๋ ํด์ ์ด๋ ค์์ ์์ ๊ฒ์ด๋ค.
ํ์ ์คํฌ๋ฆฝํธ
์ํค๋ฐฑ๊ณผ | |
๋ฉํฐ ํจ๋ฌ๋ค์: ํจ์ํ, ์ ๋ค๋ฆญ, ๋ช ๋ นํ, ๊ฐ์ฒด ์งํฅ | |
๋ง์ดํฌ๋ก์ํํธ |
|
๋ ํ์ดํ, gradual, structural[4] | |
์ํ์น ๋ผ์ด์ ์ค 2.0 | |
.ts, .tsx | |
www.typescriptlang.org |
ํ์ ์คํฌ๋ฆฝํธ(TypeScript)๋ ์๋ฐ์คํฌ๋ฆฝํธ์ ์ํผ์ ์ธ ์คํ์์ค ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์ด๋ค. ๋ง์ดํฌ๋ก์ํํธ์์ ๊ฐ๋ฐ, ์ ์งํ๊ณ ์์ผ๋ฉฐ ์๊ฒฉํ ๋ฌธ๋ฒ์ ์ง์ํ๋ค. C#์ ๋ฆฌ๋ ์ํคํ ํธ์ด์ ๋ธํ์ด, ํฐ๋ณด ํ์ค์นผ์ ์ฐฝ์์์ธ Anders Hejlsberg๊ฐ ๊ฐ๋ฐ์ ์ฐธ์ฌํ๋ค.[5] ํด๋ผ์ด์ธํธ ์ฌ์ด๋์ ์๋ฒ ์ฌ์ด๋๋ฅผ ์ํ ๊ฐ๋ฐ์ ์ฌ์ฉํ ์ ์๋ค.
ํ์ ์คํฌ๋ฆฝํธ๋ ์๋ฐ์คํฌ๋ฆฝํธ ์์ง์ ์ฌ์ฉํ๋ฉด์ ์ปค๋ค๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐํ ์ ์๊ฒ ์ค๊ณ๋ ์ธ์ด์ด๋ค.[6] ์๋ฐ์คํฌ๋ฆฝํธ์ ์ํผ์ ์ด๊ธฐ ๋๋ฌธ์ ์๋ฐ์คํฌ๋ฆฝํธ๋ก ์์ฑ๋ ํ๋ก๊ทธ๋จ์ด ํ์ ์คํฌ๋ฆฝํธ ํ๋ก๊ทธ๋จ์ผ๋ก๋ ๋์ํ๋ค.
ํ์ ์คํฌ๋ฆฝํธ์์ ์์ ์ด ์ํ๋ ํ์ ์ ์ ์ํ๊ณ ํ๋ก๊ทธ๋๋ฐ์ ํ๋ฉด ์๋ฐ์คํฌ๋ฆฝํธ๋ก ์ปดํ์ผ๋์ด ์คํํ ์ ์๋ค.
ํ์ ์คํฌ๋ฆฝํธ๋ ๋ชจ๋ ์ด์ ์ฒด์ , ๋ชจ๋ ๋ธ๋ผ์ฐ์ , ๋ชจ๋ ํธ์คํธ์์ ์ฌ์ฉ ๊ฐ๋ฅํ ์คํ ์์ค์ด๋ค.
)
๊ฐ์คํ๊ณ
์ด์ ์ปคํผ๊ฐ ์ ์ ๋ ๋ง์์ง๋ฉด?
ํ๋ ์ฒผ ์ปคํผ๋ ์๋ก ์ ๋ณด์๋๋ฐ ๋ฐ์์ด ์ข์์
์ฐ์ ๊ฐ ๋ค์ด๊ฐ ํ๋ ์ฒผ ์ปคํผ๋ ๊ฐ๋ฐํ๊ณ ๋ ๋ฐ์์ด ํฐ์ ธ์
์ฐ์ ๊ฐ ๋ค์ด๊ฐ ํ๋ ์ฒผ ์ปคํผ์ ์๊ธ๋ ๋ฟ๋ ค์ ํ๋งคํ๊ณ ์ถ๋ค๋ฉด?
๊ณ์ ์๋๋ก ์์์์ผ์ค์ผ ํ ๊น? ๊ทธ๋์ ์๋ก ๋ง๋ค์ด์ง ์ปคํผ๋จธ์ ๋ค ๋๋ฌธ์ ์กฑ๋ณด๊ฐ ๋์์ด ๊ธธ์ด์ ธ์ ์๋๋ก ๋ด๋ ค๊ฐ๊ฒ ๋ ๊น?
๋ณธ๊ฒฉ์ ์ผ๋ก ์กฑ๋ณด๊ฐ ๊ผฌ์ด๊ฒ ๋๋ค.
์์์ด ๋ฌธ์ ๋ผ๋ ๊ฒ์ด ์๋๋ผ ๋ชจ๋ ๋ฌธ์ ๋ฅผ ์์์ผ๋ก ํ๋ ค๊ณ ํ๋ฉด ๊ผฌ์ธ๋ค๋ ๋ป์ด๋ค.
(์์์ ์ฝ๋ ์ค๋ณต์ ์์ฐ๊ณผ ๋์์ ๋คํ์ฑ ๊ตฌํ์ ์ํ ํ์์์ ์ค ํ๋์ด๋ค)
ํนํ๋ ์์์ ์์์ ์๋๋ก ์์ง์ ์ผ๋ก ๊ด๊ณ๊ฐ ํ์ฑ๋๋ค.
์ด๋ฌํ ์์์ ์น๋ช ์ ์ธ ๋ฌธ์ ์ ์ ๊ฐ๋ฐ์๊ฐ ์ด๋ค ๋ถ๋ชจํด๋์ค์ ํ๋์ ์์ ํ๊ฒ ๋๋ฉด
์ด ์์ ๋ ์ฌํญ๋๋ฌธ์ ์์๋ฐ๋ ๋ชจ~๋ ์์ ํด๋์ค๋ค์ด ์ํฅ์ ๋ฐ์ ์๋ฐ์ ์๋ ๊ตฌ์กฐ์ด๋ค.
(์์์ ๋ฌธ์ ๊ฐ ํฐ์ ธ์ ์๋ณด๋ฉด ์๋๋ ํฐ์ ธ์ ์ผ์ผํ ์ฐพ์๊ฐ์ ๊ณ ์ณ์ค์ผ ํ๋ ๊ตฌ์กฐ)
์ด๋ฌํ ์์์ ๋ฌธ์ ์ ๋ค ๋๋ฌธ์ ์์์ด ๋ถํ์ํด์ง๋ ์ ์ด ์ค๋ฉด, ์ปดํฌ์ง์ ์ ์ฌ์ฉํ๋ ๊ฒ์ด ๋ ์ข๋ค!
<๋ชจ๋ ๊ฒ์ ๋ค์๋ ์ปดํฌ์ง์ , Favor Composition over inheritance>
Favor Composition over inheritance
"์์ ๋์ ์ ์ปดํฌ์ง์ (๋ป: ๊ตฌ์ฑ์์๋ค, ๊ตฌ์ฑ)์ ๋ ์ ํธํ์ธ์."
๋ ๊ณ ์ฑ์ ์์ ๋๋ง๋ค ํ์ํ ๋ถํ๋ค์ ํ๋์ฉ ๊ฝ์์ ์กฐ๋ฆฝํด๋๊ฐ๋ ๊ฒ ์ฒ๋ผ ์ปดํฌ์ง์ ๋ ํ๋์ฉ ๊ฝ์๋๊ฐ๋ ๊ฒ์ ๋งํ๋ค.
์ปดํฌ์ง์ ์ ํตํด ์๊ธ์นดํ๋ผ๋ผ๋จธ์ ์ ๋ง๋ค์ด๋ณธ๋ค.
์ด๋ฌํ ์ปดํฌ์ง์ ์ ์ด๋ป๊ฒ ๊ตฌํํ ๊น
์๋ ์์ค์ฝ๋(์ปคํผ๋จธ์ , ์ปคํผ๋จธ์ ์ ์์ํ ์นดํ๋ผ๋ผ๋จธ์ , ์ปคํผ๋จธ์ ์ ์์ํ ์๊ธ์ปคํผ๋จธ์ ๊ตฌํ๋์ด์์)์
์๊ธ์นดํ๋ผ๋ผ๋จธ์ (๊ทธ๋ฌ๋๊น ์ปคํผ๋ ๋ด๋ ค์ผํ๊ณ , ๋ฐ์ด ์ฐ์ ๋ ํ์ํ๊ณ , ์๊ธ๋ ํ๊ผฌ์ง ๋ฃ์...) ์ ์๋ก ์ถ๊ฐํ๊ณ ์ถ๋ค๋ฉด?
// //์ปคํผ๋จธ์ ๋ง๋ค๊ธฐ
type CoffeeCup = {
orderShot: number;
hasMilk?: boolean;
hasSalt?: boolean;
};
interface CoffeeMaker {
makeCoffee(shots: number): CoffeeCup;
}
// CoffeeMaker ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ CoffeMachine ํด๋์ค์ด๋ค.
class CoffeMachine implements CoffeeMaker {
protected static ONE_SHOT_CAPSULE: number = 3;
protected static ONE_CUP_WATER: number = 10;
static makeMachine(beans: number, water: number): CoffeMachine {
return new CoffeMachine(beans, water);
}
public constructor(
protected capsule: number = 3,
protected water: number = 10
) {}
makeCoffee(orderShot: number): CoffeeCup {
this.checkMaterials(orderShot);
console.log("์บก์์ฐ๊ธฐ ์ " + this.capsule);
this.useCapsule(orderShot);
console.log("๋ฌผ์ฐ๊ธฐ ์ " + this.water);
this.useWater();
this.heatWater();
return this.extract(orderShot);
}
protected checkMaterials(orderShot: number) {
if (this.capsule < orderShot * CoffeMachine.ONE_SHOT_CAPSULE) {
throw new Error(`์ปคํผ์บก์ ๋ถ์กฑ!!! ํ์ฌ ๋ณด์ ์บก์ : ${this.capsule}`);
}
if (this.water < CoffeMachine.ONE_CUP_WATER) {
throw new Error(`๋ฌผ ๋ถ์กฑ!!! ๋จ์ ๋ฌผ ${this.water}`);
}
}
clean(): void {
console.log("์ปคํผ๋จธ์ ํ๊ตฌ๋ ์ค!");
}
private extract(shots: number): CoffeeCup {
return {
orderShot: shots,
hasMilk: false,
};
}
private heatWater() {
console.log("๋ฌผ ๋์ด๋ ์ค...");
}
private useCapsule(shot: number) {
console.log("์บก์์ฐ๋ฌ์์ต๋๋ค.");
this.capsule -= shot * CoffeMachine.ONE_SHOT_CAPSULE;
console.log("์บก์ ์ฐ๊ณ ๋ ํ" + this.capsule);
}
protected useWater() {
console.log("๋ฌผ์ฐ๋ฌ์์ต๋๋ค.");
this.water -= CoffeMachine.ONE_CUP_WATER;
console.log("๋ฌผ์ฐ๊ณ ๋ ํ " + this.water);
}
addCapsule(capsule: number) {
this.capsule += capsule;
}
fulfillWater(water: number) {
this.water += water;
}
get Water(): number {
return this.water;
}
get Capsule(): number {
return this.capsule;
}
}
// CoffeMachine ํด๋์ค๋ฅผ ์์๋ฐ์ CafeLatteMachine ํด๋์ค์ด๋ค.
class CafeLatteMachine extends CoffeMachine {
makeCoffee(orderShot: number): CoffeeCup {
const coffee = super.makeCoffee(orderShot); //๊ธฐ๋ณธ์ ์ธ ์บก์ ์๋น, ๋ฌผ ์๋น, ๋ฌผ ๊ฐ์ด ๋ค ๋๋๊ณ ์์ฑ๋ ์ปคํผ๊ฐ ๋ฐํ
//๋ถ๋ชจ์์ ๋ฐ์์จ ์ปคํผ ์์ฑํ์ ์ฐ์ ๋ง ๋ถ์ด์ฃผ์!!!
this.steamMilk();
this.pourMilk();
return {
...coffee,
hasMilk: true,
};
}
private steamMilk(): void {
console.log("์ฐ์ ๋ฐ์ฐ๋ ์ค...");
}
private pourMilk() {
console.log("์ฐ์ ๋ถ๋ ์ค...");
}
}
// CoffeMachine ํด๋์ค๋ฅผ ์์๋ฐ์ saltCoffeeMachine ํด๋์ค์ด๋ค.
class saltCoffeeMachine extends CoffeMachine {
makeCoffee(orderShot: number): CoffeeCup {
const coffee = super.makeCoffee(orderShot);
//์๊ธ์ ๋ฟ๋ฆฐ๋ค.
this.addSalt();
return {
...coffee,
hasSalt: true,
};
}
addSalt() {
console.log("์๊ธ์ ํ๊ผฌ์ง");
}
}
const machines: CoffeeMaker[] = [
new CoffeMachine(25, 25),
new CafeLatteMachine(15, 25),
new saltCoffeeMachine(5, 10),
];
machines.forEach((machine) => {
console.log("------------์ปคํผ๋จธ์ ๊ฐ๋~!------------");
console.log(machine.makeCoffee(1));
});
<์ฝ๋ ์์์ ์ถ๊ฐ์ ์ถ๋ ฅ ๊ฒฐ๊ณผ>
------------์ปคํผ๋จธ์ ๊ฐ๋~!------------
์บก์์ฐ๊ธฐ ์ 25
์บก์์ฐ๋ฌ์์ต๋๋ค.
์บก์ ์ฐ๊ณ ๋ ํ22
๋ฌผ์ฐ๊ธฐ ์ 25
๋ฌผ์ฐ๋ฌ์์ต๋๋ค.
๋ฌผ์ฐ๊ณ ๋ ํ 15
๋ฌผ ๋์ด๋ ์ค...
{ orderShot: 1, hasMilk: false }
------------์ปคํผ๋จธ์ ๊ฐ๋~!------------
์บก์์ฐ๊ธฐ ์ 15
์บก์์ฐ๋ฌ์์ต๋๋ค.
์บก์ ์ฐ๊ณ ๋ ํ12
๋ฌผ์ฐ๊ธฐ ์ 25
๋ฌผ์ฐ๋ฌ์์ต๋๋ค.
๋ฌผ์ฐ๊ณ ๋ ํ 15
๋ฌผ ๋์ด๋ ์ค...
์ฐ์ ๋ฐ์ฐ๋ ์ค...
์ฐ์ ๋ถ๋ ์ค...
{ orderShot: 1, hasMilk: true }
------------์ปคํผ๋จธ์ ๊ฐ๋~!------------
์บก์์ฐ๊ธฐ ์ 5
์บก์์ฐ๋ฌ์์ต๋๋ค.
์บก์ ์ฐ๊ณ ๋ ํ2
๋ฌผ์ฐ๊ธฐ ์ 10
๋ฌผ์ฐ๋ฌ์์ต๋๋ค.
๋ฌผ์ฐ๊ณ ๋ ํ 0
๋ฌผ ๋์ด๋ ์ค...
์๊ธ์ ํ๊ผฌ์ง
{ orderShot: 1, hasMilk: false, hasSalt: true }
์์๋ง์ ์ด์ฉํด์ผ ํ๋ค๋ฉด ๋ฌด์ธ๊ฐ ์นดํ๋ผ๋ผ๋จธ์ ์์ ์ฐ์ ๊ฐ์ ธ์ค๊ณ ~ ์๊ธ์ปคํผ๋จธ์ ์์ ์๊ธ๊ฐ์ ธ์ค๊ณ ~ ํด์ผ๊ฒ ์ง๋ง
์ฐจ๋ผ๋ฆฌ ์ฐ์ ๊ฑฐํ๊ธฐ ํด๋์ค์ ์๊ธํต ํด๋์ค๊ฐ ๋ฐ๋ก์์ด์ ํ์ํ ๋ ๋ง๋ค ๊ฐ์ ธ๋ค ๋ถ์ด๋ ๊ฒ์ด ๋ ํจ์จ์ ์ผ ๊ฒ์ด๊ณ
์ปดํฌ์ง์ ์ ๊ทธ๊ฒ์ ๊ฐ๋ฅํ๊ฒ ํด์ค๋ค!
์ฐ์ ๋ค์๊ณผ ๊ฐ์ด
ํ๋ฒํ ์ฐ์ ๊ฑฐํ๊ธฐ ํด๋์ค์ ์๊ธ๋ฏน์ ํด๋์ค๋ฅผ ์์ฑํด์ค๋ค.
์ฐ์ ๊ฑฐํ๊ธฐ ํด๋์ค ์์ฒด์ ์ผ๋ก ์ฐ์ ๊ฑฐํ์ ๋ง๋ค๊ณ , ์ธ๋ถ์์ ์ปคํผ์ปต์ ๋ฐ์์ฌ ๊ฒฝ์ฐ ๋ด๋ถ์์ ๋ง๋ ์ฐ์ ๊ฑฐํ์ ์ฌ๋ฆฐ ์ปคํผ์ปต์ ๋ฐํํด์ค ๊ฒ์ด๋ค.
์๊ธ ๋ฏน์ ํด๋์ค๋ ๋ด๋ถ์์ ์๊ธ์ ์ง์ด์ฌ๋ฆฌ๊ณ , ์ธ๋ถ์์ ์ปคํผ์ปต์ ๋ฐ์์ฌ ๊ฒฝ์ฐ ๋ด๋ถ์์ ์ง์ด๋ ์๊ธ์ ๋ฟ๋ ค ๊ทธ ์ปคํผ์ปต์ ๋ฐํํด์ฃผ๋๋ก ๋ง๋ค์ด๋ณธ๋ค.
// //์ปคํผ๋จธ์ ๋ง๋ค๊ธฐ
type CoffeeCup = {
orderShot: number;
hasMilk?: boolean;
hasSalt?: boolean;
};
interface CoffeeMaker {
makeCoffee(shots: number): CoffeeCup;
}
//์ผ๋ฐ ์ฐ์ ๊ฑฐํ๊ธฐ//////////////////////////////////////////////////////////////////////
class CheapMilkSteamer {
//(1)์ฐ์ ๊ฑฐํ์ ๋ง๋๋ ๋ด๋ถ ํจ์
private steamMilk(): void {
console.log("๋ชฝ๊ธ๋ชฝ๊ธ ํฌ์ญํฌ์ญํ ์ฐ์ ํผ");
}
//(2)๋ง๋ค์ด์ง ์ปคํผ์ปต์ ๋ฐ์์, ๋ง๋ค์ด์ง ์ฐ์ ๊ฑฐํ(1)์ ๋ถ์ด์ ์ด ์ปคํผ์ปต์ ๋ค์ ๋ฆฌํดํ๋ ํจ์
addMilkform(cup: CoffeeCup): CoffeeCup {
this.steamMilk();
return {
...cup,
hasMilk: true,
};
}
}
//์๊ธ๋ฏน์///////////////////////////////////////////////////////////////////////////////
class SaltMixer {
//(1)์๊ธ์ ์ง๋ ๋ด๋ถ ํจ์
private getSalt() {
console.log("์๊ธ์ ํ๊ผฌ์ง ์ง์๋ค");
return true;
}
//(2)๋ง๋ค์ด์ง ์ปคํผ์ปต์ ๋ฐ์์, ์ง์ ์๊ธ(1)์ ๋ฟ๋ ค์ ์ด ์ปคํผ์ปต์ ๋ค์ ๋ฆฌํดํ๋ ํจ์
addSalt(cup: CoffeeCup): CoffeeCup {
const salt = this.getSalt();
return {
...cup,
hasSalt: salt,
};
}
}
// CoffeeMaker ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ CoffeMachine ํด๋์ค์ด๋ค.
class CoffeMachine implements CoffeeMaker {
protected static ONE_SHOT_CAPSULE: number = 3;
protected static ONE_CUP_WATER: number = 10;
static makeMachine(beans: number, water: number): CoffeMachine {
return new CoffeMachine(beans, water);
}
public constructor(
protected capsule: number = 3,
protected water: number = 10
) {}
makeCoffee(orderShot: number): CoffeeCup {
this.checkMaterials(orderShot);
console.log("์บก์์ฐ๊ธฐ ์ " + this.capsule);
this.useCapsule(orderShot);
console.log("๋ฌผ์ฐ๊ธฐ ์ " + this.water);
this.useWater();
this.heatWater();
return this.extract(orderShot);
}
protected checkMaterials(orderShot: number) {
if (this.capsule < orderShot * CoffeMachine.ONE_SHOT_CAPSULE) {
throw new Error(`์ปคํผ์บก์ ๋ถ์กฑ!!! ํ์ฌ ๋ณด์ ์บก์ : ${this.capsule}`);
}
if (this.water < CoffeMachine.ONE_CUP_WATER) {
throw new Error(`๋ฌผ ๋ถ์กฑ!!! ๋จ์ ๋ฌผ ${this.water}`);
}
}
clean(): void {
console.log("์ปคํผ๋จธ์ ํ๊ตฌ๋ ์ค!");
}
private extract(shots: number): CoffeeCup {
return {
orderShot: shots,
hasMilk: false,
};
}
private heatWater() {
console.log("๋ฌผ ๋์ด๋ ์ค...");
}
private useCapsule(shot: number) {
console.log("์บก์์ฐ๋ฌ์์ต๋๋ค.");
this.capsule -= shot * CoffeMachine.ONE_SHOT_CAPSULE;
console.log("์บก์ ์ฐ๊ณ ๋ ํ" + this.capsule);
}
protected useWater() {
console.log("๋ฌผ์ฐ๋ฌ์์ต๋๋ค.");
this.water -= CoffeMachine.ONE_CUP_WATER;
console.log("๋ฌผ์ฐ๊ณ ๋ ํ " + this.water);
}
addCapsule(capsule: number) {
this.capsule += capsule;
}
fulfillWater(water: number) {
this.water += water;
}
get Water(): number {
return this.water;
}
get Capsule(): number {
return this.capsule;
}
}
// CoffeMachine ํด๋์ค๋ฅผ ์์๋ฐ์ CafeLatteMachine ํด๋์ค์ด๋ค.
class CafeLatteMachine extends CoffeMachine {
makeCoffee(orderShot: number): CoffeeCup {
const coffee = super.makeCoffee(orderShot); //๊ธฐ๋ณธ์ ์ธ ์บก์ ์๋น, ๋ฌผ ์๋น, ๋ฌผ ๊ฐ์ด ๋ค ๋๋๊ณ ์์ฑ๋ ์ปคํผ๊ฐ ๋ฐํ
//๋ถ๋ชจ์์ ๋ฐ์์จ ์ปคํผ ์์ฑํ์ ์ฐ์ ๋ง ๋ถ์ด์ฃผ์!!!//////////////////////////////////////////////
this.steamMilk();
this.pourMilk();
return {
...coffee,
hasMilk: true,
};
}
private steamMilk(): void {
console.log("์ฐ์ ๋ฐ์ฐ๋ ์ค...");
}
private pourMilk() {
console.log("์ฐ์ ๋ถ๋ ์ค...");
}
}
// CoffeMachine ํด๋์ค ๋ฅผ ์์๋ฐ์ saltCoffeeMachine ํด๋์ค์ด๋ค.
class saltCoffeeMachine extends CoffeMachine {
makeCoffee(orderShot: number): CoffeeCup {
const coffee = super.makeCoffee(orderShot);
//์๊ธ์ ๋ฟ๋ฆฐ๋ค.//////////////////////////////////////////////////////////////////////
this.addSalt();
return {
...coffee,
hasSalt: true,
};
}
private addSalt() {
console.log("์๊ธ์ ํ๊ผฌ์ง");
}
}
const machines: CoffeeMaker[] = [
new CoffeMachine(25, 25),
new CafeLatteMachine(15, 25),
new saltCoffeeMachine(5, 10),
];
machines.forEach((machine) => {
console.log("------------์ปคํผ๋จธ์ ๊ฐ๋~!------------");
console.log(machine.makeCoffee(1));
});
์ฌ๊ธฐ๊น์ง ๋ง๋ค์์ผ๋ฉด ์นดํ๋ผ๋ผ๋จธ์ ๋ด๋ถ์์ ๊ตณ์ด ์ฐ์ ๋ฅผ ๋ง๋ค๊ณ ๋ถ์ด์ฃผ๋ ๋ด๋ถ ํ๋์ด ํ์ํ์ง,
์๊ธ์ปคํผ๋จธ์ ๋ด๋ถ์์ ๊ตณ์ด ์๊ธ์ ์ง์ด์ ๋ฟ๋ ค์ฃผ๋ ๋ด๋ถ ํ๋์ด ํ์ํ์ง์ ๋ํ ์์ฌ์ด ํ์ํ๋ค.
๋ง๋ค์ด ๋ ์ฐ์ ๊ฑฐํ๊ธฐ ํด๋์ค์ ์๊ธ๋ฏน์ ํด๋์ค๋ฅผ ํ์ฉํ๋ ๊ฒ์ด ์ค๊ณ์ ๋ ๋์ ๋ฏํ๋ค.
์นดํ๋ผ๋ผ ๋จธ์ ๋ถํฐ ๊ณ ์ณ๋ณธ๋ค.
1. ์นดํ๋ผ๋ผ๋จธ์ ๋ด๋ถ์์ ์ฐ์ ๊ด๋ จ ๋ก์ง์ ๊ฑท์ด๋ธ๋ค.
// CoffeMachine ํด๋์ค๋ฅผ ์์๋ฐ์ CafeLatteMachine ํด๋์ค์ด๋ค.
class CafeLatteMachine extends CoffeMachine {
makeCoffee(orderShot: number): CoffeeCup {
const coffee = super.makeCoffee(orderShot); //๊ธฐ๋ณธ์ ์ธ ์บก์ ์๋น, ๋ฌผ ์๋น, ๋ฌผ ๊ฐ์ด ๋ค ๋๋๊ณ ์์ฑ๋ ์ปคํผ๊ฐ ๋ฐํ
return {
...coffee,
hasMilk: true,
};
}
}
2. ์ฐ์ ๊ฑฐํ๊ธฐ ํด๋์ค์์ ํ์ํ ๊ฒ๋ค์ ๋ฐ์์ค์
์ด๊ฒ์ dependency injection(https://ko.wikipedia.org/wiki/%EC%9D%98%EC%A1%B4%EC%84%B1_%EC%A3%BC%EC%9E%85)
์ด๋ผ๊ณ ๋ ํ๋ค.
// CoffeMachine ํด๋์ค๋ฅผ ์์๋ฐ์ CafeLatteMachine ํด๋์ค์ด๋ค.\\\\\\\\\\\\\\\\\\\\\\\\\\\\
class CafeLatteMachine extends CoffeMachine {
public constructor(
protected capsule: number = 3,
protected water: number = 10,
private milkFrother: CheapMilkSteamer
) {
super(capsule, water);
}
makeCoffee(orderShot: number): CoffeeCup {
const coffee = super.makeCoffee(orderShot); //๊ธฐ๋ณธ์ ์ธ ์บก์ ์๋น, ๋ฌผ ์๋น, ๋ฌผ ๊ฐ์ด ๋ค ๋๋๊ณ ์์ฑ๋ ์ปคํผ๊ฐ ๋ฐํ
return {
...coffee,
hasMilk: true,
};
}
}
3. ์ด์ ์ง์ ์ ์ผ๋ก hasMilk ํ๋กํผํฐ๋ฅผ true๋ก ๋ณ๊ฒฝํด์ ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ ๋ก์ง๋ ๊ฑท์ด๋ด๊ณ
milkFrother์ milk๋ฅผ ๋ฐ๋ก ์ ๋ฌํด์ฃผ์.
// CoffeMachine ํด๋์ค๋ฅผ ์์๋ฐ์ CafeLatteMachine ํด๋์ค์ด๋ค.\\\\\\\\\\\\\\\\\\\\\\\\\\\\
class CafeLatteMachine extends CoffeMachine {
public constructor(
protected capsule: number = 3,
protected water: number = 10,
private milkFrother: CheapMilkSteamer
) {
super(capsule, water);
}
makeCoffee(orderShot: number): CoffeeCup {
const coffee = super.makeCoffee(orderShot); //๊ธฐ๋ณธ์ ์ธ ์บก์ ์๋น, ๋ฌผ ์๋น, ๋ฌผ ๊ฐ์ด ๋ค ๋๋๊ณ ์์ฑ๋ ์ปคํผ๊ฐ ๋ฐํ
return this.milkFrother.addMilkform(coffee);
}
}
์๊ธ์ปคํผ ๋จธ์ ๋ ์์ ๋น์ทํ๊ฒ ๊ณ ์ณ๋ณธ๋ค.
1. ์๊ธ์ปคํผ๋จธ์ ๋ด๋ถ์์ ์๊ธ ๊ด๋ จ ๋ก์ง์ ๊ฑท์ด๋ธ๋ค.
class saltCoffeeMachine extends CoffeMachine {
makeCoffee(orderShot: number): CoffeeCup {
const coffee = super.makeCoffee(orderShot);
return {
...coffee,
hasSalt: true,
};
}
}
2. ์๊ธ๋ฏน์ ํด๋์ค์์ ํ์ํ ๊ฒ๋ค์ ๋ฐ์์ค์
์ด๊ฒ์ dependency injection(https://ko.wikipedia.org/wiki/%EC%9D%98%EC%A1%B4%EC%84%B1_%EC%A3%BC%EC%9E%85)
์ด๋ผ๊ณ ๋ ํ๋ค.
class saltCoffeeMachine extends CoffeMachine {
public constructor(
protected capsule: number = 3,
protected water: number = 10,
private saltTopping: SaltMixer
) {
super(capsule, water);
}
makeCoffee(orderShot: number): CoffeeCup {
const coffee = super.makeCoffee(orderShot);
return {
...coffee,
hasSalt: true,
};
}
}
3. ์ด์ ์ง์ ์ ์ผ๋ก hasMilk ํ๋กํผํฐ๋ฅผ true๋ก ๋ณ๊ฒฝํด์ ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ ๋ก์ง๋ ๊ฑท์ด๋ด๊ณ
์๊ธํ ํ์ ์ํธ๋ฏน์๋ฅผ ๋ฐ๋ก ์ ๋ฌํด์ฃผ์.
class saltCoffeeMachine extends CoffeMachine {
public constructor(
protected capsule: number = 3,
protected water: number = 10,
private saltTopping: SaltMixer
) {
super(capsule, water);
}
makeCoffee(orderShot: number): CoffeeCup {
const coffee = super.makeCoffee(orderShot);
return this.saltTopping.addSalt(coffee);
}
}
<ํ์ฌ๊น์ง ์์ค์ฝ๋>
// //์ปคํผ๋จธ์ ๋ง๋ค๊ธฐ
type CoffeeCup = {
orderShot: number;
hasMilk?: boolean;
hasSalt?: boolean;
};
interface CoffeeMaker {
makeCoffee(shots: number): CoffeeCup;
}
//์ผ๋ฐ ์ฐ์ ๊ฑฐํ๊ธฐ//////////////////////////////////////////////////////////////////////
class CheapMilkSteamer {
//(1)์ฐ์ ๊ฑฐํ์ ๋ง๋๋ ๋ด๋ถ ํจ์
private steamMilk(): void {
console.log("๋ชฝ๊ธ๋ชฝ๊ธ ํฌ์ญํฌ์ญํ ์ฐ์ ํผ");
}
//(2)๋ง๋ค์ด์ง ์ปคํผ์ปต์ ๋ฐ์์, ๋ง๋ค์ด์ง ์ฐ์ ๊ฑฐํ(1)์ ๋ถ์ด์ ์ด ์ปคํผ์ปต์ ๋ค์ ๋ฆฌํดํ๋ ํจ์
addMilkform(cup: CoffeeCup): CoffeeCup {
this.steamMilk();
return {
...cup,
hasMilk: true,
};
}
}
//์๊ธ๋ฏน์///////////////////////////////////////////////////////////////////////////////
class SaltMixer {
//(1)์๊ธ์ ์ง๋ ๋ด๋ถ ํจ์
private getSalt() {
console.log("์๊ธ์ ํ๊ผฌ์ง ์ง์๋ค");
return true;
}
//(2)๋ง๋ค์ด์ง ์ปคํผ์ปต์ ๋ฐ์์, ์ง์ ์๊ธ(1)์ ๋ฟ๋ ค์ ์ด ์ปคํผ์ปต์ ๋ค์ ๋ฆฌํดํ๋ ํจ์
addSalt(cup: CoffeeCup): CoffeeCup {
const salt = this.getSalt();
return {
...cup,
hasSalt: salt,
};
}
}
// CoffeeMaker ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ CoffeMachine ํด๋์ค์ด๋ค.////////////////////////////
class CoffeMachine implements CoffeeMaker {
protected static ONE_SHOT_CAPSULE: number = 3;
protected static ONE_CUP_WATER: number = 10;
static makeMachine(beans: number, water: number): CoffeMachine {
return new CoffeMachine(beans, water);
}
public constructor(
protected capsule: number = 3,
protected water: number = 10
) {}
makeCoffee(orderShot: number): CoffeeCup {
this.checkMaterials(orderShot);
console.log("์บก์์ฐ๊ธฐ ์ " + this.capsule);
this.useCapsule(orderShot);
console.log("๋ฌผ์ฐ๊ธฐ ์ " + this.water);
this.useWater();
this.heatWater();
return this.extract(orderShot);
}
protected checkMaterials(orderShot: number) {
if (this.capsule < orderShot * CoffeMachine.ONE_SHOT_CAPSULE) {
throw new Error(`์ปคํผ์บก์ ๋ถ์กฑ!!! ํ์ฌ ๋ณด์ ์บก์ : ${this.capsule}`);
}
if (this.water < CoffeMachine.ONE_CUP_WATER) {
throw new Error(`๋ฌผ ๋ถ์กฑ!!! ๋จ์ ๋ฌผ ${this.water}`);
}
}
clean(): void {
console.log("์ปคํผ๋จธ์ ํ๊ตฌ๋ ์ค!");
}
private extract(shots: number): CoffeeCup {
return {
orderShot: shots,
hasMilk: false,
};
}
private heatWater() {
console.log("๋ฌผ ๋์ด๋ ์ค...");
}
private useCapsule(shot: number) {
console.log("์บก์์ฐ๋ฌ์์ต๋๋ค.");
this.capsule -= shot * CoffeMachine.ONE_SHOT_CAPSULE;
console.log("์บก์ ์ฐ๊ณ ๋ ํ" + this.capsule);
}
protected useWater() {
console.log("๋ฌผ์ฐ๋ฌ์์ต๋๋ค.");
this.water -= CoffeMachine.ONE_CUP_WATER;
console.log("๋ฌผ์ฐ๊ณ ๋ ํ " + this.water);
}
addCapsule(capsule: number) {
this.capsule += capsule;
}
fulfillWater(water: number) {
this.water += water;
}
get Water(): number {
return this.water;
}
get Capsule(): number {
return this.capsule;
}
}
// CoffeMachine ํด๋์ค๋ฅผ ์์๋ฐ์ CafeLatteMachine ํด๋์ค์ด๋ค.\\\\\\\\\\\\\\\\\\\\\\\\\\\\
class CafeLatteMachine extends CoffeMachine {
public constructor(
protected capsule: number = 3,
protected water: number = 10,
private milkFrother: CheapMilkSteamer
) {
super(capsule, water);
}
makeCoffee(orderShot: number): CoffeeCup {
const coffee = super.makeCoffee(orderShot); //๊ธฐ๋ณธ์ ์ธ ์บก์ ์๋น, ๋ฌผ ์๋น, ๋ฌผ ๊ฐ์ด ๋ค ๋๋๊ณ ์์ฑ๋ ์ปคํผ๊ฐ ๋ฐํ
return this.milkFrother.addMilkform(coffee);
}
}
// CoffeMachine ํด๋์ค ๋ฅผ ์์๋ฐ์ saltCoffeeMachine ํด๋์ค์ด๋ค.\\\\\\\\\\\\\\\\\\\\\\\\\\\
class saltCoffeeMachine extends CoffeMachine {
public constructor(
protected capsule: number = 3,
protected water: number = 10,
private saltTopping: SaltMixer
) {
super(capsule, water);
}
makeCoffee(orderShot: number): CoffeeCup {
const coffee = super.makeCoffee(orderShot);
return this.saltTopping.addSalt(coffee);
}
}
const machines: CoffeeMaker[] = [
new CoffeMachine(25, 25),
new CafeLatteMachine(15, 25, new CheapMilkSteamer()),
new saltCoffeeMachine(5, 10, new SaltMixer()),
];
machines.forEach((machine) => {
console.log("------------์ปคํผ๋จธ์ ๊ฐ๋~!-- ----------");
console.log(machine.makeCoffee(1));
});
<์ถ๋ ฅ ๊ฒฐ๊ณผ> awesome!! ! ์์ฃผ ์ ์๋ํ๋ค.
------------์ปคํผ๋จธ์ ๊ฐ๋~!------------
์บก์์ฐ๊ธฐ ์ 25
์บก์์ฐ๋ฌ์์ต๋๋ค.
์บก์ ์ฐ๊ณ ๋ ํ22
๋ฌผ์ฐ๊ธฐ ์ 25
๋ฌผ์ฐ๋ฌ์์ต๋๋ค.
๋ฌผ์ฐ๊ณ ๋ ํ 15
๋ฌผ ๋์ด๋ ์ค...
{ orderShot: 1, hasMilk: false }
------------์ปคํผ๋จธ์ ๊ฐ๋~!------------
์บก์์ฐ๊ธฐ ์ 15
์บก์์ฐ๋ฌ์์ต๋๋ค.
์บก์ ์ฐ๊ณ ๋ ํ12
๋ฌผ์ฐ๊ธฐ ์ 25
๋ฌผ์ฐ๋ฌ์์ต๋๋ค.
๋ฌผ์ฐ๊ณ ๋ ํ 15
๋ฌผ ๋์ด๋ ์ค...
๋ชฝ๊ธ๋ชฝ๊ธ ํฌ์ญํฌ์ญํ ์ฐ์ ํผ
{ orderShot: 1, hasMilk: true }
------------์ปคํผ๋จธ์ ๊ฐ๋~!------------
์บก์์ฐ๊ธฐ ์ 5
์บก์์ฐ๋ฌ์์ต๋๋ค.
์บก์ ์ฐ๊ณ ๋ ํ2
๋ฌผ์ฐ๊ธฐ ์ 10
๋ฌผ์ฐ๋ฌ์์ต๋๋ค.
๋ฌผ์ฐ๊ณ ๋ ํ 0
๋ฌผ ๋์ด๋ ์ค...
์๊ธ์ ํ๊ผฌ์ง ์ง์๋ค
{ orderShot: 1, hasMilk: false, hasSalt: true }
์ด๋ ๊ฒ ๊ฐ๊ฐ์ ํด๋์ค์์ ํ์ํ ๊ฒ์ ๋งค๋ฒ ์ถ๊ฐํ๊ธฐ ์ํด ๋งค๋ฒ ์์์ ํด์ ์๋ก์ด ์์๋ค์ ์ง์ด๋ฃ๋ ํด๋์ค๋ฅผ ๋ง๋ค์ด๋ด๊ธฐ ๋ณด๋ค๋
๊ฐ๊ฐ ํ์ํ ๊ธฐ๋ฅ์ด ์๋ค๊ณ ์๊ฐ์ด ๋ค๋ฉด(์ด ์์ ์์๋ ์ฐ์ ๊ฑฐํ๊ธฐํด๋์ค์ ์๊ธ๋ฏน์ ํด๋์ค)
์ด๋ ๊ฒ ํด๋์ค๋ก ๋ฐ๋ก ๋ง๋ค์ด๋ ์ผ๋ก์จ ํ์ํ ๊ณณ์ ๊ฐ์ ธ๋ค๊ฐ ๋ถ์ฌ์ฐ๋!
์ปดํฌ์ง์ ์ผ๋ก ๊ตฌํํ๋ ๊ฒ์ด ์ค๊ณ์ ๋ ์ด๋ก์ด ์ ์ด ์๋ค.(์์์ ๋จ๋ฐ๋ณด๋ค๋)
์ด๊ฑธ๋ก ์ ์ผ ์ฒ์์ ๋ง๋ค๊ณ ์ถ์๋
์๊ธ์นดํ๋ผํ ์ปคํผ๋จธ์ ํด๋์ค๋ฅผ ๋ง๋ค์ด๋ณผ๊น?
// ์ฒ์๋ถํฐ ๋ง๋ค๊ณ ์ถ์๋ saltCafeLatteCoffeeMachine ํด๋์ค์ด๋ค.\\\\\\\\\\\\\\\\\\\\\\\\\\\
class saltCafeLatteCoffeeMachine extends CoffeMachine {
public constructor(
protected capsule: number = 3,
protected water: number = 10,
private saltTopping: SaltMixer,
private milkFrother: CheapMilkSteamer
) {
super(capsule, water);
}
makeCoffee(orderShot: number): CoffeeCup {
let coffee = super.makeCoffee(orderShot);
coffee = this.saltTopping.addSalt(coffee);
return this.milkFrother.addMilkform(coffee);
}
์ด์ ! ๋ถํ ๋ถ์ด๋ฏ์ด ํด๋์ค๋ฅผ ๋์ฑ ์ฝ๊ฒ์ฐ์ด๋ผ ์ ์๊ฒ ๋์๋ค~!!!
์ ํด๋์ค๋ฅผ ๋ ๊ฐ๊ฒฐํ๊ฒ ์ฐ๋ฉด ๋ค์๊ณผ ๊ฐ๋ค
// ๋ง๋ค๊ณ ์ถ์๋ saltCafeLatteCoffeeMachine ํด๋์ค์ด๋ค.\\\\\\\\\\\\\\\\\\\\\\\\\\\
class saltCafeLatteCoffeeMachine extends CoffeMachine {
public constructor(
protected capsule: number = 3,
protected water: number = 10,
private saltTopping: SaltMixer,
private milkFrother: CheapMilkSteamer
) {
super(capsule, water);
}
makeCoffee(orderShot: number): CoffeeCup {
let coffee = super.makeCoffee(orderShot);
return this.milkFrother.addMilkform(this.saltTopping.addSalt(coffee));
}
}
์ด๊ฑธ ๋ ์ธ๋ จ๋๊ฒ ์์ฑํ๊ณ ์ถ๋ค๋ฉด ์๋์ ๊ฐ์ด ์ ๋ฆฌํด์ค ์ ์๋ค.
// CoffeMachine ํด๋์ค๋ฅผ ์์๋ฐ์ CafeLatteMachine ํด๋์ค์ด๋ค.\\\\\\\\\\\\\\\\\\\\\\\\\\\\
class CafeLatteMachine extends CoffeMachine {
public constructor(
protected capsule: number = 3,
protected water: number = 10,
private milkFrother: CheapMilkSteamer
) {
super(capsule, water);
}
makeCoffee(orderShot: number): CoffeeCup {
const coffee = super.makeCoffee(orderShot); //๊ธฐ๋ณธ์ ์ธ ์บก์ ์๋น, ๋ฌผ ์๋น, ๋ฌผ ๊ฐ์ด ๋ค ๋๋๊ณ ์์ฑ๋ ์ปคํผ๊ฐ ๋ฐํ
const saltAdded = this.milkFrother.addMilkform(coffee);
return this.milkFrother.addMilkform(saltAdded);
}
}
<์ต์ข ์ ์ฒด ์ฝ๋>
// //์ปคํผ๋จธ์ ๋ง๋ค๊ธฐ
type CoffeeCup = {
orderShot: number;
hasMilk?: boolean;
hasSalt?: boolean;
};
interface CoffeeMaker {
makeCoffee(shots: number): CoffeeCup;
}
//์ผ๋ฐ ์ฐ์ ๊ฑฐํ๊ธฐ//////////////////////////////////////////////////////////////////////
class CheapMilkSteamer {
//(1)์ฐ์ ๊ฑฐํ์ ๋ง๋๋ ๋ด๋ถ ํจ์
private steamMilk(): void {
console.log("๋ชฝ๊ธ๋ชฝ๊ธ ํฌ์ญํฌ์ญํ ์ฐ์ ํผ");
}
//(2)๋ง๋ค์ด์ง ์ปคํผ์ปต์ ๋ฐ์์, ๋ง๋ค์ด์ง ์ฐ์ ๊ฑฐํ(1)์ ๋ถ์ด์ ์ด ์ปคํผ์ปต์ ๋ค์ ๋ฆฌํดํ๋ ํจ์
addMilkform(cup: CoffeeCup): CoffeeCup {
this.steamMilk();
return {
...cup,
hasMilk: true,
};
}
}
//์๊ธ๋ฏน์///////////////////////////////////////////////////////////////////////////////
class SaltMixer {
//(1)์๊ธ์ ์ง๋ ๋ด๋ถ ํจ์
private getSalt() {
console.log("์๊ธ์ ํ๊ผฌ์ง ์ง์๋ค");
return true;
}
//(2)๋ง๋ค์ด์ง ์ปคํผ์ปต์ ๋ฐ์์, ์ง์ ์๊ธ(1)์ ๋ฟ๋ ค์ ์ด ์ปคํผ์ปต์ ๋ค์ ๋ฆฌํดํ๋ ํจ์
addSalt(cup: CoffeeCup): CoffeeCup {
const salt = this.getSalt();
return {
...cup,
hasSalt: salt,
};
}
}
// CoffeeMaker ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ CoffeMachine ํด๋์ค์ด๋ค.////////////////////////////
class CoffeMachine implements CoffeeMaker {
protected static ONE_SHOT_CAPSULE: number = 3;
protected static ONE_CUP_WATER: number = 10;
static makeMachine(beans: number, water: number): CoffeMachine {
return new CoffeMachine(beans, water);
}
public constructor(
protected capsule: number = 3,
protected water: number = 10
) {}
makeCoffee(orderShot: number): CoffeeCup {
this.checkMaterials(orderShot);
console.log("์บก์์ฐ๊ธฐ ์ " + this.capsule);
this.useCapsule(orderShot);
console.log("๋ฌผ์ฐ๊ธฐ ์ " + this.water);
this.useWater();
this.heatWater();
return this.extract(orderShot);
}
protected checkMaterials(orderShot: number) {
if (this.capsule < orderShot * CoffeMachine.ONE_SHOT_CAPSULE) {
throw new Error(`์ปคํผ์บก์ ๋ถ์กฑ!!! ํ์ฌ ๋ณด์ ์บก์ : ${this.capsule}`);
}
if (this.water < CoffeMachine.ONE_CUP_WATER) {
throw new Error(`๋ฌผ ๋ถ์กฑ!!! ๋จ์ ๋ฌผ ${this.water}`);
}
}
clean(): void {
console.log("์ปคํผ๋จธ์ ํ๊ตฌ๋ ์ค!");
}
private extract(shots: number): CoffeeCup {
return {
orderShot: shots,
hasMilk: false,
};
}
private heatWater() {
console.log("๋ฌผ ๋์ด๋ ์ค...");
}
private useCapsule(shot: number) {
console.log("์บก์์ฐ๋ฌ์์ต๋๋ค.");
this.capsule -= shot * CoffeMachine.ONE_SHOT_CAPSULE;
console.log("์บก์ ์ฐ๊ณ ๋ ํ" + this.capsule);
}
protected useWater() {
console.log("๋ฌผ์ฐ๋ฌ์์ต๋๋ค.");
this.water -= CoffeMachine.ONE_CUP_WATER;
console.log("๋ฌผ์ฐ๊ณ ๋ ํ " + this.water);
}
addCapsule(capsule: number) {
this.capsule += capsule;
}
fulfillWater(water: number) {
this.water += water;
}
get Water(): number {
return this.water;
}
get Capsule(): number {
return this.capsule;
}
}
// CoffeMachine ํด๋์ค๋ฅผ ์์๋ฐ์ CafeLatteMachine ํด๋์ค์ด๋ค.\\\\\\\\\\\\\\\\\\\\\\\\\\\\
class CafeLatteMachine extends CoffeMachine {
public constructor(
protected capsule: number = 3,
protected water: number = 10,
private milkFrother: CheapMilkSteamer
) {
super(capsule, water);
}
makeCoffee(orderShot: number): CoffeeCup {
const coffee = super.makeCoffee(orderShot); //๊ธฐ๋ณธ์ ์ธ ์บก์ ์๋น, ๋ฌผ ์๋น, ๋ฌผ ๊ฐ์ด ๋ค ๋๋๊ณ ์์ฑ๋ ์ปคํผ๊ฐ ๋ฐํ
return this.milkFrother.addMilkform(coffee);
}
}
// CoffeMachine ํด๋์ค ๋ฅผ ์์๋ฐ์ saltCoffeeMachine ํด๋์ค์ด๋ค.\\\\\\\\\\\\\\\\\\\\\\\\\\\
class saltCoffeeMachine extends CoffeMachine {
public constructor(
protected capsule: number = 3,
protected water: number = 10,
private saltTopping: SaltMixer
) {
super(capsule, water);
}
makeCoffee(orderShot: number): CoffeeCup {
const coffee = super.makeCoffee(orderShot);
return this.saltTopping.addSalt(coffee);
}
}
// ๋ง๋ค๊ณ ์ถ์๋ saltCafeLatteCoffeeMachine ํด๋์ค์ด๋ค.\\\\\\\\\\\\\\\\\\\\\\\\\\\
class saltCafeLatteCoffeeMachine extends CoffeMachine {
public constructor(
protected capsule: number = 3,
protected water: number = 10,
private saltTopping: SaltMixer,
private milkFrother: CheapMilkSteamer
) {
super(capsule, water);
}
makeCoffee(orderShot: number): CoffeeCup {
const coffee = super.makeCoffee(orderShot);
const saltAdded = this.saltTopping.addSalt(coffee);
return this.milkFrother.addMilkform(saltAdded);
}
}
const machines: CoffeeMaker[] = [
new CoffeMachine(25, 25),
new CafeLatteMachine(15, 25, new CheapMilkSteamer()),
new saltCoffeeMachine(5, 10, new SaltMixer()),
new saltCafeLatteCoffeeMachine(
5,
10,
new SaltMixer(),
new CheapMilkSteamer()
),
];
machines.forEach((machine) => {
console.log("------------์ปคํผ๋จธ์ ๊ฐ๋~!-- ----------");
console.log(machine.makeCoffee(1));
});
<์ถ๋ ฅ ๊ฒฐ๊ณผ>
------------์ปคํผ๋จธ์ ๊ฐ๋~!-- ----------
์บก์์ฐ๊ธฐ ์ 25
์บก์์ฐ๋ฌ์์ต๋๋ค.
์บก์ ์ฐ๊ณ ๋ ํ22
๋ฌผ์ฐ๊ธฐ ์ 25
๋ฌผ์ฐ๋ฌ์์ต๋๋ค.
๋ฌผ์ฐ๊ณ ๋ ํ 15
๋ฌผ ๋์ด๋ ์ค...
{ orderShot: 1, hasMilk: false }
------------์ปคํผ๋จธ์ ๊ฐ๋~!-- ----------
์บก์์ฐ๊ธฐ ์ 15
์บก์์ฐ๋ฌ์์ต๋๋ค.
์บก์ ์ฐ๊ณ ๋ ํ12
๋ฌผ์ฐ๊ธฐ ์ 25
๋ฌผ์ฐ๋ฌ์์ต๋๋ค.
๋ฌผ์ฐ๊ณ ๋ ํ 15
๋ฌผ ๋์ด๋ ์ค...
๋ชฝ๊ธ๋ชฝ๊ธ ํฌ์ญํฌ์ญํ ์ฐ์ ํผ
{ orderShot: 1, hasMilk: true }
------------์ปคํผ๋จธ์ ๊ฐ๋~!-- ----------
์บก์์ฐ๊ธฐ ์ 5
์บก์์ฐ๋ฌ์์ต๋๋ค.
์บก์ ์ฐ๊ณ ๋ ํ2
๋ฌผ์ฐ๊ธฐ ์ 10
๋ฌผ์ฐ๋ฌ์์ต๋๋ค.
๋ฌผ์ฐ๊ณ ๋ ํ 0
๋ฌผ ๋์ด๋ ์ค...
์๊ธ์ ํ๊ผฌ์ง ์ง์๋ค
{ orderShot: 1, hasMilk: false, hasSalt: true }
------------์ปคํผ๋จธ์ ๊ฐ๋~!-- ----------
์บก์์ฐ๊ธฐ ์ 5
์บก์์ฐ๋ฌ์์ต๋๋ค.
์บก์ ์ฐ๊ณ ๋ ํ2
๋ฌผ์ฐ๊ธฐ ์ 10
๋ฌผ์ฐ๋ฌ์์ต๋๋ค.
๋ฌผ์ฐ๊ณ ๋ ํ 0
๋ฌผ ๋์ด๋ ์ค...
์๊ธ์ ํ๊ผฌ์ง ์ง์๋ค
๋ชฝ๊ธ๋ชฝ๊ธ ํฌ์ญํฌ์ญํ ์ฐ์ ํผ
{ orderShot: 1, hasMilk: true, hasSalt: true }
<์ต์ข ์์ฝ>
์ด ํด๋์ค๋ ์ฐ์ ๋ฅผ ์ด๋ป๊ฒ ๋ง๋๋์ง ์๊ธ์ ์ด๋ป๊ฒ ์ฒ๋ฆฌ๋๋๊ฑด์ง ์ ํ ์ ๊ฒฝ์ฐ์ง ์๋๋ค.
๋ค๋ง ํ์ํ ๊ธฐ๋ฅ์ ๊ฐ์ ธ์์ ์ธ๋ถ์์ ์ฃผ์ ๋ฐ์์ผ๋ก์จ
์ปดํฌ์ง์ ์ ์ด์ฉํด์ ํ์ํ ๊ธฐ๋ฅ์ ๊ณ์ ์ฌ์ฌ์ฉํ ์ ์๋ค.
์ด๋ฌํ ์ปดํฌ์ง์ ์ ์ฝ๋์ ์ฌ์ฌ์ฉ์ฑ์ ๊ต์ฅํ ๋์ฌ์ฃผ๋ ํจ๊ณผ๊ฐ ์๋ค.
๋ค๋ง, ์น๋ช ์ ์ธ ๋จ์ ์ด ์๋๋ฐ ์ฐ์ ๊ฑฐํ๊ธฐ ํด๋์ค, ์๊ธ๋ฏน์ํด๋์ค, ์นดํ๋ผ๋ผ๋จธ์ , ์๊ธ์ปคํผ๋จธ์ , ์๊ธ์นดํ๋ผ๋ผ๋จธ์ ์ด๋ ๊ฒ ์ฐ๊ฒฐ๋ ํด๋์ค๋ค ๊ฐ์ ์ปคํ๋ง์ด ๋๋ฌด ์ฌํ๊ณ ๊ฐ๋ ฅํ ์ ์ด ๋ฐ๋ก ๊ทธ๊ฒ์ด๋ค.
๋ฐ๋ก ๊ทธ ์ฐ์ ๊ฑฐํ๊ธฐ ํด๋์ค์ ๋ฐ๋ก ๊ทธ ์๊ธ๋ฏน์ ํด๋์ค๋ง์ ์ฌ์ฉํด์ผํ๋ฉฐ(ํญ์ ์ค์ค๋ก๋ฅผ ์ ์ฝ),
๋ค๋ฅธ ๊ณ ๊ธ์ฐ์ ๊ฑฐํ๊ธฐํด๋์ค ์คํ๋ฏน์ ํด๋์ค ๋ฑ์ผ๋ก ํ๊ณ ์ถ๋ค๋ฉด ํด๋์ค ๋ด์ฉ์ ๋ค์ ๊ณ ์ณ์ผํ๋ค.
์ด๋ ๊ฒ ํด๋์ค ๊ฐ ์๋ก ๋๋ฌด ์์๊ณ ์๋ ๊ด๊ณํ์ฑ์ ๊ฑด๊ฐํ์ง ๋ชปํ๋ค.
ํจ์๋ฅผ ์ ๊ณตํ๋ ํด๋์ค๋ฅผ ๋ฐ๊พธ๋ฉด ๊ทธ ํด๋์ค๋ฅผ ์ฌ์ฉํ๊ณ ์ปคํ๋ง์ด ๊ฐํ ๋ชจ๋ ํด๋์ค๋ค์ ์ ๋ฐ์ดํธํด์ผํ๋ ๋ฌธ์ ๊ฐ ์๋ค.
๊ทธ๋ ๋ค๋ฉด ์ด๋ฌํ ํด๋์ค ๊ฐ ๊ฒฐํฉ๋๊ฐ ๋์ ๋ฌธ์ ์ํฉ๋ค(์ปคํ๋ง)์ ํผํ ์ ์๋๊ฐ?
๋์ฑ ํ์ฅ์ด ๊ฐ๋ฅํ๊ณ ์ ์ฐ์ฑ ๋์ ํ ํฌ๋์ interface(๋ฐ๋ก ๋ค์ ํฌ์คํ , ์ด์ ๊ด๋ จํ ์ ๋ตํจํด [ํ์ ์คํฌ๋ฆฝํธ] composition ์ฅ์ ์ ๊ทน๋ํํ๋ ๊ฐ๋ ฅํ interface — My dev Note๐ (tistory.com))๋ก ๊ตฌ์ฌํ ์ ์๋ค.