CS/디자인패턴

[디자인패턴] Template Method 패턴 (템플릿 메소드)

mabb 2023. 5. 26. 22:08
반응형

▶디자인 패턴 ( Design Pattern)
:프로그램 개발 시 문제 해결을 위하여 빈번히 사용되는 개발자들의 경험, 내적인 축적에 대하여,
GoF(Gang of Four)라 불리는 4인의 개발자들이 각각을 패턴으로 정의하고 이름을 붙였다.
이를 디자인 패턴 (Design Pattern) 이라고 한다.
 23개의 디자인 패턴을
『Elements of Reusable Object-Oriented Software』
라는 책으로 발간하였다.

Gang of Four 분들은 모두 안경을 썼다.

 
▷디자인 패턴의 용어를 빌리면 서로의 아이디어를 보다 용이하게 비교, 논의할 수 있게 된다.
▷재사용과 기능확장이 쉬운 소프트웨어를 만들기 위한 유익한 기법이 바로 디자인 패턴이다.
 
---------------------------------------------------------
1. Iterator
2. Adapter
3. Template Method
4. Factory Method
5. Singleton
6. Prototype
7. Builder
8. Abstract Factory
9. Bridge
10. Strategy
11. Composite
12. Decorator
13. Visitor
14. Chain of Responsibility
15. Facade
16. Mediator
17. Observer
18. Memento
19. State
20. Flyweight
21. Proxy
22. Command
23. Interpreter
---------------------------------------------------------

▶Template Method 패턴

"공통적인 로직, 뼈대, 흐름을 정하는 템플릿 메서드를 만들어 사용"
"흐름 속의 구체적인 내용은 하위 클래스에서 정의하는 패턴"


-왜 사용하는가
-클래스다이어그램
-각 클래스(인터페이스)의 역할
-Java소스


▶Template Method패턴에 대한 이해

-템플릿은 정해진 형태를 만드는 틀이다.

-템플릿을 통해 형태를 만들 때 재료는 다양하게 바꿔볼 수 있다.

-템플릿 역할을 하는 '템플릿메소드'를 만들어 사용하는 패턴이 Template Method 패턴이다.
(여기서 메소드는 정말로 자바 클래스 안의 메소드이다)

-템플릿 메소드로 공통된 흐름, 뼈대를 정의하고 내부적인 동작은 구상에 맡긴다.

-상위 클래스(추상클래스 또는 인터페이스)에서 템플릿 메서드를 정의한다.

-템플릿 메소드 내에는 추상 메서드들로 공통된 로직, 흐름, 뼈대를 잡아 놓았다.

-템플릿 메소드 내의 추상메소드의 구체적인 기능은 하위 클래스에서 정한다.

-상위 클래스의 템플릿 메소드에서 흐름을 정하고 하위 클래스에서는 흐름 속의 구체적인 처리를 결정한다.

-공통적인 특정 흐름, 뼈대를 가지는 기능 A, B, C를 만들어야 할 때 완성된 A기능을 복사하여 B와 C기능을 만들고
각각 다르게 구현할 부분만 수정을 한다면 어떨까? 

-공통된 흐름에 대한 부분이 중복되고 수정 및 유지 보수 시 A, B, C코드를 모두 변경하여야 한다. 

-특정 로직으로 동작할 메소드를 템플릿 메소드로 정의하고 세부적인 내용은 하위 클래스에서 정하는 패턴인 것이다.

-공통의 로직, 뼈대, 흐름 = 틀 = 템플릿
->템플릿역할을하는메소드=템플릿메소드

-공통된 틀을 만들고  재료를 바꿔가며 쓰는 것

-공통된 틀을 추상적으로 뽑아놓고 세부적인 것을 구상하여 다형성으로 사용하는 것이다.

 

▶왜 사용하는가

공통된 로직, 공통된 뼈대, 공통된 흐름을 갖는데 조금씩 기능이 다른 여러 개의 기능을 만들고자 할 때 사용한다. 공통된 로직은 템플릿메소드 내의 추상메소드로  정의하고, 세부적인 내용은 하위클래스에서 추상메소드를 오버라이드하여 구현하는 것이다. 클라이언트는 메소드의 공통된 로직을 뽑아내 템플릿화 한 템플릿 메소드를 이용하는데, 세부적인 내용을 정의한 하위 클래스의 객체로 대체하여 쓸 수가 있는 것이다. (객체지향의 LSP, 리스코프 치환 법칙)
결국은 객체지향의 다형성(Polymorphism)을 이용하고, 공통된 로직을 뽑아낸 추상에 의존하여 재사용성과 유지보수 효율을 높이는 것이 핵심이다.

java.io.InputStream에서도 템플릿메소드 패턴을 사용하고 있다.

public abstract class InputStream
extends Object
implements Closeable
 

InputStream (Java SE 20 & JDK 20)

All Implemented Interfaces: Closeable, AutoCloseable Direct Known Subclasses: AudioInputStream, ByteArrayInputStream, FileInputStream, FilterInputStream, ObjectInputStream, PipedInputStream, SequenceInputStream, StringBufferInputStream This abstract class

docs.oracle.com

Applications that need to define a subclass of InputStream must always provide a method that returns the next byte of input.

InputStream의 subclass는 read() 추상메소드를 오버라이드하여 입력의 다음 byte를 반환하는 기능을 구현하여야 할 책임이 있다.  (하위클래스 책임, Subclass Responsibility)

▽InputStream의 서브클래스들
AudioInputStream
ByteArrayInputStream
FileInputStream
FilterInputStream
ObjectInputStream
PipedInputStream
SequenceInputStream
StringBufferInputStream

 

▶클래스다이어그램

Template Method 패턴의 클래스다이어그램

 

▶각 클래스(인터페이스)의 역할

▷클라이언트 역할 - Client.java
:템플릿메소드를 이용한다. 템플릿 메소드 내의 추상메소드를 구현한 하위 클래스 객체를 대입함으로써 템플릿메소드의 로직으로 구현된 다양한 기능을 사용할 수 있다.

▷TemplateMethod - TemplateMethod.java (인터페이스 또는 추상클래스로 가능)
: 인터페이스를 사용하여 디폴트메소드로 틀 역할을 하는 템플릿 메소드를 정의하였다. 인터페이스 대신 추상클래스를 사용해도 된다. 템플릿 메소드 내 로직은 해당 클래스가 가지는 추상메소드로 정의하는데 이 추상메소드는 하위 클래스에서 오버라이드하여 구체적인 기능을 구현한다.

▷ConcreteClass1과 ConcreteClass2  -ConcreteClass1.java와 ConcreteClass2.java
: 구체적 기능을 정의하는 하위 클래스들이다. 템플릿 메소드 내의 추상메소드를 재정의하여 공통된 흐름 속에서의 구체적인 기능을 구현한다.
단, 오버라이드를 하려면 먼저 템플릿의 로직, 흐름에 대한 이해가 필요하다.
 

▶Java 소스

*java1.8 이전 버전을 사용하는 교재를 참고하였습니다.
Java API 문서 등을 참조하여 제네릭을 적용하는 등 소스를 개선할 필요가 있습니다.
(https://docs.oracle.com/en/java/javase/20/docs/api/)

Client.java

package main.java.designpattern.TemplateMethod;

public class Client {

    public static void main(String[] args){

        TemplateMethod tm1 = new ConcreteClass1();
        TemplateMethod tm2 = new ConcreteClass2();

        tm1.templateMethod();
        tm2.templateMethod();
    }
}

 

TemplateMethod.java

package main.java.designpattern.TemplateMethod;

public interface TemplateMethod {

    public default void templateMethod(){
        //step1()을 동작하고
        //step2()를 3번 동작시키고
        //step3()을 동작하는 것이 공통적인 로직, 흐름, 뼈대이다.

        System.out.println("\n\n<<템플릿 메소드의 로직>>");
        System.out.println("가장 먼저 step1을 실행!");
        step1();

        System.out.println("step2를 세 번 실행!");
        for(int i=0; i<3; i++){
            step2();
        }

        System.out.println("마지막으로 step3을 실행!");
        step3();
    }

    //추상 메소드는 템플릿메소드의 공통적인 로직, 흐름, 뺘대를 잡아주는 역할을 하며
    //구체적인 기능은 하위 클래스에서 정의한다.
    public abstract void step1();
    public abstract void step2();
    public abstract void step3();

}

 

ConcreteClass1.java

package main.java.designpattern.TemplateMethod;

public class ConcreteClass1 implements TemplateMethod{
    @Override
    public void step1() {
        System.out.println("step1단계를 1번 방식으로 구현");
    }

    @Override
    public void step2() {
        System.out.println("step2단계를 1번 방식으로 구현");
    }

    @Override
    public void step3() {
        System.out.println("step3단계를 1번 방식으로 구현");

    }
}

 

 

ConcreteClass2.java

package main.java.designpattern.TemplateMethod;

public class ConcreteClass2 implements TemplateMethod{
    @Override
    public void step1() {
        System.out.println("step1단계를 2번 방식으로 구현");
    }

    @Override
    public void step2() {
        System.out.println("step2단계를 2번 방식으로 구현");
    }

    @Override
    public void step3() {
        System.out.println("step3단계를 2번 방식으로 구현");
    }
}

 

 

※ 해당 포스팅에서 템플릿메소드를 인터페이스로 구현하였는데...

-템플릿메소드에서 사용하는 추상메소드는 하위 클래스에서 오버라이드 하여야 하지만 하위 클래스에서 템플릿메소드까지 오버라이드해버리면 안 된다.

-그런데 인터페이스에서는 메소드 오버라이드를 막기 위하여 final을 사용할 수가 없다.

※ 해당 포스팅에서 추상메소드를 public으로 선언하였는데...

-protected 접근자로 같은 패키지 또는 자식 클래스만 접근할 수 있도록 제한하는 것이 더 안전하다.

TemplateMethod는 인터페이스보다는 다음과 같이 추상클래스에서 만드는 것이 좋다.

TemplateMethodAbstractClass.java

package main.java.designpattern.TemplateMethod;

public abstract class TemplateMethodAbstractClass {

    public final void templateMethod(){

        step1();
        step2();
        step3();

    }

    protected abstract void step1();
    protected abstract void step2();
    protected abstract void step3();

}

 

 


---------------------------------------------------------
※참고자료
-Java언어로 배우는 디자인 패턴 입문
-Headfirst Design Pattern
-나무위키, 위키백과, 네이버백과
-리팩토링구루(https://refactoring.guru/ko)

반응형