## 23. Composite, Command, Observer 디자인 패턴, 추상 클래스/메서드 활용하기
- Composite 패턴을 활용하여 메뉴 구현하기
- BreadcrumbPrompt에 적용
- Menu, MenuGroup 클래스 정의
ㄴ 폴더가 폴더를 담을 수 있음 => 메뉴도 메뉴를 담을 수 있음
Composite 패턴
ㄴ 구조패턴(Structural Patterns)에 있는 Composite(합성패턴)
ㄴ 객체들의 관계를 트리구조로 구성하여 부분-전체 계층을 표현하는 패턴
bitcamp.util 패키지에 Menu 클래스 생성
테스트를 위해 패키지 P1 생성
ㄴ bitcamp.test 패키지에 p1 패키지 생성
ㄴ 생성한 p1 패키지에 A 클래스를 생성
A.java
ㄴ 접근제어자 테스트 위해 A 클래스 생성
Test1.java
ㄴ private 으로 선언된 인스턴스 필드 v1 는 접근 불가
Test2.java
ㄴ bitcamp.test 패키지에 Test2 클래스 생성
ㄴ A 클래스와 패키지가 다르므로 private 으로 선언된 인스턴스 필드 v1, default 로 선언된 인스턴스 필드 v2, protected 로 선언된 인스턴스 필드 v3는 접근 불가
Test3.java
ㄴ bitcamp.test 패키지에 Test3 클래스 생성
Test3.java
ㄴ A 클래스와 패키지가 다르므로 private 으로 선언된 인스턴스 필드 v1, default 로 선언된 인스턴스 필드 v2는 접근 불가
ㄴ Test3 클래스가 A 클래스를 상속은 받았지만 obj 는 A 클래스의 인스턴스를 생성하므로 obj 로 다른 패키지에 있는 A 클래스의 protected 로 선언된 v3 인스턴스 필드는 접근 불가
ㄴ m() 도 상속받은 멤버가 아니므로 접근 불가
A.java
ㄴ m() 메서드의 접근제어를 protected 로 변경
Test2.java
ㄴ A 클래스의 m() 메서드는 같은 패키지도 아니고 서브 클래스도 아니므로 접근 불가
Test3.java
ㄴ m2() 는 같은 스태틱 멤버이므로 그냥 호출 가능
Composite 패턴을 활용하여 메뉴 구현하기
BreadcrumbPrompt에 적용 및 Menu, MenuGroup 클래스 정의
Menu.java
ㄴ title 을 받는 생성자 추가
Menu.java
ㄴ title 이 private 이므로 getter 를 이용해 title 을 받아올 수 있도록 함
Menu.java
App.java
ㄴ 생성한 Menu 클래스 사용하기 위해 import 해주기
Menu.java
ㄴ 메뉴를 실행할 메서드 execute() 생성
MenuGroup.java
ㄴ bitcamp.util 패키지에 MenuGroup 클래스 생성
=> bitcamp.util 패키지에 있는 ArrayList 클래스를 사용하고 Menu 클래스를 상속받도록 함
MenuGroup.java
ㄴ MenuGroup 클래스의 생성자에서는 super 키워드를 사용하여 상위 클래스인 Menu 클래스의 생성자를 호출해야 함
=> super() 생성자를 호출하는 코드를 작성하지 않으면 컴파일러가 자동으로 수퍼 클래스인 Menu 클래스의 기본 생성자를 호출함
ㄴ 하지만 현재, 수퍼 클래스인 Menu 클래스에서 default constructor 가 정의되지 않아서 에러 발생
=> 암시적 슈퍼 생성자 Menu()가 정의되지 않아 다른 생성자를 명시적으로 호출해야 함
• Student(){…}; => 자신의 필드만 초기화 시킴
=> Member() 를 상속하고 있으므로 super(); 를 호출해줘야 함
• super(); 를 컴파일러가 자동 삽입하는데,수퍼 클래스에 기본생성자가 없으면
자식 클래스에서 명시적으로 선언해줘야 함
•super(); 문장은 생성자의 가장 처음에 작성되어야 함
MenuGroup.java
=> ArrayList menu = new ArrayList(); 코드를 생성자 바깥쪽에 생성해도 컴파일 시 아래처럼 변경되어 실행됨
ㄴ menu 변수에는 새로운 ArrayList 객체가 할당됨
=> MenuGroup 클래스의 객체를 생성할 때 title 값을 전달하여 초기화할 수 있으며, menu 변수는 빈 ArrayList로 초기화됨
MenuGroup.java
ㄴ 보통 자식 클래스를 의미하는 childs 라는 이름의 변수를 사용
=> 일반적으로 메뉴 그룹은 다른 메뉴들을 포함할 수 있으므로, childs 리스트는 해당 그룹에 속한 메뉴들을 저장함
ㄴ addMenu(Menu menu) 메서드를 생성하여 ArrayList 클래스의 add(Object obj) 메서드를 사용하도록 코드 작성
MenuGroup.java
ㄴ Menu 클래스에 있는 execute() 메서드를 재정의(Overriding)
=> execute() 메서드 실행 시 Menu 객체 menu 에 childs 변수에 저장된 자식 메뉴들을 순회하면서 각 메뉴의 제목 출력
=> 마지막으로 "0. 이전/종료" 메뉴를 출력
App.java
ㄴ MenuGroup 클래스 사용하기 위해 코드 작성
App.java
ㄴ MenuGroup 객체인 main을 생성 ("메인"이라는 title 을 가지고 있음)
ㄴ main에 세 개의 자식 메뉴를 추가 (각각 "직원", "물품", "게시글", "공지"이라는 title을 가진 Menu 객체)
=> main은 "메인" 메뉴 그룹을 나타내고, 그 아래에 "직원", "물품", "게시글", "공지" 네 개의 자식 메뉴가 포함되어 있는 구조
App.java
ㄴ 잠시 해당 블럭 주석으로 막아두기
App.java
ㄴ main -> mainMenu로 변수명 변경 후 MenuGroup 의 인스턴스 변수 mainMenu 가 가리키는 MenuGroup 클래스의 execute() 를 실행함
App.java
ㄴ prompt 를 파라미터로 추가하여 getMenu() 를 각각 호출할 수 있도록 만들어줌
=> prompt 의존객체를 생성자에 넘겨줌
Menu.java
ㄴ MenuPrompt 파라미터를 추가해줌
MemberGroup.java
ㄴ execute 메서드에 MenuPrompt 파라미터 추가
ㄴ 현재 MenuGroup 객체의 타이틀을 prompt에 추가
ㄴ getTitle() 메서드는 현재 MenuGroup 객체의 타이틀을 반환
ㄴ null은 해당 메뉴를 선택했을 때 실행할 핸들러가 없음을 나타냄 (주석 막아놨으므로 일단 null 로 설정)
MemberGroup.java
ㄴ Menu 출력 형식 부분을 printMenu() 메소드로 분리
MemberGroup.java
ㄴ prompt.inputMenu() 로 받아온 String 타입을 int 타입으로 변환시켜 menuNo 에 넣음
ㄴ 해당 번호가 0보다 작거나 childs 리스트의 크기보다 크다면 "메뉴 번호가 옳지 않습니다" 출력하도록 함
ㄴ 해당 번호가 0이라면 prompt.removeBreadcrumbs(); 코드를 실행하고 호출한 곳으로 리턴됨
ㄴ 해당 번호가 0 과 childs 리스트의 크기 사이의 값이라면 ArrayList 의 get(int index) 를 이용하여 해당 번호에 대한Object 값을 가져옴 => Menu 타입으로 형변환 해줘야 함, 종료를 제외한 menuNo 는 1부터 시작이므로 menuNo - 1 을 해줘야 함
MemberGroup.java
ㄴ 무한 루프를 추가해줘서 0일 경우만 무한루프를 나가도록 만듦
MemberGroup.java
ㄴ 입력 문자열이 "menu" 와 같다면 printMenu() 를 실행하도록 하고 continue; 를 이용해 다시 while문의 처음으로 가서 무한반복
MenuPrompt.java
ㄴ menus 스택 선언을 제거
MenuPrompt.java
ㄴ appendBreadcrumbs 메서드의 파라미터 String menu도 제거해야됨
ㄴ 선택한 내용 모두 제거
MenuPrompt.java
ㄴ inputMenu() 메서드의 리턴값 수정
MenuPrompt.java -> BreadcrumbPrompt.java
ㄴ MenuPrompt -> BreadcrumbPrompt 로 변경(Refactor 이용)
MenuGroup.java
=>
App.java
=>
ㄴ getMenu() 지우기
App.java
ㄴ 해당 코드 제거
App.java
=>
ㄴ prompt 모두 제거
App.java
ㄴ MenuGroup 객체 mainMenu 의 execute 메서드에 아규먼트로 prompt 를 전달함
현재 상태에서 테스트 실행
BoardcrumbPrompt.java
=>
ㄴ addMenu -> add 로 변경 (Refactor 이용)
App.java
ㄴ 임시적으로 주석 막아뒀던 코드 완전히 제거
App.java
ㄴ prompt 적용