디자읞 팹턮/GOF

💠 템플늿 메소드(Template Method) 팹턮 - 완벜 마슀터하Ʞ

읞파_ 2022. 12. 2. 09:24

Template Method Pattern
Template Method Pattern

Template Method Pattern

템플늿 메서드(Template Method) 팚턎은 여러 큎래슀에서 공통윌로 사용하는 메서드륌 템플늿화 하여 상위 큎래슀에서 정의하고, 하위 큎래슀마닀 섞부 동작 사항을 닀륎게 구현하는 팚턎읎닀.

슉, 변하지 않는 Ʞ능(템플늿)은 상위 큎래슀에 만듀얎두고 자죌 변겜되며 확장할 Ʞ능은 하위 큎래슀에서 만듀도록 하여, 상위의 메소드 싀행 동작 순서는 고정하멎서 섞부 싀행 낎용은 닀양화 될 수 있는 겜우에 사용된닀.

템플늿 메소드 팚턎은 상속읎띌는 Ʞ술을 극대화하여, 알고늬슘의 댈대륌 맞추는 것에 쎈점을 둔닀. 읎믞 수많은 프레임워크에서 많은 부분에 템플늿 메소드 팹턮 윔드가 우늬도 몚륎게 적용되얎 있닀.

디자읞 팚턎에서의 템플늿은 변하지 않는 것을 의믞한닀.

 

템플늿 메서드 팹턮 구조

Template-Method-Pattern

  • AbstractClass(추상 큎래슀) : 템플늿 메소드륌 구현하고, 템플늿 메소드에서 돌아가는 추상 메소드륌 선얞한닀. 읎 추상 메소드는 하위 큎래슀읞 ConcreteClass 역할에 의핎 구현된닀.
  • ConcreteClass(구현 큎래슀) : AbstractClass륌 상속하고 추상 메소드륌 구첎적윌로 구현한닀. ConcreteClass에서 구현한 메소드는 AbstractClass의 템플늿 메소드에서 혞출된닀.

 

hook 메서드

Template-Method-Pattern

훅(hook) 메소드는 부몚의 템플늿 메서드의 영향읎나 순서륌 제얎하고 싶을때 사용되는 메서드 형태륌 말한닀.

위의 귞늌에서 볎듯읎 템플늿 메서드 낎에 싀행되는 동작을 step2() 읎띌는 메서드의 ì°ž, 거짓 유묎에 따띌 닀음 슀텝을 얎떻게 읎얎나갈지 지정한닀. 읎륌 통핎 자식 큎래슀에서 좀더 유연하게 템플늿 메서드의 알고늬슘 로직을 닀양화 할 수 있닀는 특징읎 있닀.

훅 메소드는 추상 메소드가 아닌 음반 메소드로 구현하는데, 선택적윌로 였버띌읎드 하여 자식 큎래슀에서 제얎하거나 아니멎 놔두거나 하Ʞ 위핎서 읎닀.


템플늿 메서드 팹턮 흐멄

 

큎래슀 구성

Template-Method-Pattern

abstract class AbstractTemplate {

    // 템플늿 메소드 : 메서드 앞에 final 킀워드륌 붙읎멎 자식 큎래슀에서 였버띌읎딩읎 불가능핚.
	// 자식 큎래슀에서 상위 템플늿을 였버띌읎딩핎서 자Ʞ마음대로 바꟞도록 하는 행위륌 원천 뎉쇄
    public final void templateMethod() {
        // 상속하여 구현되멎 싀행될 메소드듀
        step1();
        step2();
        
        if(hook()) { // 안의 로직을 싀행하거나 싀행하지 않음
            // ...
        }
        
        step3();
    }

    boolean hook() {
        return true;
    }

    // 상속하여 사용할 것읎Ʞ 때묞에 protected 접귌제얎자 섀정
    protected abstract void step1();
    protected abstract void step2();
    protected abstract void step3();
}
class ImplementationA extends AbstractTemplate {

    @Override
    protected void step1() {}

    @Override
    protected void step2() {}

    @Override
    protected void step3() {}
}

class ImplementationB extends AbstractTemplate {

    @Override
    protected void step1() {}

    @Override
    protected void step2() {}

    @Override
    protected void step3() {}

    // hook 메소드륌 였버띌읎드 í•Žì„œ false로 하여 템플늿에서 마지막 로직읎 싀행되지 않도록 섀정
    @Override
    protected boolean hook() {
        return false;
    }
}

 

큎래슀 흐멄

Template-Method-Pattern

class Client {
   public static void main(String[] args) {
       // 1. 템플늿 메서드가 싀행할 구현화한 하위 알고늬슘 큎래슀 생성
       AbstractTemplate templateA = new ImplementationA();

       // 2. 템플늿 싀행
       templateA.templateMethod();
   }
}

템플늿 메소드 팹턮 특징

 

팹턮 사용 시Ʞ

  • 큎띌읎얞튞가 알고늬슘의 특정 닚계만 확장하고, 전첎 알고늬슘읎나 핎당 구조는 확장하지 않도록 할때
  • 동음한 Ʞ능은 상위 큎래슀에서 정의하멎서 확장, 변화가 필요한 부분만 하위 큎래슀에서 구현할 때

 

팹턮 장점

  • 큎띌읎얞튞가 대규몚 알고늬슘의 특정 부분만 재정의하도록 하여, 알고늬슘의 닀륞 부분에 발생하는 변겜 사항의 영향을 덜 받도록 한닀.
  • 상위 추상큎래슀로 로직을 공통화 하여 윔드의 쀑복을 쀄음 수 있닀.
  • 서람 큎래슀의 역할을 쀄읎고, 핵심 로직을 상위 큎래슀에서 ꎀ늬하므로서 ꎀ늬가 용읎핎진닀
    • 헐늬우드 원칙 (Hollywood Principle) : 고수쀀 구성요소에서 저수쀀을 닀룚는 원칙 (추상화에 의졎)

 

팹턮 닚점

  • 알고늬슘의 제공된 곚격에 의핎 유연성읎 제한될 수 있닀.
  • 알고늬슘 구조가 복잡할수록 템플늿 로직 형태륌 유지하Ʞ 얎렀워진닀.
  • 추상 메소드가 많아지멎서 큎래슀의 생성, ꎀ늬가 얎렀워질 수 있닀.
  • 상위 큎래슀에서 선얞된 추상 메소드륌 하위 큎래슀에서 구현할 때, ê·ž 메소드가 얎느 타읎밍에서 혞출되는지 큎래슀 로직을 읎핎핎알 할 필요가 있닀.
  • 로직에 변화가 생겚 상위 큎래슀륌 수정할 때, 몚든 서람 큎래슀의 수정읎 필요 할수도 있닀.
  • 하위 큎래슀륌 통핎 Ʞ볞 닚계 구현을 억제하여 늬슀윔프 치환 법칙을 위반할 여지가 있닀.

예제륌 통핎 알아볎는 Template Method íŒší„Ž

 

상황에 따띌 닀양한 연산 알고늬슘 적용

number.txt 파음에 적혀있는 숫자 값듀을 읜얎, 숫자듀을 연산한 결곌륌 알렀죌는 Ʞ능을 구현핎볎자. 

Template-Method-Pattern

 

큎늰하지 않은 묞제의 윔드 ❌

아래의 구성을 볎멎 FileProcesser 큎래슀에 process() 메서드에서 알고늬슘을 수행하는 것을 볌 수 있닀.

Client의 메읞 메소드에서 파음겜로륌 섀정핎죌고 process() 륌 싀행하멎 몚든 숫자듀을 덧셈한 결곌 15가 출력된닀.

class FileProcessor {
    private String path; // 생성자로 부터 파음겜로륌 받아와 저장

    public FileProcessor(String path) {
        this.path = path;
    }

    public int process() {
        try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
            int result = 0;
            String line = null;

            // 파음에 있는 각 띌읞에 있는 숫자값듀을 몚두 덧셈
            while ((line = reader.readLine()) != null) {
                result += Integer.parseInt(line);
            }
            return result;

        } catch (IOException e) {
            throw new IllegalArgumentException(path + "에 핎당하는 파음읎 없습니닀.", e);
        }
    }
}

class Client {
    public static void main(String[] args) {
        // 파음겜로 섀정
        FileProcessor fileProcessor = new FileProcessor("number.txt");

        // 덧셈한 결곌값 얻Ʞ
        int result = fileProcessor.process();
        System.out.println(result);
    }
}

귞런데 만음 몚든 숫자듀을 곱셈하거나 나누는 추가적읞 연산하는 Ʞ능읎 필요하닀고 한닀.

귞래서 MultiplyFileProcessor 띌는 새로욎 큎래슀륌 정의하고 FileProcessor 큎래슀에서 Ʞ능을 가젞와 연산하는 띌읞 부분을 곱셈윌로 바꿔 죌었닀.

class MultiplyFileProcessor {
    private String path;

    public MultiplyFileProcessor(String path) {
        this.path = path;
    }

    public int process() {
        try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
            int result = 0;
            String line = null;

            // 파음에 있는 각 띌읞에 있는 숫자값듀을 몚두 곱셈
            while ((line = reader.readLine()) != null) {
                result *= Integer.parseInt(line); // 읎 부분만 곱셈윌로 바꿔쀀닀.
            }
            return result;
            
        } catch (IOException e) {
            throw new IllegalArgumentException(path + "에 핎당하는 파음읎 없습니닀.", e);
        }
    }
}

하지만 연산 부분만 닀륌뿐읎지 파음을 읜얎와 읜는 알고늬슘 부분은 똑같닀. 슉, 윔드의 쀑복읎 발생한 것읎닀.

볎통 메서드 쀑복을 핎결하Ʞ 위핎 상속을 하고 부몚 큎래슀에서 메서드륌 정의하멎 자식 큎래슀에서 가젞와 사용하멎 되지만, 위의 메서드 로직을 볎멎 한두쀄만 윔드가 닀륞 상황읎닀.

귞러멎 공통된 부분만읎띌도 따로 메소드로 빌멎 되는데, 윔드 로직상 였히렀 더 복잡핎질것만 같닀.

따띌서 공통된 부분을 메소드로 빌는게 아닌, 반대로 닀륞 부분을 메소드로 빌버늬는 발상의 전환을 하멎 된닀.

 

템플늿 메서드 팚턎을 적용한 윔드 ✔

Template-Method-Pattern

뚌저 FileProcessor륌 ì¶”상 큎래슀로 만듀고 process() 메서드륌 템플늿 메서드로 명명한닀. 귞늬고 템플늿에서 싀행되는 caculate() 메서드륌 따로 추상 메서드로 만듀얎 자식 큎래슀에서 구현을 하도록 유도 한닀.

Template-Method-Pattern

abstract class FileProcessor {
    private String path; // 생성자로 부터 파음겜로륌 받아와 저장

    public FileProcessor(String path) {
        this.path = path;
    }

    // 템플늿 메소드 (였버띌읎딩 못하게 final 처늬)
    public final int process() {
        try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
            int result = getResult();
            String line = null;

            while ((line = reader.readLine()) != null) {
                result = caculate(result, Integer.parseInt(line));
            }
            return result;

        } catch (IOException e) {
            throw new IllegalArgumentException(path + "에 핎당하는 파음읎 없습니닀.", e);
        }
    }

    protected abstract int caculate(int result, int number);
    protected abstract int getResult();
}

읎제 각 구현 큎래슀듀에서 추상 큎래슀륌 상속하고 추상 메서드륌 구현하여 알고늬슘 로직을 섀정핎죌멎, 추상 큎래슀에서 믞늬 정의된 템플늿 메서드의 로직에 따띌 각Ʞ 닀륞 알고늬슘을 수행하게 된닀.

쀑복되는 윔드듀은 추상 큎래슀에 몚아두고 닀륞 부분만 각 큎래슀듀읎 구현하도록 하여 가독성을 높읎고 쀑복은 제거시킚 것읎닀.

// 더하Ʞ 연산 수행
class PlusFileProcessor extends FileProcessor{

    public PlusFileProcessor(String path) {
        super(path);
    }

    @Override
    protected int caculate(int result, int number) {
        return result += number;
    }
    
    @Override
    protected int getResult() {
        return 0;
    }
}

// 곱셈 연산 수행
class MultiplyFileProcessor extends FileProcessor{

    public MultiplyFileProcessor(String path) {
        super(path);
    }

    @Override
    protected int caculate(int result, int number) {
        return result *= number;
    }
    
    @Override
    protected int getResult() {
        return 1; // 곱셈은 쎈깃값읎 0읎멎 결곌도 멱등하니까
    }
}
class Client {
    public static void main(String[] args) {
        PlusFileProcessor plusFileProcessor = new PlusFileProcessor("numbers.txt");
        int result1 = plusFileProcessor.process();
        System.out.println(result1); // 15

        MultiplyFileProcessor multiplyFileProcessor = new MultiplyFileProcessor("numbers.txt");
        int result2 = multiplyFileProcessor.process();
        System.out.println(result2); // 120
    }
}

할늬우드 원칙 쀀수

헐늬우드 원칙(Hollywood Principle) 읎란 고수쀀 몚듈(추상큎래슀, 읞터페읎슀)에 의졎하고 고수쀀 몚듈에서 연띜(메소드 싀행) 하띌는 원칙읎닀.

객첎 끌늬 읎상하게 얌킀고 섀쌜, 의졎성읎 복잡하게 ꌬ여있는 것을 '의졎성 부팚(dependency rot)' 띌고 부륎는데, 헐늬우드 원칙을 활용하멎 의졎성 부팚륌 방지할 수 있게 된닀.

자바 프로귞래밍윌로 간닚히 말하자멎, 닀형성을 읎용핎 고수쀀의 객첎 타입에서만 왠만하멎 메서드 싀행을 하띌는 말읎닀.

바로 예제 윔드로 듀얎가볎자

 

큎늰하지 않은 묞제의 윔드 ❌

닀음 LowerA 와 LowerB 큎래슀가 있고 각각 메소드륌 가지고 있닀고 하자.

class LowerA {
    void print(int num) {
        System.out.println(num);
    }

    int calculate(int n1, int n2) {
        return n1 + n2;
    }
}

class LowerB {
    void echo(int variable) {
        System.out.println(variable);
    }

    int operation(int n1, int n2) {
        return n1 * n2;
    }
}

메서드 구성을 자섞히 삎펎볎니 print() ì™€ echo() ë©”서드는 메서드 명만 닀륌 뿐읎지 메서드 시귞니처와 메서드 로직읎 완전히 동음하닀. 따띌서 읎때 우늬가 배웠던 대로 상위(고수쀀) 큎래슀로 묶얎 윔드 쀑복을 쀄음 수 있닀.

class Higher {
    void print(int num) {
        System.out.println(num);
    }
}

class LowerA extends Higher {

    int calculate(int n1, int n2) {
        return n1 + n2;
    }
}

class LowerB extends Higher {

    int operation(int n1, int n2) {
        return n1 * n2;
    }
}

큎래슀간 상속 ꎀ계륌 형성하멎 객첎 지향의 ꜃읞 닀형성을 읎용할 수 있게 된닀. 하지만 아직도 묞제가 있닀. 메서드 낎부 로직읎 달띌 통음시킀지 못한 calculate() 와 operation() 메서드 읎닀.

만음 읎 둘을 싀행할 필요가 있닀멎 얎쩔수 없읎 닀욎 캐슀팅을 í•Žì•Œ 된닀.

class Client {
    public static void main(String[] args) {
        // 업캐슀팅 (고수쀀 몚듈 타입윌로 읞슀턎슀화)
        Higher obj = new LowerA();

        // 상위 큎래슀로 메서드가 통음되얎 있Ʞ에 메서드 싀행에 묞제 없음
        obj.print(1000);

        // 하지만 메서드 낎부 로직읎 닀륞 겜우 상위 큎래슀로 묶얎 쀄수가 없얎, 하위 큎래슀에 따로 정의핎알 되고 닀욎캐슀팅을 통핎 싀행핎알 핹
        ((LowerA) obj).calculate(10, 20);

        // 만음 닀륞 자식 큎래슀로 교첎할 필요가 있닀멎,
        obj = new LowerB();

        // 역시 닀욎 캐슀팅을 통핎 싀행핎알 한닀.
        ((LowerB) obj).operation(100, 200);
    }
}

 

템플늿 메서드 팚턎을 적용한 윔드 ✔

하지만 우늬는 디자읞 팚턎에 대핮 ë°°ì› ë‹€.

대부분 사람듀읎 템플늿 메소드 팚턎을 ë°°ìšžë•Œ '템플늿 메소드' 에 국한되얎 생각하는데, 템플늿 메소드는 알고늬슘 댈대음 뿐읎고, 템플늿 메소드 팚턎의 진자 핵심은 추상 큎래슀륌 통한 윔드 통합 및 고수쀀 의졎 유도 읎닀.

abstract class Higher {
    void print(int num) {
        System.out.println(num);
    }

    abstract int calculate(int n1, int n2);
}

class LowerA extends Higher {

    @Override
    int calculate(int n1, int n2) {
        return n1 + n2;
    }
}

class LowerB extends Higher {

    @Override
    int calculate(int n1, int n2) {
        return n1 * n2;
    }
}
class Client {
    public static void main(String[] args) {
        // 업캐슀팅 (고수쀀 몚듈 타입윌로 읞슀턎슀화)
        Higher obj = new LowerA();
        obj.calculate(10, 20); // 30

        obj = new LowerB();
        obj.calculate(100, 200); // 20000
    }
}

정늬하자멎, 상속을 통핎 쀑복되는 윔드륌 상위 큎래슀로 통음시쌜 윔드 쀑복을 제거하고, 메서드 시귞니처가 같지만 낎부 로직읎 자식 큎래슀마닀 닀륞 부분은 추상 메소드륌 통핎 상위 큎래슀에서 닀형성윌로 메서드륌 싀행할 수 있도록, 고수쀀의 타입윌로 유지하띌는 것읎닀.


hook 메서드 응용하Ʞ

 

큎늰하지 않은 묞제의 윔드 ❌

닀음곌 같읎 컀플와 홍찚륌 만드는 Coffee 와 Tea 큎래슀가 있닀. 배욎바와 같읎 윔드의 쀑복읎 볎읎며 읎륌 템플늿 메서드 팚턎윌로 묶얎죌렀고 한닀.

class Coffee {

    void prepareRecipe() {
        boilWater(); // "묌 끓읎Ʞ"
        brewCoffeeGrinds(); // "필터륌 통핎 컀플륌 우렀낎는 쀑"
        pourInCup(); // "컵에 따륎는 쀑"
        addSugarAndMilk(); // "섀탕곌 우유륌 추가하는 쀑"
    }

    public void boilWater() {
        System.out.println("묌 끓읎Ʞ");
    }

    public void brewCoffeeGrinds() {
        System.out.println("필터륌 통핎 컀플륌 우렀낎는 쀑");
    }

    public void pourInCup() {
        System.out.println("컵에 따륎는 쀑");
    }

    public void addSugarAndMilk() {
        System.out.println("섀탕곌 우유륌 추가하는 쀑");
    }
}


class Tea {

    void prepareRecipe() {
        boilWater(); // "묌 끓읎Ʞ"
        steepTeaBag(); // "찚륌 우늬는 쀑"
        pourInCup(); // "컵에 따륎는 쀑"
    }

    public void boilWater() {
        System.out.println("묌 끓읎Ʞ");
    }

    public void steepTeaBag() {
        System.out.println("찚륌 우늬는 쀑");
    }

    public void pourInCup() {
        System.out.println("컵에 따륎는 쀑");
    }
}

두 큎래슀륌 템플늿 메소드 팚턎윌로 합치Ʞ 전에 템플늿의 닀륞 부분을 삎펎볎자.

Template-Method-hook

우선 boilWater() 와 pourInCup() 메서드 사읎에 있는 부분은, Coffee와 Tea 큎래슀에서 공통적윌로 싀행되는 부분읎니, 추상 메서드로 따로 빌서 구현하도록 하멎 될 것 같닀.

귞렇닀멎 Coffee 큎래슀에만 싀행되는 addSugarAndMilk() 메서드 부분은 얎떻게 할까?  바로 읎곳을 hook 처늬하여 Coffee 자식 큎래슀에서만 싀행되도록 하멎 된닀.

 

템플늿 메서드 팚턎을 적용한 윔드 ✔

Template-Method-hook

abstract class CaffeineBeverage {

    // 템플늿 메서드
    final void prepareRecipe() {
        boilWater(); // "묌 끓읎Ʞ"
        brew();
        pourInCup(); // "컵에 따륎는 쀑"
        if (customerWantsCondiments()) {
            addCondiments();
        }
    }

    abstract void brew();

    abstract void addCondiments();

    // hook 메서드
    boolean customerWantsCondiments() {
        return false;
    }

    public void boilWater() {
        System.out.println("묌 끓읎Ʞ");
    }

    public void pourInCup() {
        System.out.println("컵에 따륎는 쀑");
    }
}
class Coffee extends CaffeineBeverage {
    public void brew() {
        System.out.println("필터륌 통핎 컀플륌 우렀낎는 쀑");
    }

    public void addCondiments() {
        System.out.println("섀탕곌 우유륌 추가하는 쀑");
    }

    boolean customerWantsCondiments() {
        String answer = "";

        System.out.print("컀플에 우유와 섀탕을 넣얎 드늎까요? (y/n) : ");

        try (BufferedReader in = new BufferedReader(new InputStreamReader(System.in))) {
            answer = in.readLine();
        } catch (IOException ioe) {
            System.out.println("IO 였류");
        }

        if (answer.toLowerCase().startsWith("y")) {
            return true;
        } else {
            return false;
        }
    }
}

class Tea extends CaffeineBeverage {
    public void brew() {
        System.out.println("찚륌 우늬는 쀑");
    }

    public void addCondiments() {}
}

위의 hook 메소드 역할읞 customerWantsCondiments()의 구현부륌 자섞히 볎멎, 닚순 true / false 늬턎을 떠나서 ê·ž 읎전에 사용자로부터 입력값을 받는 등의 추가적읞 로직을 구성한 걞 볌 수 있닀.

읎런식윌로 hook 메소드륌 좀 더 닀양하게 응용할 수 있닀는 점을 볎여죌Ʞ 위핎 예륌 듀었닀.

class client {
    public static void main(String[] args) {

        CaffeineBeverage coffee = new Coffee();
        System.out.println("컀플 만드는쀑...");
        coffee.prepareRecipe();

        System.out.println("\n--------------------------------\n");

        CaffeineBeverage tea = new Tea();
        System.out.println("홍찚 만드는쀑...");
        tea.prepareRecipe();
    }
}

Template-Method-hook


싀묎에서 찟아볎는 Template Method 팹턮

 

Java

  • java.io.InputStream, java.io.OutputStream, java.io.Reader, java.io.Writer 의 음반 메서드륌 하위 큎래슀가 재정의
  • java.util.AbstractListjava.util.AbstractSet, java.util.AbstractMap ì˜ 음반 메서드륌 하위 큎래슀가 재정의
  • javax.servlet.http.HttpServlet의 몚든 doXXX()메서드는 Ʞ볞적윌로 응답에 HTTP 405 "Method Not Allowed" 늬턎 윔드륌 볎낎Ʞ 때묞에 읎듀을 상속하여 재정의 하여 사용한닀.

 

HttpServlet

우늬가 HttpServlet 큎래슀륌 상속 하고 doGet() 곌 doPost() 메서드륌 였버띌읎드 하멎, 상위 서랔늿 객첎에서 템플늿 로직을 수행하닀가 doGet, doPost 륌 혞출핎알 할 때 하위 큎래슀륌 찞조하여 메서드륌 싀행한닀.

public class MyHello extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}

 

AbstractMap

AbstractMap<K,V> íŽëž˜ìŠ€ì— 정의 되얎있는 get() 메서드륌 읎륌 상속하는 HashMap, TreeMap 등 서람큎래슀에서 였버띌읎드하여 자신만의 구현 방법윌로 닀륞 방식윌로 재정의 하고 있는 것을 볌 수 있닀. ꌭ 추상 메소드륌 재정의 핎알되는게 아니띌 음반 메소드도 템플늿에 고정되얎 싀행되는 것읎띌멎 였버띌읎딩하여 재정의 하멎 곧 알고늬슘 변화가 되Ʞ 때묞읎닀.

 

AbstractMap<K,V> 의 get() 메소드

public V get(Object key) {
    Iterator<Entry<K,V>> i = entrySet().iterator();
    if (key==null) {
        while (i.hasNext()) {
            Entry<K,V> e = i.next();
            if (e.getKey()==null)
                return e.getValue();
        }
    } else {
        while (i.hasNext()) {
            Entry<K,V> e = i.next();
            if (key.equals(e.getKey()))
                return e.getValue();
        }
    }
    return null;
}

 

HashMap<K,V> extends AbstractMap<K,V> 의 get() 메소드

public V get(Object key) {
    Node<K,V> e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}

 

TreeMap<K,V> extends AbstractMap<K,V> 의 get() 메소드

public V get(Object key) {
    Entry<K,V> p = getEntry(key);
    return (p==null ? null : p.value);
}

Spring Framework

 

Configuration

WebSecurityConfigureAdapter 큎래슀륌 상속 하고 configure() 메서드륌 였버띌읎드하멎, 슀프링 프레임워크의 Config 로직의 거대한 알고늬슘 쀑 음부륌 구현하게 되는 것읎닀.

public class TemplateInSpring {

    public static void main(String[] args) {
        // TODO 템플늿-윜백 팹턮
        // JdbcTemplate
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.execute("insert");

        // RestTemplate
        RestTemplate restTemplate = new RestTemplate();

        HttpHeaders headers = new HttpHeaders();
        headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
        headers.set("X-COM-PERSIST", "NO");
        headers.set("X-COM-LOCATION", "USA");

        HttpEntity<String> entity = new HttpEntity<String>(headers);
        ResponseEntity<String> responseEntity = restTemplate
                .exchange("http://localhost:8080/users", HttpMethod.GET, entity, String.class);
    }

    @Configuration
    class SecurityConfig extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests().anyRequest().permitAll();
        }
    }
}

ꎀ렚된 디자읞 팹턮 종류

 

Strategy vs Temaplate Method

 

팹턮 유사점

전략 팚턎곌 템플늿 메서드 팚턎은 ì•Œê³ ëŠ¬ìŠ˜ì„ 때에 따띌 적용한닀는 컚셉윌로썚, 둘읎 공통점을 가지고 있닀.

전략 및 템플늿 메서드 팚턎은 ê°œë°©í˜• 폐쇄 원칙을 충족하고 윔드륌 변겜하지 않고 소프튞웚얎 몚듈을 쉜게 확장할 수 있도록 하는 데 사용할 수 있닀. 

 

팹턮 찚읎점

전략 팚턎은 합성(composition)을 통핎 핎결책을 강구하며, í…œí”ŒëŠ¿ 메서드 팚턎은 상속(inheritance)을 통핎 핎결책을 제시한닀. 귞래서 전략 팚턎은 큎띌읎얞튞와 객첎 간의 결합읎 느슚한 반멎, 템플늿 메서드 팚턎에서는 두 몚듈읎 더 밀접하게 결합된닀. (결합도가 높윌멎 안좋음)

전략 팚턎에서는 대부분 읞터페읎슀륌 사용하지만, 템플늿 메서드 팚턎서는 죌로 추상 큎래슀나 구첎적읞 큎래슀륌 사용한닀.

전략 팚턎에서는 전첎 전략 알고늬슘을 변겜할 수 있지만, 템플늿 메서드 팚턎에서는 알고늬슘의 음부만 변겜되고 나뚞지는 변겜되지 않은 상태로 유지된닀. (템플늿에 종속)

따띌서 닚음 상속만읎 가능한 자바에서 상속 제한읎 있는 템플늿 메서드 팚턎볎닀는, 닀양하게 많은 전략을 implements 할 수 있는 전략 팚턎읎 협업에서 많읎 사용되는 펞읎닀.


# 찞고자료

윔딩윌로 학습하는 GoF의 디자읞 팹턮 - 백Ʞ션

https://refactoring.guru/design-patterns/template-method

https://reactiveprogramming.io/blog/en/design-patterns/template-method 

https://blogshine.tistory.com/13 

https://yaboong.github.io/design-pattern/2018/09/27/template-method-pattern/