Computer Science/디자인패턴

[디자인 패턴] 템플릿 메서드 패턴

Jinhwan 2024. 3. 31. 00:28

개념

  • 템플릿 메서드 패턴은 행위 패턴의 일종으로 객체의 메소드에 대해서 변경이 되는 부분들에 대해서 서브 클래스의 그 책임을 위임하는 패턴이다.
  • 추상 기본 클래스 : 템플릿 메서드(알고리즘의 골격)을 정의하며, 이 클래스에는 알고리즘의 각 단계를 실행하는 메서드도 정의된다. 일부 메서드는 기본 클래스에서 정의되며 일부는 서브클래스에서 반드시 구현되어야하는 추상 메서드일 수 있다.
  • 구체적인 클래스 : 추상 기본 클래스를 상속받아 특정 단계를 구체적으로 구현한다. 이러한 구현은 알고리즘의 변하지 않는 구조내에서 특정 작업을 수행한다.

구현

https://github.com/RicardoKim/DesignPattern-Go-/tree/main/template_method_pattern

 

DesignPattern-Go-/template_method_pattern at main · RicardoKim/DesignPattern-Go-

Contribute to RicardoKim/DesignPattern-Go- development by creating an account on GitHub.

github.com

장점

  • 코드의 재사용성 향상 
  • 확장성 : 상위 클래스의 변경 없이 서브 클래스의 코드 변경으로 알고리즘 구동 방식에 기능을 추가할 수 있다.
  • 설계의 일관성 : 알고리즘 구조를 표준화 하는데 사용된다.

단점

  • 유연성 제한 : 서브클래스의 구조 자체를 변경할 수 없다.
  • 리스코프 치환 원칙 위반 가능성 :  서브클래스가 슈퍼클래스의 행위를 제대로 치환하지 못할 경우, 객체 지향 설계 원칙 중 하나인 리스코프 치환 원칙을 위반할 수 있다.
    • LSP(Liskov Substitution Principle) : 서브타입은 언제나 기반타입으로 교체가능해야한다.
      • 자식 클래스는 부모 클래스의 인터페이스를 제대로 수행할 수 있어야한다.

사용 예시

JDBC Template

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jdbc/core/JdbcTemplate.html

 

JdbcTemplate (Spring Framework 6.1.5 API)

Execute a query for a result object, given static SQL. Uses a JDBC Statement, not a PreparedStatement. If you want to execute a static query with a PreparedStatement, use the overloaded JdbcOperations.queryForObject(String, Class, Object...) method with nu

docs.spring.io

핵심 작동 원리

  • 작업 템플릿 정의 : DB실행과정에서 기본 흐름을 정의함. 이러한 과정은 모든 데이터베이스 작업에 공통적으로 필요한 단계를 의미.
  • 변경 가능한 부분 분리 : SQL쿼리, 쿼리 매개변수 바인딩, 결과 집합의 처리 등과 같은 부분을 개발자가 커스터 마이즈 할 수 있으며 일반적으로 콜백 인터페이스를 구현하는 형태로 제공된다.
  • 리소스 관리와 예외 처리의 자동화 : 데이터베이스 연결, 예외 처리 등과 같은 반복적이고 일반적인 작업을 자동화하여 복잡한 예외로직 처리 및 리소스 누수 방지를 함.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class MyService {

    private final JdbcTemplate jdbcTemplate;

    @Autowired
    public MyService(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public List<MyEntity> findAll() {
        String sql = "SELECT id, name, value FROM my_table";
        return jdbcTemplate.query(sql, (rs, rowNum) -> new MyEntity(
                rs.getLong("id"),
                rs.getString("name"),
                rs.getString("value")
        ));
    }

    public void insertMyEntity(String name, String value) {
        String sql = "INSERT INTO my_table (name, value) VALUES (?, ?)";
        jdbcTemplate.update(sql, name, value);
    }

    static class MyEntity {
        private Long id;
        private String name;
        private String value;

        public MyEntity(Long id, String name, String value) {
            this.id = id;
            this.name = name;
            this.value = value;
        }
    }
}

위의 코드에서는 DB연결 및 예외처리 로직을 볼 수 없다. 연결은 DataSource 빈에 의해서 처리되게 되고 위의 코드에서 예외가 발생하게 되면 JdbcTemplate에서 사전에 정의한 DataAccessException을 발생하게 된다. 이를 통해 우리는 여러 DB Connection에 대해서 연결과 예외처리가 공통적으로 처리되고(추상 기본 클래스의 장점) 그 외에 SQL쿼리, 쿼리 매개변수 바인딩, 결과 집합의 처리등은 커스터마이즈가 되는 것을 알 수 있다(구체적인 클래스의 장점)


참고

https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%ED%85%9C%ED%94%8C%EB%A6%BF-%EB%A9%94%EC%86%8C%EB%93%9CTemplate-Method-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90

https://inpa.tistory.com/entry/OOP-%F0%9F%92%A0-%EC%95%84%EC%A3%BC-%EC%89%BD%EA%B2%8C-%EC%9D%B4%ED%95%B4%ED%95%98%EB%8A%94-LSP-%EB%A6%AC%EC%8A%A4%EC%BD%94%ED%94%84-%EC%B9%98%ED%99%98-%EC%9B%90%EC%B9%99

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jdbc/core/JdbcTemplate.html