템플릿이란 바뀌는 성질이 다른 코드 중에서 변경이 거의 일어나지 않으며 일정한 패턴으로 유지되는 특성을 가진 부분을 자유롭게 변경되는 성질을 가진 부분으로부터 독립시켜서 효과적으로 활용할 수 있도록 하는 방법

분리와 재사용을 위한 디자인 패턴 적용

변하는 부분을 변하지 않는 나머지 코드에서 분리 → 변하지 않는 부분을 재사용할 수 있는 방법이 있을것

템플릿 메소드 패턴의 적용

상속을 통해 기능을 확장해서 사용하는 부분이다. 변하지 않는 부분은 슈퍼클래스에 두고 변하는 부분은 추상 메소드로 정의해둬서 서브클래스에서 오버라이드 하여 새롭게 정의해 쓰도록 하는 것

UserDao 클래스의 기능을 확장하고 싶을 때 상속을 통해 자유롭게 확장(상위 클래스에 불필요한 변화는 X) Dao 로직마다 상속을 통해 새로운 클래스를 만들어야함

전략 패턴의 적용

오브젝트를 둘로 분리하고 클래스 레벨에서는 인터페이스를 통해서만 의존하도록 만드는 전략 패턴

OCP 관점에 보면 확장에 해당하는 변하는 부분을 별도의 클래스로 만들어 추상화된 인터페이스를 통해 위임는 방식

전략 패턴은 필요에 따라 Context는 그대로 유지되면서 전략을 바꿔 쓸 수 있다는것(OCP의 개방/폐쇄 원칙) Context가 Strategy 인터페이스 뿐 아니라 특정 구현 클래스를 직접 알고 있다는 건, 전략 패턴에도 OCP에도 잘 들어맞는다고 볼 수 없다

DI 적용을 위한 클라이언트/컨텍스트 분리

전략 패턴에 따르면 Context가 어떤 전략을 사용하게 할 것인가는 Context를 사용하는 앞단의 Client가 결정하는 게 일반적

Client가 전략 선택, 오브젝트로 만듬 → Context에 전달 → Context는 전달받은 Strategy 구현 클래스의 오브젝트를 사용

클라이언트가 컨텍스트가 사용할 전략을 정해서 전달 → DI 구조

마이크로 DI 원시적인 전략 패턴 구조를 따라 클라이언트가 오브젝트 팩토리의 책임을 함께 지고 있을 수도 있다. 또는 클라이언트와 전략(의존 오브젝트)이 결합될 수도 있다. 심지어는 클라이언트와 DI 관계에 있는 두 오브젝트가 모두 하나의 클래스 안에 담길 수도 있다.

이런 경우에는 DI가 매우 작은 다누이의 코드와 메소드 사이에서 일어나기도 한다. 이렇게 DI의 장점을 단순화해서 IoC 컨테이너의 도움 없이 코드 내에서 적용한 경우를 마이크로 DI라고도 함

템플릿과 콜백

복잡하지만 바뀌지 않는 일정한 패턴을 갖는 작업 흐름이 존재하고 그중 일부분만 자주 바꿔서 사용해야 하는 경우, 전략패턴을 적용한다. → 스프링에서는 템플릿/콜백 패턴 이라고 부른다. 전략 패턴의 컨텍스트를 템플릿, 익명 내부 클래스로 만들어지는 오브젝트를 콜백 이라고 함

템플릿

어떤 목적을 위해 미리 만들어둔 모양이 있는 틀 템플릿 메소드 패턴은 고정된 틀의 로직을 가진 템플릿 메소드를 슈퍼클래스에 두고, 바뀌는 부분을 서브클래스의 메소드에 두는 구조로 이루어짐

콜백

실행되는 것을 목적으로 다른 오브젝트의 메소드에 전달되는 오브젝트 특정 로직을 담은 메소드를 실행시키기 위해 사용

템플릿/콜백의 동작 원리

RAII(Resource Acquisition Is Initialization)

객체와 자원의 라이프 사이클을 일치시킨다

프로그래머가 직접 자원을 획득하고 관리하는 것이 아니라, 자원의 생성, 파괴, 관리를 모두 객체에 위임하는 것을 의미한다.

private void executeSql(String sql, PreparedStatementSetter setter) {  
    try (Connection c = dataSource.getConnection(); PreparedStatement ps = c.prepareStatement(sql)) {  
        setter.setValues(ps);  
        ps.executeUpdate();  
    } catch (SQLException e) {  
        throw new RuntimeException(e);  
}}
with connect(host="localhost", user="username", password="password") as connection:
    with connection.cursor() as cursor:
	    cursor.execute("CREATE DATABASE myDB")

Closure

public int lambda() {
    BiFunction<Integer, Integer, Integer> function = (x, y) -> x + y;
    return function.apply(1, 2);
}
 
public int closure() {
    int z = 3;
    BiFunction<Integer, Integer, Integer> function = (x, y) -> x + y + z;
    return function.apply(1, 2);
}

Closure는 자신을 둘러싼 Context 내의 변수에 접근할 수 있다

environment capture

복사? ㄴㄴ 해당 값이 사라지지 않도록 잡아둔다? 지역 클래스나 익명 클래스가 외부 지역 변수를 참조할 때, 그 값을 자신의 인스턴스에 복사? 언어마다 다름

  • 자바
    • 레퍼런스 카운터
    • garbage counter → 메소드가 종료된 후에도 해당 변수의 값을 계속 사용 가능. 단 이때 지역 변수가 변경되면 큰일남 그래서 자바는 외부 지역 변수를 final 또는 effectively final 로 만들것을 강제함

Ref.

이일민, “토비의 스프링 3.1”, 에이콘 출판사(2012)

[BLOG] SCRIPTS BY(2024)

[BLOG] 햄치즈버거의 블로그(2024)

[BLOG] Heechan(2024)