티스토리 뷰

728x90
반응형
메소드에서 알고리즘의 골격을 정의함.
알고리즘의 여러 단계 중 일부는 서브클래스에서 구현할 수 있음.
알고리즘의 구조는 유지하면서 서브클래스의 특정 단계를 재정의 가능.

public abstract class CaffeineBeverage {        //전체적인 처리 과정을 관리
    final void prepareRecipe() {    //템플릿 메소드. 아무렇게나 음료를 만들지 못하도록 final 선언
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }

    abstract void brew();   //서로 다른 방식으로 처리되는 메소드를 추상화

    abstract void addCondiments();      //메소드를 일반화시켜서 베이스 클래스에 등록

    void boilWater() {      //같은 방식으로 처리되는 메소드
        System.out.println("boil water...");
    }

    void pourInCup() {
        System.out.println("pour in cup...");
    }
}

public class Tea extends CaffeineBeverage{
    @Override
    void brew() {
        System.out.println("brew tea...");
    }

    @Override
    void addCondiments() {
        System.out.println("add lemon...");
    }
}

public class Coffee extends CaffeineBeverage{

    @Override
    void brew() {
        System.out.println("brew coffee...");
    }

    @Override
    void addCondiments() {
        System.out.println("add sugar and milk...");
    }
}

후크(hook) 활용

  • 추상 클래스에서 선언되는 메소드긴 하지만 기본적인 내용만 구현되어 있거나 아무 코드도 들어있지 않은 메소드
  • 서브 클래스에서 후크를 오버라이드할 수도 있고 스킵할 수도 있다.
  • 오버라이드하지 않으면 추상 클래스에서 기본으로 제공한 코드가 실행
  • 필수적이지 않은 부분은 추상 클래스가 아닌 후크로 구현
  • 서브 클래스에서 추상 클래스에 있는 모든 추상 메소드를 구현해야함
  • 추상 메소드가 너무 많아지면 좋지 않음
public abstract class CaffeinBeverageWithHook {
    void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        if (customerWantsCondiments()) {    //hook 메소드 활용
            addCondiments();
        }
    }

    abstract void brew();

    abstract void addCondiments();

    void boilWater() {
        System.out.println("boil water...");
    }

    void pourInCup() {
        System.out.println("pour in cup...");
    }

    boolean customerWantsCondiments() {     //별 내용 없는 기본 메소드로 구현. 서브클래스에서 필요에 따라 오버라이드
        return true;
    }
}

public class CoffeeWithHook extends CaffeinBeverageWithHook{
    @Override
    void brew() {
        System.out.println("brew coffee...");
    }

    @Override
    void addCondiments() {
        System.out.println("add sugar and milk...");
    }

    @Override
    public boolean customerWantsCondiments() {      //hook를 override해서 원하는 기능 구현
        String answer = getUserInput();
        return answer.toLowerCase().startsWith("y");
    }

    private String getUserInput() {
        return "Y";
    }
}
디자인 원칙
1. 애플리케이션에서 달라지는 부분을 찾아내고, 달라지지 않는 부분으로부터 분리시킨다. (캡슐화)
2. 구현이 아닌 인터페이스에 맞춰서 프로그래밍한다.
3. 상속보다는 구성을 활용한다.
4. 서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.
5. 클래스는 확장에 대해서는 열려 있어야하지만 코드 변경에 대해서는 닫혀 있어야 한다.
6. 추상화된 것에 의존하도록 만들어라. 구상 클래스에 의존하도록 만들지 않도록 한다. (Dependency Inversion)
7. 최소 지식 원칙 - 정말 친한 친구하고만 얘기하라.
8. 헐리우드 원칙 - 먼저 연락하지 마세요. 저희가 연락 드리겠습니다.

헐리우드 원칙

  • 의존성 부패(dependency rot)를 방지
  • 의존성이 복잡하게 꼬여있는 것을 의존성 부패
  • 저수준 구성요소에서 시스템에 접속을 할 수는 있지만,
  • 언제 어떤 식으로 그 구성요소들을 사용할지는 고수준 구성요소에서 결정

헐리우드 원칙 vs 의존성 역전 원칙

  • 두 원칙은 객체를 분리시킨다는 공통의 목표
  • 디자인 상의 의존성을 피하는 방법에 있어서 DI가 더 강하고 일반적인 내용을 담음
  • 헐리우드 원칙은 저수준 구성요소들을 다양하게 사용할 수 있으면서도,
  • 다른 클래스가 그러한 구성요소에 너무 의존하지 않게 만들어주는 디자인을 구현하기 위한 기법을 제공

템플릿 메소드 패턴 vs 스트래티지 패턴

템플릿 메소드 패턴 스트래티지 패턴
알고리즘의 개요를 정의하고 작업 중 일부는 서브클래스에서 처리 알고리즘군을 정의하고 알고리즘들을 서로 바꿔가면서 쓸 수 있게 처리
중복되는 코드는 수퍼클래스에 들어있고 서브클래스 각 단계마다 다른 구현을 사용 클라이언트에게 객체 구성을 통해 알고리즘 구현을 선택할 수 있게 제공
효율적이고 객체 개수가 적음. 코드를 재사용 객체 구성으로 더 유연함 다른 스트래티지 객체를 사용하기만 하면 알고리즘을 변경 가능
수퍼클래스에 의존성 어떤 것에도 의존하지 않음
728x90
반응형
반응형
300x250