[스프링 입문을 위한 자바 객체 지향의 원리와 이해] Chapter6 정리

구범모·2023년 7월 13일
0

개구리책

목록 보기
5/6
post-thumbnail

어댑터 패턴

💡 어댑터 : 변환기.

변환기의 역할 : 서로 다른 두 인터페이스 사이에 통신이 가능케 하는 것.

ex : 실생활에서 충전기가 휴대폰과 콘센트를 연결해주는 것.

JDBC / ODBC가 다양한 DB시스템을 단일한 인터페이스로 조작할 수 있게 해준다.

OCP를 활용한 패턴.

💡 어댑터 패턴은 합성, 즉 객체를 속성으로 만들어서 참조하는 디자인 패턴이다! 호출당하는 쪽의 메서드를, 호출하는 쪽의 코드에 대응하도록 중간에 변환기를 통해 호출하는 패턴.

프록시 패턴

💡 서비스 객체가 들어갈 자리에 대리자 객체를 대신 투입해, 클라이언트 쪽에서는 실제 서비스 객체를 통해 메서드를 호출하고 반환값을 받는지, 대리자 객체를 통해 메서드를 호출하고 반환값을 받는지 전혀 모르게 처리한다. 제어 흐름을 조정하기 위한 목적으로, 중간에 대리자를 두는 패턴.

OCP, DIP를 활용한 패턴.

그냥 캡슐화를 잘 지키는 패턴인 것 같다.

데코레이터 패턴

프록시 패턴과 구현방법이 같지만, 리턴값에 장식을 더한다.

리턴값을 포장한다고 해서, 데코레이터 패턴.

싱글턴 패턴

인스턴스를 하나만 만들어 사용하기 위한 패턴.

💡 왜 인스턴스를 하나만 만들지 ? → 인스턴스를 여러개 만들게 되면 불필요한 자원(메모리 등)을 사용하게 되고, 프로그램이 예상치 못한 결과(이건 뭘까)를 낳을 수 있기 때문.

인스턴스를 하나만 만들기 위해, 생성자를 private으로 지정한 이후(외부에서 생성자 호출 불가능.),
단일객체를 반환하기 위한 정적 메서드(getter)와 해당 단일객체를 참조하는 정적 참조변수가 필요하다.

이때 getter에서는 해당 단일객체가 생성되어있지 않으면 생성 후 반환하고, 이미 생성되어 있으면 바로 해당 단일객체를 반환한다.

공유되는 정적 변수는 속성을 갖지 않게 하는 것이 정석 → 동시에 해당 변수(객체)에 접근해서 속성값을 변경할 경우, 경쟁상태에 진입할 수 있기 때문.

템플릿 메서드 패턴

노션에서 쓰는 템플릿처럼, 공통되게 사용되는 메서드를 미리 정의해 두고, 상속받는 클래스에서 구현을 강제하도록 추상 메서드를 두거나 선택적으로 오버라이딩(훅 메서드) 하는 패턴.

팩토리 메서드 패턴

객체를 생성하는 팩터리 클래스의 메소드를, 하위 클래스에서 해당 팩터리 메서드를 오버라이딩 하여 객체를 반환하게 하는 패턴.

예제에서 설명한 것은 추상 팩터리 메서드 패턴으로써, 하나의 객체를 구성하는 것들을 같은 분류로 보아, 하나의 클래스 안에서 구성요소들을 생성하는 메소드를 오버라이딩한다.

DIP를 활용한다.

전략 패턴

💡 1. 전략 메서드를 가진 전략 객체 2. 전략 객체를 사용하는 컨텍스트 3. 전략 객체를 생성해 컨텍스트에 주입하는 클라이언트

위 세개의 구성요소가 정말 중요하다고 한다.

클라이언트가 컨트롤러의 역할을 하는 듯 보인다.

군인의 예제에서,
보급장교 : 클라이언트
군인 : 컨텍스트
무기 : 전략이 된다.

전략을 인터페이스로 정의하고, 해당 전략을 무기로써 구현체로 구현한다.

이 때, 컨텍스트가 될 군인에게 무기인 전략을 주입해 준다.

템플릿 메서드 패턴과 유사한데, 템플릿 메서드는 상속(추상 메서드 & 오버라이딩)을 이용하는 반면, 전략 패턴은 인터페이스를 이용하기 때문에 구현의 강제성이 부여된다는 점이 차이점으로 보인다.

다만 자바는 단일 상속만이 가능하므로, 다양한 메서드를 구현하기 위해 템플릿 메서드 패턴보다는 전략 패턴을 더 활용한다고 한다.(이 때, 최대한 ISP를 활용해 인터페이스를 쪼개서 구현할 듯 싶다.)

OCP, DIP를 활용한다.

템플릿 콜백 패턴

💡 전략을 익명 내부 클래스로 구현한 전략 패턴

DI에서 활용하는, 특별한 형태의 전략 패턴.

전략 패턴과의 차이점

전략을 익명 내부 클래스로 정의해서 사용한다.

예제

전략

package java.templateCallbackPractice;

public interface Strategy {
    void runStrategy();
}

(리팩토링 전)컨텍스트

package java.templateCallbackPractice;

// Context
public class Soldier {
    void runContext(Strategy strategy) {
        System.out.println("전투 시작");
        strategy.runStrategy();
        System.out.println("전투 종료");
    }
}

(리팩토링 전)클라이언트

package java.templateCallbackPractice;

public class Client {
    public static void main(String[] args) {
        Soldier rambo = new Soldier();
        
        rambo.runContext(new Strategy() {
            @Override
            public void runStrategy() {
                System.out.println("총총ㅊ오옻오");
            }
        });

        System.out.println();
        
        rambo.runContext(new Strategy() {
            @Override
            public void runStrategy() {
                System.out.println("카카카ㅏ카랔라");
            }
        });
    }
}

클라이언트 코드를 보면, 익명 클래스 사용으로 (같은 메서드를 오버라이딩 하는 과정에서) 중복 코드가 너무 많이 발생한다.

(리팩토링 후)컨텍스트

package java.templateCallbackPractice;

// Context
public class Soldier {
    void runContext(String weaponSound) {
        System.out.println("전투 시작");
        executeWeapon(weaponSound).runStrategy();
        System.out.println("전투 종료");
    }
    
    private Strategy executeWeapon(final String weaponSound) {
        return new Strategy() {
            @Override
            public void runStrategy() {
                System.out.println(weaponSound);
            }
        };
    }
}

(리팩토링 후)클라이언트

package java.templateCallbackPractice;

public class Client {
    public static void main(String[] args) {
        Soldier rambo = new Soldier();

        rambo.runContext("총..촘초초총");

        System.out.println();
        
        rambo.runContext("카캌...카카카카ㅏㄹ!");
    }
}

리팩토링 이후 클라이언트 코드가 많이 깔끔해졌다. (중복코드를 컨텍스트로 옮겼으므로)

이런식으로 최대한 클라이언트 코드를 짧게 유지하는 듯 하다.

전략 패턴의 일종이므로 역시나 OCPDIP를 활용한다.

스프링을 이해하기 위해 특히나 중요한 패턴들

  1. 전략 패턴
  2. 템플릿 콜백 패턴
  3. 리팩터링 된 템플릿 콜백 패턴
profile
우상향 하는 개발자

0개의 댓글