이전에 만들었던 어플리케이션에 디자인 패턴을 공부하고 적용해보며
더 객체지향 프로그래밍과 가깝고 유지보수가 용이하도록 만들어 보는 시간을 가져보겠습니다.
1. 현재 시스템 구조와 한계
제가 만든 시스템은 이전 글에 설명했듯이
다음과 같은 구조를 띄고 있습니다.
하지만 이미 존재하는 메뉴에 새로운 메뉴가 추가 될 수 있습니다.
다음과 같이 말이죠
이때 부터 구조적인 문제가 나타납니다.
상위 메뉴에 새로운 하위 메뉴 항목이 늘어날때마다 클래스를 수정해야합니다.
이러한 문제를 해결하기 위해 컴포지트 패턴을 이용할 수 있습니다.
2. Composite 패턴
컴포지트 패턴은 단일객체와 복합객체를 동일한 개념으로 처리합니다.
단일객체와 복합객체를 동일한 개념으로 처리하게 되면 복합객체가 또다른 복합객체나 단일객체를 참조하게하여
다음과 같은 트리 형태의 객체 구조를 만들 수 있습니다.
다음과 같이 복합객체(Composite)와 단일객체(Leaf)는 같은 인터페이스를 구현하여
같은 타입의 객체로 처리가 가능합니다.
3. Command 패턴
Command 패턴은 요청(명령)을 객체로 캡슐화하여 클라이언트가 요청을 직접 처리하지 않고,
명령 객체를 통해 실행할 수 있도록 하는 패턴입니다.
즉, 명령을 객체화 하여 행동을 요청하는 객체와 실제로 행동을 수행하는 객체를 분리합니다.
이를 통해 서로 독립적으로 관리할 수 있습니다.
사실, 각 메뉴별 CRUD를 객체화하는것은 부자연스럽다는 의문이 들지만 여기서는 학습을 위해,
그리고 단일객체와 복합객체의 명확한 분리를 위해 Command 패턴을 적용해보았습니다.
보통 Command 패턴은 사용자가 텍스트 에디터를 사용할 때와 같이 실행 취소(Undo) 및 다시 실행(Redo) 기능이 필요하며
하나의 작업을 객체화할때 유용하게 사용될 수 있습니다.
4. 결과
단일 객체는 회원가입, 회원상세와 같은 기능들을 각각의 객체로 처리합니다.
그리고 복합 객체는 이런 기능을갖는 객체의 모음 즉, 메뉴가 될것입니다.
단일객체인 기능들과 복합객체인 메뉴를 동일한 개념으로 처리하기 위해
지금 부터는 기능을 메뉴로 바라봐야 합니다.
단, 기능과 메뉴를 구별하기 위해 기능은 Menu 인터페이스를 구현한 MenuItem이라는 클래스로,
메뉴는 Menu 인터페이스를 구현한 MenuGroup이라는 클래스로 감싸서 조립하는 형태로 만들어 보았습니다.
구조는 다음과 같습니다.
따라서 다음과 같이 어플리케이션의 엔트리 포인트인 Main.class의 main 메서드 내부를 작성할 수 있습니다.
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
System.out.println("콘솔 게시판 프로그램 시작");
MenuGroup mainMenu = new MenuGroup("메인 메뉴");
MenuGroup boardMenu = new MenuGroup("게시판 메뉴");
BoardRepository boardRepository = new BoardRepository();
boardMenu.add(new MenuItem("게시글 생성", new BoardCreateHandler(in, boardRepository)));
boardMenu.add(new MenuItem("게시글 상세", new BoardViewHandler(in, boardRepository)));
boardMenu.add(new MenuItem("게시글 수정", new BoardModifyHandler(in, boardRepository)));
boardMenu.add(new MenuItem("게시글 삭제", new BoardDeleteHandler(in, boardRepository)));
boardMenu.add(new MenuItem("게시글 목록", new BoardListHandler(in, boardRepository)));
mainMenu.add(boardMenu);
MenuGroup assignmentMenu = new MenuGroup("과제 메뉴");
AssignmentRepository assignmentRepository = new AssignmentRepository();
assignmentMenu.add(new MenuItem("과제 생성", new AssignmentCreateHandler(in, assignmentRepository)));
assignmentMenu.add(new MenuItem("과제 상세", new AssignmentViewHandler(in, assignmentRepository)));
assignmentMenu.add(new MenuItem("과제 수정", new AssignmentModifyHandler(in, assignmentRepository)));
assignmentMenu.add(new MenuItem("과제 삭제", new AssignmentDeleteHandler(in, assignmentRepository)));
assignmentMenu.add(new MenuItem("과제 목록", new AssignmentListHandler(in, assignmentRepository)));
mainMenu.add(assignmentMenu);
MenuGroup memberMenu = new MenuGroup("회원 메뉴");
MemberRepository memberRepository = new MemberRepository();
memberMenu.add(new MenuItem("회원 가입", new MemberCreateHandler(in, memberRepository)));
memberMenu.add(new MenuItem("회원 상세", new MemberViewHandler(in, memberRepository)));
memberMenu.add(new MenuItem("회원 수정", new MemberModifyHandler(in, memberRepository)));
memberMenu.add(new MenuItem("회원 탈퇴", new MemberResignHandler(in, memberRepository)));
memberMenu.add(new MenuItem("회원 목록", new MemberListHandler(in, memberRepository)));
mainMenu.add(memberMenu);
mainMenu.execute(in);
}
}
이렇게 하면 기존의 클래스를 수정하지 않고 추가적인 메뉴를 구성할 수 있게 됩니다.
전체코드:
'OOP' 카테고리의 다른 글
VO 객체 도입하기 (0) | 2024.11.12 |
---|---|
콘솔 게시판앱을 만들며 Class와 Interface, DI 이해하기 - 1 (0) | 2024.10.10 |