๐ ํ ํ๋ฆฟ ๋ฉ์๋ ํจํด์ด๋?
ํ ํ๋ฆฟ ๋ฉ์๋ ํจํด(Template Method Pattern)์ ํ๋ ํจํด(Behavioral Pattern) ์ค ํ๋๋ก, ์๊ณ ๋ฆฌ์ฆ์ ๊ณจ๊ฒฉ์ ์์ ํด๋์ค์์ ์ ์ํ๊ณ ๊ตฌ์ฒด์ ์ธ ๋จ๊ณ๋ค์ ํ์ ํด๋์ค์์ ๊ตฌํํ๋๋ก ํ๋ ํจํด์ ๋๋ค.
์ด ํจํด์ ํต์ฌ์ ์๊ณ ๋ฆฌ์ฆ์ ๊ตฌ์กฐ๋ ๋ณ๊ฒฝํ์ง ์์ผ๋ฉด์, ์๊ณ ๋ฆฌ์ฆ์ ํน์ ๋จ๊ณ๋ค์ ํ์ ํด๋์ค์์ ์ฌ์ ์ํ ์ ์๋๋ก ํ๋ ๊ฒ์ ๋๋ค.
๐๏ธ ํจํด์ ๊ตฌ์กฐ
ํ ํ๋ฆฟ ๋ฉ์๋ ํจํด์ ๋ค์๊ณผ ๊ฐ์ ๊ตฌ์กฐ๋ก ์ด๋ฃจ์ด์ ธ ์์ต๋๋ค:
- AbstractClass (์ถ์ ํด๋์ค): ํ ํ๋ฆฟ ๋ฉ์๋์ ์ถ์ ๋ฉ์๋๋ค์ ์ ์
- ConcreteClass (๊ตฌ์ฒด ํด๋์ค): ์ถ์ ๋ฉ์๋๋ค์ ๊ตฌ์ฒด์ ์ผ๋ก ๊ตฌํ
๐จ ์ฅ์ ๊ณผ ๋จ์
โ ์ฅ์
- ์ฝ๋ ์ฌ์ฌ์ฉ์ฑ ํฅ์: ๊ณตํต๋ ์๊ณ ๋ฆฌ์ฆ ๊ตฌ์กฐ๋ฅผ ์ฌ์ฌ์ฉํ ์ ์์ต๋๋ค
- ์ผ๊ด์ฑ ๋ณด์ฅ: ์๊ณ ๋ฆฌ์ฆ์ ์ ์ฒด์ ์ธ ํ๋ฆ์ด ์ผ์ ํ๊ฒ ์ ์ง๋ฉ๋๋ค
- ์ ์ง๋ณด์ ์ฉ์ด: ๊ณตํต ๋ก์ง์ ๋ณ๊ฒฝ์ด ํ์ํ ๋ ์์ ํด๋์ค๋ง ์์ ํ๋ฉด ๋ฉ๋๋ค
โ ๋จ์
- ์์ ์์กด์ฑ: ์์ ๊ด๊ณ์ ๊ฐํ๊ฒ ์์กดํฉ๋๋ค
- ์ ์ฐ์ฑ ์ ํ: ์๊ณ ๋ฆฌ์ฆ์ ๊ตฌ์กฐ๋ฅผ ๋ณ๊ฒฝํ๊ธฐ ์ด๋ ต์ต๋๋ค
๐ Java ์์ : ๋ฒ์ค ์ดํ ์์คํ
๋ฒ์ค ์ดํ ๊ณผ์ ์ ํ ํ๋ฆฟ ๋ฉ์๋ ํจํด์ผ๋ก ๊ตฌํํด๋ณด๊ฒ ์ต๋๋ค.
// ์ถ์ ํด๋์ค: ๋ฒ์ค ์ดํ์ ํ
ํ๋ฆฟ ์ ์
public abstract class Bus {
// ํ
ํ๋ฆฟ ๋ฉ์๋: ๋ฒ์ค ์ดํ์ ์ ์ฒด์ ์ธ ํ๋ฆ ์ ์
public final void operate() {
startEngine();
checkSafety();
announceRoute();
driveRoute();
collectFare();
stopEngine();
}
// ๊ณตํต ๋ฉ์๋๋ค
private void startEngine() {
System.out.println("๐ฅ ์์ง์ ์์ํฉ๋๋ค.");
}
private void stopEngine() {
System.out.println("๐ ์์ง์ ์ ์งํฉ๋๋ค.");
}
// ์ถ์ ๋ฉ์๋๋ค - ํ์ ํด๋์ค์์ ๊ตฌํ
protected abstract void checkSafety();
protected abstract void announceRoute();
protected abstract void driveRoute();
protected abstract void collectFare();
}
// ์๋ด๋ฒ์ค ๊ตฌํ
public class CityBus extends Bus {
@Override
protected void checkSafety() {
System.out.println("๐ ์๋ด๋ฒ์ค ์์ ์ ๊ฒ: ๋ฌธ ์๋, ๋ฒจ ์๋ ํ์ธ");
}
@Override
protected void announceRoute() {
System.out.println("๐ข ๋ค์ ์ ๋ฅ์ฅ์ ์์ฒญ์ญ์
๋๋ค.");
}
@Override
protected void driveRoute() {
System.out.println("๐ ์๋ด ๋๋ก๋ฅผ ์ดํํฉ๋๋ค.");
}
@Override
protected void collectFare() {
System.out.println("๐ณ ๊ตํต์นด๋๋ฅผ ํ๊ทธํด์ฃผ์ธ์.");
}
}
// ๊ณ ์๋ฒ์ค ๊ตฌํ
public class ExpressBus extends Bus {
@Override
protected void checkSafety() {
System.out.println("๐ ๊ณ ์๋ฒ์ค ์์ ์ ๊ฒ: ์ข์๋ฒจํธ, ๋น์๊ตฌ ํ์ธ");
}
@Override
protected void announceRoute() {
System.out.println("๐ข ์์ธ→๋ถ์ฐ ๊ณ ์๋ฒ์ค์
๋๋ค.");
}
@Override
protected void driveRoute() {
System.out.println("๐ฃ๏ธ ๊ณ ์๋๋ก๋ฅผ ์ดํํฉ๋๋ค.");
}
@Override
protected void collectFare() {
System.out.println("๐ซ ์น์ฐจ๊ถ์ ํ์ธํด์ฃผ์ธ์.");
}
}
// ์ฌ์ฉ ์์
public class BusExample {
public static void main(String[] args) {
System.out.println("=== ์๋ด๋ฒ์ค ์ดํ ===");
Bus cityBus = new CityBus();
cityBus.operate();
System.out.println("\n=== ๊ณ ์๋ฒ์ค ์ดํ ===");
Bus expressBus = new ExpressBus();
expressBus.operate();
}
}
โฐ TypeScript ์์ : ์๊ณ ์์คํ
๋ค์ํ ์ข ๋ฅ์ ์๊ณ๋ฅผ ํ ํ๋ฆฟ ๋ฉ์๋ ํจํด์ผ๋ก ๊ตฌํํด๋ณด๊ฒ ์ต๋๋ค.
// ์ถ์ ํด๋์ค: ์๊ณ์ ํ
ํ๋ฆฟ ์ ์
abstract class Clock {
// ํ
ํ๋ฆฟ ๋ฉ์๋: ์๊ณ ๋์์ ์ ์ฒด์ ์ธ ํ๋ฆ ์ ์
public displayTime(): void {
this.updateTime();
this.formatTime();
this.showDisplay();
this.playSound();
}
// ๊ณตํต ๋ฉ์๋
private updateTime(): void {
console.log("โฑ๏ธ ํ์ฌ ์๊ฐ์ ์
๋ฐ์ดํธํฉ๋๋ค.");
}
// ์ถ์ ๋ฉ์๋๋ค - ํ์ ํด๋์ค์์ ๊ตฌํ
protected abstract formatTime(): void;
protected abstract showDisplay(): void;
protected abstract playSound(): void;
}
// ๋์งํธ ์๊ณ ๊ตฌํ
class DigitalClock extends Clock {
protected formatTime(): void {
const now = new Date();
const timeString = now.toLocaleTimeString('ko-KR', {
hour12: false,
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
console.log(`๐ข ๋์งํธ ํฌ๋งท: ${timeString}`);
}
protected showDisplay(): void {
console.log("๐ป LED ๋์คํ๋ ์ด์ ์๊ฐ์ ํ์ํฉ๋๋ค.");
}
protected playSound(): void {
console.log("๐ ์์์! ๋์งํธ ์๋์");
}
}
// ์๋ ๋ก๊ทธ ์๊ณ ๊ตฌํ
class AnalogClock extends Clock {
protected formatTime(): void {
const now = new Date();
const hours = now.getHours() % 12;
const minutes = now.getMinutes();
console.log(`๐ ์๋ ๋ก๊ทธ ํฌ๋งท: ${hours}์ ${minutes}๋ถ`);
}
protected showDisplay(): void {
console.log("โ๏ธ ์์นจ๊ณผ ๋ถ์นจ์ผ๋ก ์๊ฐ์ ํ์ํฉ๋๋ค.");
}
protected playSound(): void {
console.log("๐ ๋ก๋ก๋ก! ์๊ณ ์ข
์๋ฆฌ");
}
}
// ์ค๋งํธ์์น ๊ตฌํ
class SmartWatch extends Clock {
protected formatTime(): void {
const now = new Date();
const timeString = now.toLocaleString('ko-KR', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
console.log(`๐ฑ ์ค๋งํธ ํฌ๋งท: ${timeString}`);
}
protected showDisplay(): void {
console.log("๐บ OLED ํ๋ฉด์ ๋ค์ํ ์ ๋ณด์ ํจ๊ป ์๊ฐ์ ํ์ํฉ๋๋ค.");
}
protected playSound(): void {
console.log("๐ต ์ฌ์ฉ์ ์ง์ ์๋ฆผ์");
}
}
// ์ฌ์ฉ ์์
class ClockExample {
static run(): void {
console.log("=== ๋์งํธ ์๊ณ ===");
const digitalClock = new DigitalClock();
digitalClock.displayTime();
console.log("\n=== ์๋ ๋ก๊ทธ ์๊ณ ===");
const analogClock = new AnalogClock();
analogClock.displayTime();
console.log("\n=== ์ค๋งํธ์์น ===");
const smartWatch = new SmartWatch();
smartWatch.displayTime();
}
}
// ์คํ
ClockExample.run();
๐ฏ ํ์ฉ ์๋๋ฆฌ์ค
ํ ํ๋ฆฟ ๋ฉ์๋ ํจํด์ ๋ค์๊ณผ ๊ฐ์ ์ํฉ์์ ์ ์ฉํฉ๋๋ค:
๐ณ ์๋ฆฌ ๋ ์ํผ ์์คํ
- ๊ธฐ๋ณธ์ ์ธ ์๋ฆฌ ๊ณผ์ (์ค๋น→์กฐ๋ฆฌ→๋ง๋ฌด๋ฆฌ)์ ๋์ผํ์ง๋ง, ๊ฐ ๋จ๊ณ์ ๊ตฌ์ฒด์ ์ธ ๋ด์ฉ์ด ๋ค๋ฅธ ๊ฒฝ์ฐ
๐ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ํ์ดํ๋ผ์ธ
- ๋ฐ์ดํฐ ์ฝ๊ธฐ→์ฒ๋ฆฌ→์ ์ฅ์ ํ๋ฆ์ ๊ฐ์ง๋ง, ๊ฐ ๋จ๊ณ์ ๊ตฌํ์ด ๋ค๋ฅธ ๊ฒฝ์ฐ
๐ฎ ๊ฒ์ ์์คํ
- ๊ฒ์์ ๊ธฐ๋ณธ ํ๋ฆ(์ด๊ธฐํ→๊ฒ์์งํ→๊ฒฐ๊ณผํ์)์ ๊ฐ์ง๋ง, ๊ฒ์๋ง๋ค ๊ตฌ์ฒด์ ์ธ ๋ก์ง์ด ๋ค๋ฅธ ๊ฒฝ์ฐ
๐ก ๋ง๋ฌด๋ฆฌ
ํ ํ๋ฆฟ ๋ฉ์๋ ํจํด์ ์๊ณ ๋ฆฌ์ฆ์ ๋ผ๋๋ฅผ ์ ๊ณตํ๋ฉด์๋ ์ธ๋ถ ๊ตฌํ์ ์ ์ฐ์ฑ์ ๋ณด์ฅํ๋ ๊ฐ๋ ฅํ ํจํด์ ๋๋ค. ์ฝ๋์ ์ค๋ณต์ ์ค์ด๊ณ ์ผ๊ด์ฑ์ ์ ์งํ๋ฉด์๋, ๊ฐ ๊ตฌํ์ฒด๊ฐ ์์ ๋ง์ ํน์ฑ์ ๊ฐ์ง ์ ์๋๋ก ํด์ค๋๋ค.
ํนํ ํ๋ ์์ํฌ ๊ฐ๋ฐ์ด๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค๊ณ์์ ์์ฃผ ์ฌ์ฉ๋๋ ํจํด์ด๋ฏ๋ก, ๊ฐ์ฒด์งํฅ ์ค๊ณ์ ํต์ฌ ๊ฐ๋ ์ค ํ๋๋ก ๊ผญ ์์๋์๊ธฐ ๋ฐ๋๋๋ค! ๐
๋๊ธ