해당 포스팅은 한빛 미디어 헤드퍼스트 디자인 패턴(에릭 프리먼, 엘리자베스 롭슨 저)를 통해 공부한 내용을 정리한 블로그입니다.

아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
좋은 프로그래머는 자기 두뇌를 사용한다. 그러나 좋은 가이드라인은 모든 케이스를 고려해야만 하는 노력을 줄여준다.
(Francis Glassborow, 개발자)

Coffee 클래스와 Tea 클래스 만들기


  • 커피와 홍차 만드는 방법은 비슷.
  • 비슷한 클래스는 공통된 부분을 추상화해서 베이스 클래스로 만들면 좋은 방법
  • 추상화 클래스 적용.

  • Coffee 클래스에서도 Tea 클래스에서도 추상화 가능한 메소드를 추가
    • brewCoffeeGrinds(), steepTeaBag() 의 역할이 비슷
    • addSugarAndMilk(), addLemon()의 역할이 비슷
    • 각각 brew()addCondiments()로 추상화
  • 일반화를 최대한 적용. ⇒ CaffeinBeverage 클래스에 반영
  • 일부 메서드를 서브 클래스에 의존 ⇒ 각각의 brew()addCondiments()를 추상화

Template Method Pattern


  • prepareRecipe()는 템플릿 메소드
    • 메소드이자 카페인 음료수를 만드는 알고리즘의 템플릿
  • 이전 코드
    • 음료 코드 각자가 알고리즘 수행.
    • 중복, 분산 코드가 많음.
    • 알고리즘이 바뀌면 수정해야 할 부분이 여러군데임.
  • 템플릿 메소드 적용 코드
    • 알고리즘을 CaffeineBeverage가 독점
    • 서브 클래스에서 코드를 재사용 가능.
    • 다른 음료도 쉽게 추가할 수 있는 알고리즘 제공. => 알고리즘의 템플릿 만들기
💡 템플릿 메소드
알고리즘의 골격을 정의. 템플릿 메소드를 사용하면 알고리즘의 일부 단계를 서브 클래스에서 구현할 수 있으며, 알고리즘의 구조는 그대로 유지하면서 알고리즘의 특정 단계를 서브 클래스에서 재정의.

Example

public abstract class CaffeineBeverage {
    final void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }

    abstract void brew();
    abstract void addCondiments();

    public void pourInCup() {
        System.out.println("컵에 따르는 중");
    }
    public void boilWater() {
        System.out.println("물 끓이는 중");
    }
}

public class Coffee extends CaffeineBeverage{

    @Override
    public void brew() {
        System.out.println("필터로 커피 우려내는 중");
    }

    @Override
    public void addCondiments() {
        System.out.println("설탕과 우유를 추가하는 중");
    }
}

public class Tea extends CaffeineBeverage{

    @Override
    public void brew() {
        System.out.println("찻잎을 우려내는 중");
    }

    @Override
    public void addCondiments() {
        System.out.println("레몬을 추가하는 중");
    }
}

hook

  • 기본적으로 아무것도 하지 않는 구상 메소드를 정의.
  • 서브 클래스에서 오버라이드할 수도 있고 아닐 수도 있음.
    • 서브 클래스에서 메소드를 커스텀해서 사용할 수 있음.
public class CoffeeWithHook extends CaffeineBeverageWithHook {
 
	public void brew() {
		System.out.println("Dripping Coffee through filter");
	}
 
	public void addCondiments() {
		System.out.println("Adding Sugar and Milk");
	}
 
	public boolean customerWantsCondiments() {

		String answer = getUserInput();

		if (answer.toLowerCase().startsWith("y")) {
			return true;
		} else {
			return false;
		}
	}
 
	private String getUserInput() {
		String answer = null;

		System.out.print("Would you like milk and sugar with your coffee (y/n)? ");

		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		try {
			answer = in.readLine();
		} catch (IOException ioe) {
			System.err.println("IO error trying to read your answer");
		}
		if (answer == null) {
			return "no";
		}
		return answer;
	}
}

고찰

  • 제공해야만 하는 기능 ⇒ 추상 메소드
  • 서브 클래스 전용 커스텀후크
  • 추상 메소드가 너무 많아지는 문제.
    • 템플릿 메소드를 만들 때에 이 점을 염두.
    • 모든 단계가 필수가 아니므로 후크 사용

할리우드 원칙


먼저 연락 X. 연락 주겠어

  • 할리우드 원칙을 사용하면 dependency rot를 방지.
    • 의존성 부패 ⇒ 고수준 구성요소와 저수준 구성요소가 서로서로 의존하는 형태.

할리우드 원칙과 템플릿 메소드 패턴

  • CaffeineBeverage 클래스는 고수준 구성요소
  • Tea, Coffee(구상 클래스)가 고수준 구성요소를 호출하지 않음.

프레임 워크를 만드는데 적절한 패턴

  • Arrays.sort() 에서 사용되는 패턴
    • 서브 클래스를 구현하기 위해서 Comparable 인터페이스 제공을 통해 compareTo() 구현하도록 유도
  • JFrame 클래스에서 사용
    • paint() 함수가 후크

+ Recent posts