[๋””์ž์ธํŒจํ„ด] ํ…œํ”Œ๋ฆฟ ๋ฉ”์†Œ๋“œ ํŒจํ„ด (Template Method Pattern)

๋ฐ˜์‘ํ˜•
๐Ÿ’ก ํ…œํ”Œ๋ฆฟ ๋ฉ”์†Œ๋“œ ํŒจํ„ด (template method pattern)
๋ฉ”์†Œ๋“œ์—์„œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ๊ณจ๊ฒฉ์„ ์ •์˜ํ•œ๋‹ค.
์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ์—ฌ๋Ÿฌ ๋‹จ๊ณ„ ์ค‘ ์ผ๋ถ€๋Š” ์„œ๋ธŒํด๋ž˜์Šค์—์„œ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.
ํ…œํ”Œ๋ฆฟ ๋ฉ”์†Œ๋“œ๋ฅผ ์ด์šฉํ•˜๋ฉด ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ๊ตฌ์กฐ๋Š” ๊ทธ๋Œ€๋กœ ์œ ์ง€ํ•˜๋ฉด์„œ ์„œ๋ธŒํด๋ž˜์Šค์—์„œ ํŠน์ • ๋‹จ๊ณ„๋ฅผ ์žฌ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์˜ˆ์ œ)  ์ปคํ”ผ์™€ ํ‹ฐ์˜ ์ถ”์ถœ ๊ณผ์ •

์ปคํ”ผ ์ถ”์ถœ ๊ณผ์ • ํ‹ฐ ์ถ”์ถœ ๊ณผ์ •
1. ๋ฌผ์„ ๋“์ธ๋‹ค.
2. ์ปคํ”ผ๋ฅผ ์šฐ๋ ค๋‚ธ๋‹ค.
3. ์ปต์— ๋”ฐ๋ฅธ๋‹ค.
4. ์„คํƒ• ๋ฐ ์šฐ์œ ๋ฅผ ์ฒจ๊ฐ€ํ•œ๋‹ค.
1. ๋ฌผ์„ ๋“์ธ๋‹ค.
2. ํ‹ฐ๋ฅผ ์šฐ๋ ค๋‚ธ๋‹ค.
3. ์ปต์— ๋”ฐ๋ฅธ๋‹ค.
4. ๋ ˆ๋ชฌ์„ ์ฒจ๊ฐ€ํ•œ๋‹ค

์ผ๋ จ์˜ ๊ณผ์ •์ด ๊ฑฐ์˜ ๋™์ผํ•˜๋‹ค. ์ฐจ์ด๊ฐ€ ์žˆ๋Š” ๋ถ€๋ถ„๋งŒ ๊ฐ•์กฐ ํ‘œ์‹œ ํ•ด๋ณด์•˜๋‹ค.

๊ณตํ†ต๋˜๋Š” ๋ถ€๋ถ„์„ ์ถ”์ƒํ™” (abstract) ์‹œ์ผœ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด ๋ณด์ž.

// ์ถ”์ƒํด๋ž˜์Šค (abstract)
public abstract class CaffeineBeverage {
    final void prepareRecipe() {
        boilWater();	  // 1. ๋ฌผ์„ ๋“์ธ๋‹ค.
        brew();		  // 2. ๋“๋Š” ๋ฌผ์— ์šฐ๋ ค๋‚ธ๋‹ค.
        pourInCup(); 	  // 3. ์ปต์— ๋”ฐ๋ฅธ๋‹ค.
        addCondiments();  // 4. ํ–ฅ์‹ ๋ฃŒ๋ฅผ ์ฒจ๊ฐ€ํ•œ๋‹ค.
    }

    // ๋ณ€ํ•˜๋Š” ๋ถ€๋ถ„์€ ๊ฐ ํด๋ž˜์Šค์—์„œ ์žฌ์ •์˜ํ•œ๋‹ค.
    abstract void brew();
    abstract void addCondiments();

    void boilWater() {
        System.out.println("๋ฌผ์„ ๋“์ธ๋‹ค.");
    }

    void pourInCup() {
        System.out.println("์ปต์— ๋”ฐ๋ฅธ๋‹ค.");
    }
}

์œ„ ์ถ”์ƒํด๋ž˜์Šค๋ฅผ ํ™•์žฅํ•˜์—ฌ Coffee, Tea ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

// Coffee ํด๋ž˜์Šค
public class Coffee extends CaffeineBeverage {
    @Override
    public void brew() {
        System.out.println("์ปคํ”ผ๋ฅผ ์ถ”์ถœํ•œ๋‹ค.");
    }
    @Override
    public void addCondiments() {
        System.out.println("์„คํƒ•, ์šฐ์œ ๋ฅผ ์ฒจ๊ฐ€ํ•œ๋‹ค.");
    }
}

// Tea ํด๋ž˜์Šค
public class Tea extends CaffeineBeverage {
    @Override
    public void brew() {
        System.out.println("ํ‹ฐ๋ฅผ ์šฐ๋ ค๋‚ธ๋‹ค.");
    }
    @Override
    public void addCondiments() {
        System.out.println("๋ ˆ๋ชฌ์„ ์ฒจ๊ฐ€ํ•œ๋‹ค.");
    }
}

์ถœ๋ ฅํ•ด๋ณด๊ธฐ

public class CaffeineBeverageTest {

    @Test
    void coffeeBrew() {
        CaffeineBeverage coffee = new Coffee();
        coffee.prepareRecipe();
    }

    @Test
    void teaBrew() {
        CaffeineBeverage tea = new Tea();
        tea.prepareRecipe();
    }
}

์ถœ๋ ฅ๊ฒฐ๊ณผ

1. coffeeBrew() ์ถœ๋ ฅ
๋ฌผ์„ ๋“์ธ๋‹ค.
์ปคํ”ผ๋ฅผ ์ถ”์ถœํ•œ๋‹ค.
์ปต์— ๋”ฐ๋ฅธ๋‹ค.
์„คํƒ•, ์šฐ์œ ๋ฅผ ์ฒจ๊ฐ€ํ•œ๋‹ค.

2. teaBrew() ์ถœ๋ ฅ
๋ฌผ์„ ๋“์ธ๋‹ค.
ํ‹ฐ๋ฅผ ์šฐ๋ ค๋‚ธ๋‹ค.
์ปต์— ๋”ฐ๋ฅธ๋‹ค.
๋ ˆ๋ชฌ์„ ์ฒจ๊ฐ€ํ•œ๋‹ค.

Template Method ๋กœ ์–ป์„ ์ˆ˜ ์žˆ์—ˆ๋˜ ๊ฒƒ

๋ณ€ํ•˜๋Š” ๊ฒƒ๊ณผ ๋ณ€ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์„ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

  1. CaffeineBevarage ํด๋ž˜์Šค์—์„œ ์ž‘์—…์„ ์ฒ˜๋ฆฌ, ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๋…์ ํ•œ๋‹ค.
  2. CaffeineBevarage ๋•๋ถ„์— ์„œ๋ธŒํด๋ž˜์Šค์—์„œ ์ฝ”๋“œ๊ฐ€ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค.
  3. ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด ํ…œํ”Œ๋ฆฟ ํ•˜๋‚˜์— ์กด์žฌํ•˜๋ฏ€๋กœ ํ•œ ๋ถ€๋ถ„๋งŒ ๊ณ ์น˜๋ฉด ๋œ๋‹ค.
  4. ๋‹ค๋ฅธ ์Œ๋ฃŒ๋„ ์‰ฝ๊ฒŒ ์ถ”๊ฐ€๊ฐ€๋Šฅํ•œ Framework๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
  5. ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ ํ…œํ”Œ๋ฆฟ์— ์กด์žฌํ•˜๋ฉฐ, ์„œ๋ธŒํด๋ž˜์Šค์—์„œ๋Š” ๊ตฌํ˜„๋งŒ ํ•œ๋‹ค.

Template Method์™€ Hook

ํ›„ํฌ(hooks)๋ž€, ์ถ”์ƒํด๋ž˜์Šค์—์„œ ์„ ์–ธ๋˜๋Š” ๋ฉ”์†Œ๋“œ์ด๊ธด ํ•˜์ง€๋งŒ ๊ธฐ๋ณธ์ ์ธ ๋‚ด์šฉ๋งŒ ๊ตฌํ˜„๋˜์–ด ์žˆ๊ฑฐ๋‚˜ ์•„๋ฌด ์ฝ”๋“œ๋„ ๋“ค์–ด์žˆ์ง€ ์•Š์€ ๋ฉ”์†Œ๋“œ์ด๋‹ค. ์„œ๋ธŒํด๋ž˜์Šค๋Š” ์˜ค๋ฒ„๋ผ์ด๋“œํ•˜์—ฌ ๋‹ค์–‘ํ•œ ์œ„์น˜์— ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค.

// ์ถ”์ƒํด๋ž˜์Šค์— hook ์ถ”๊ฐ€
public abstract class CaffeinBeverage {
    final void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        // hook
        if(customerWantsCondiments()){
            addCondiments();
        }
    }
    abstract void brew();
    abstract void addCondiments();
	
    void boilWater() {
        System.out.println("๋ฌผ์„ ๋“์ธ๋‹ค.");
    }
    void pourInCup() {
        System.out.println("์ปต์— ๋”ฐ๋ฅธ๋‹ค.");
    }
	
    // ์ด ๋ฉ”์†Œ๋“œ๋Š” ์„œ๋ธŒํด๋ž˜์Šค์—์„œ ํ•„์š”์—๋”ฐ๋ผ ์˜ค๋ฒ„๋ผ์ด๋“œํ•˜์—ฌ ์‚ฌ์šฉ๊ฐ€๋Šฅ
    boolean customerWantsCondiments() {
        // hook : ์•„๋ฌด๊ฒƒ๋„ ์ •์˜๋˜์žˆ์ง€ ์•Š๊ณ  ํ•„์š”์—๋”ฐ๋ผ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
        return true;
    }
}

ํ• ๋ฆฌ์šฐ๋“œ ์›์น™๊ณผ ํ…œํ”Œ๋ฆฟ ๋ฉ”์†Œ๋“œ ํŒจํ„ด

๐Ÿ“Œ ํ• ๋ฆฌ์šฐ๋“œ ์›์น™
๋จผ์ € ์—ฐ๋ฝํ•˜์ง€ ๋งˆ์„ธ์š”. ์ €ํฌ๊ฐ€ ์—ฐ๋ฝ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

ํ…œํ”Œ๋ฆฟ ๋ฉ”์†Œ๋“œ ํŒจํ„ด์€, ์˜์กด์„ฑ ๋ถ€ํŒจ(dependency rot)๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

์˜์กด์„ฑ ๋ถ€ํŒจ๋ž€?
์–ด๋–ค ๊ณ ์ˆ˜์ค€ ๊ตฌ์„ฑ์š”์†Œ๊ฐ€ ์ €์ˆ˜์ค€ ๊ตฌ์„ฑ์š”์†Œ์— ์˜์กดํ•˜๊ณ , ๊ทธ ์ €์ˆ˜์ค€ ๊ตฌ์„ฑ์š”์†Œ๋Š” ๋‹ค์‹œ ๊ณ ์ˆ˜์ค€ ๊ตฌ์„ฑ์š”์†Œ์— ์˜์กดํ•˜๊ณ , ๊ทธ ๊ณ ์ˆ˜์ค€ ๊ตฌ์„ฑ์š”์†Œ๋Š” ๋‹ค์‹œ ๋˜ ๋‹ค๋ฅธ ๊ตฌ์„ฑ์š”์†Œ์— ์˜์กดํ•˜๊ณ ... ์ด๋ ‡๊ฒŒ ์˜์กด์„ฑ์ด ๋ณต์žกํ•˜๊ฒŒ ๊ผฌ์ธ ์ƒํƒœ๋ฅผ ๋งํ•œ๋‹ค.

ํ• ๋ฆฌ์šฐ๋“œ ์›์น™์€ ์ €์ˆ˜์ค€ ๊ตฌ์„ฑ์š”์†Œ์—์„œ ์‹œ์Šคํ…œ์— ์ ‘์† ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์–ธ์ œ ์–ด๋–ค์‹์œผ๋กœ ๊ทธ ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ• ์ง€๋Š” ๊ณ ์ˆ˜์ค€ ๊ตฌ์„ฑ์š”์†Œ์—์„œ ๊ฒฐ์ •ํ•˜๊ฒŒ ๋œ๋‹ค. ์ฆ‰, ์ €์ˆ˜์ค€ ๊ตฌ์„ฑ์š”์†Œ๋Š” ์ปดํ“จํ…Œ์ด์…˜์— ์ฐธ์—ฌํ•  ์ˆ˜๋Š” ์žˆ์ง€๋งŒ ์ ˆ๋Œ€ ๊ณ ์ˆ˜์ค€ ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•˜๋ฉด ์•ˆ๋œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

UML Class Diagram

GOF ๋””์ž์ธ ํŒจํ„ด์˜ ์ •์˜

ํ…œํ”Œ๋ฆฟ ๋ฉ”์†Œ๋“œ ๋””์ž์ธ ํŒจํ„ด์˜ ๋ชฉ์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.
"์ž‘์—…์—์„œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ๊ณจ๊ฒฉ์„ ์ •์˜ํ•˜๊ณ  ์ผ๋ถ€ ๋‹จ๊ณ„๋ฅผ ํ•˜์œ„ ํด๋ž˜์Šค๋กœ ์—ฐ๊ธฐํ•ฉ๋‹ˆ๋‹ค. ํ…œํ”Œ๋ฆฟ ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•˜์œ„ ํด๋ž˜์Šค๊ฐ€ ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ๊ตฌ์กฐ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ ๋„ ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ํŠน์ • ๋‹จ๊ณ„๋ฅผ ์žฌ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค."

์ฆ‰, ๋ถ€๋ชจ ํด๋ž˜์Šค์— ๋กœ์ง์˜ ๊ตฌ์กฐ๋ฅผ ํ…œํ”Œ๋ฆฟ์œผ๋กœ ์ •์˜ํ•˜๊ณ , ๋ณ€๊ฒฝ๋˜๋Š” ๋กœ์ง์€ ์ž์‹ ํด๋ž˜์Šค์— ์ •์˜ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

์ด๋กœ์จ ์ž์‹ ํด๋ž˜์Šค๊ฐ€ ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ์ „์ฒด ๊ตฌ์กฐ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€์•Š๊ณ  ๋ณ€๊ฒฝ์ด ๋˜๋Š” ํŠน์ • ๋กœ์ง๋งŒ ์žฌ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

์ƒ์†๊ณผ ์˜ค๋ฒ„๋ผ์ด๋”ฉ์„ ์ด์šฉํ•œ ๋‹คํ˜•์„ฑ์„ ํ™œ์šฉํ•˜๋Š” ๊ฒƒ์ด๋‹ค.  

 

ํ…œํ”Œ๋ฆฟ ๋ฉ”์†Œ๋“œ ํŒจํ„ด์ด ๊ฐ€์ง€๋Š” ๋ฌธ์ œ

ํ…œํ”Œ๋ฆฟ ๋ฉ”์†Œ๋“œ ํŒจํ„ด์€ ์ƒ์†์„ ์‚ฌ์šฉํ•œ๋‹ค. ์ฆ‰, ์ƒ์†์˜ ๋‹จ์ ์„ ๋ชจ๋‘ ๊ฐ€์ง„๋‹ค.

์ž์‹(ํ•˜์œ„) ํด๋ž˜์Šค๊ฐ€ ๋ถ€๋ชจ(์ƒ์œ„) ํด๋ž˜์Šค์™€ ์ปดํŒŒ์ผ ์‹œ์ ์— ๊ฐ•ํ•˜๊ฒŒ ๊ฒฐํ•ฉ๋˜๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค. ์˜์กด๊ด€๊ณ„์— ๋Œ€ํ•œ ๋ฌธ์ œ์ธ๋ฐ, ํ•˜์œ„ ํด๋ž˜์Šค๋Š” ์ƒ์œ„ ํด๋ž˜์Šค์˜ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋ฐ๋„ ๋ถ€๋ชจ ํด๋ž˜์Šค๋ฅผ ์ƒ์† ๋ฐ›๊ณ  ์žˆ๋‹ค.

์ƒ์† ๋ฐ›๋Š” ๋‹ค๋Š” ๊ฒƒ์€ ์˜์กดํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค. ์‚ฌ์šฉํ•˜๋“  ์‚ฌ์šฉํ•˜์ง€์•Š๋“  ๋ถ€๋ชจ ํด๋ž˜์Šค๋ฅผ ๊ฐ•ํ•˜๊ฒŒ ์˜์กดํ•œ๋‹ค.

 

๋ถ€๋ชจ ํด๋ž˜์Šค์— ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ธฐ๋Šฅ์ด ์žˆ๋Š”๋ฐ๋„ ๋ถ€๋ชจ ํด๋ž˜์Šค์— ์˜์กดํ•˜๋Š” ๊ฒƒ์ด ์ข‹์€ ์„ค๊ณ„์ผ๊นŒ?

์ƒ๊ฐํ•ด๋ณด๋ฉด ๋ถ€๋ชจ ํด๋ž˜์Šค๋ฅผ ์ˆ˜์ •ํ•˜๋ฉด ์ž์‹ ํด๋ž˜์Šค์—๋„ ์˜ํ–ฅ์„ ๋ผ์นœ๋‹ค. ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ธฐ๋Šฅ ๋•Œ๋ฌธ์— ์˜ํ–ฅ์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด๋‹ค. ์ด๋Š” ์ข‹์€ ์„ค๊ณ„๋ผ ๋ณผ ์ˆ˜ ์—†๋‹ค.

 

๋˜, ์ƒ์†์„ ์‚ฌ์šฉํ•˜๊ธฐ๋•Œ๋ฌธ์— ๋ณ„๋„์˜ ์„œ๋ธŒ ํด๋ž˜์Šค๋‚˜ ์ต๋ช… ๋‚ด๋ถ€ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

ํ•œ๋‘๊ฐœ ์ •๋„๋ฉด ๊ดœ์ฐฎ์„์ง€ ๋ชฐ๋ผ๋„ ๊ทธ ์ˆ˜๊ฐ€ ๋งŽ๋‹ค๋ฉด ๋งค์šฐ ๋ณต์žกํ•ด์ง„๋‹ค.

 

ํ…œํ”Œ๋ฆฟ ๋ฉ”์†Œ๋“œ ํŒจํ„ด๊ณผ ๋น„์Šทํ•œ ์—ญํ• ์„ ํ•˜๋ฉด์„œ ์ƒ์†์˜ ๋‹จ์ ์„ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ๋Š” ๋””์ž์ธ ํŒจํ„ด์ด ์ „๋žต ํŒจํ„ด (Strategy Pattern, ์ŠคํŠธ๋ž˜ํ‹ฐ์ง€ ํŒจํ„ด) ์ด๋‹ค.


์ฐธ๊ณ 
ํ—ค๋“œ ํผ์ŠคํŠธ ๋””์ž์ธ ํŒจํ„ด (Head First Design Patterns) - ์—๋ฆญ ํ”„๋ฆฌ๋จผ,์—˜๋ฆฌ์ž๋ฒ ์Šค ๋กญ์Šจ,์ผ€์ด์‹œ ์‹œ์—๋ผ,๋ฒ„ํŠธ ๋ฒ ์ด์ธ 

๋ฐ˜์‘ํ˜•