[๋์์ธํจํด] ์ปดํฌ์งํธ ํจํด(Composite Pattern)
ํ๊ทธ: Design Pattern
์นดํ ๊ณ ๋ฆฌ: Design Pattern
์ปดํฌ์งํธ ํจํด(Composite pattern)
์ปดํฌ์งํธ ํจํด์ ๋ํด ์์๋ณด๊ณ , ๋์ ์ ๊ณผ ํ์ ์ฐจ์ด์ ์ ์์๋ด ๋๋ค.
๋ค์๊ณผ ๊ฐ์ด ๊ตฌํ๋ ๋ฉ๋ด ์ถ๋ ฅ ํ๋ก๊ทธ๋จ์ด ์๋ค.
1
2
3
4
5
6
7
  [ ๋ฉ์ธ ๋ฉ๋ด ]
  1. ๊ณผ์ 
  2. ๊ฒ์๊ธ
  3. ํ์
  4. ๊ฐ์
์ธ์ฌ
  5. ๋์๋ง
  0. ์ข
๋ฃ
๋ฉ์ธ ๋ฉ๋ด์์ ํด๋น ๋ฒํธ๋ฅผ ๋๋ฅด๋ฉด ์๋ธ๋ฉ๋ด๊ฐ ์ถ๋ ฅ๋๋ค. ์๋ฅผ๋ค์ด ์ฌ์ฉ์๋ก ๋ถํฐ 2๋ฅผ ์ ๋ ฅ๋ฐ์ ๊ฒ์๊ธ ๋ฉ๋ด๋ก ๋ค์ด๊ฐ๋ฉด ์๋์ ๊ฐ์ ๋ฉ๋ด๊ฐ ์ถ๋ ฅ๋๋ค.
1
2
3
4
5
6
7
  [ ๊ฒ์๊ธ ]
  1. ๋ฑ๋ก
  2. ์กฐํ
  3. ๋ณ๊ฒฝ
  4. ์ญ์ 
  5. ๋ชฉ๋ก
  0. ์ด์ 
Before
๊ฐ๊ฐ์ ๋ฉ๋ด ํด๋์ค์ ์ ๋ ฅ์ ๋ฐ๋ ์์ค์ฝ๋๊ฐ ์กด์ฌํ๊ณ ๊ทธ ์ ๋ ฅ์ ์ฒ๋ฆฌํ๋ ๊ณผ์ ๋ ๊ฐ๊ฐ์ ๋ฉ๋ด ํด๋์ค์ ๊ตฌํ๋์ด ์๋ค.
MainMenu.class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
  public class MainMenu {
  
      ...
  
    Menu assignmentMenu = new AssignmentMenu("๊ณผ์ ", this.prompt);
    Menu boardMenu = new BoardMenu("๊ฒ์๊ธ", this.prompt);
    Menu greetingMenu = new BoardMenu("๊ฐ์
์ธ์ฌ", this.prompt);
    Menu memberMenu = new MemberMenu("ํ์", this.prompt);
    Menu helpMenu = new HelpMenu("๋์๋ง", this.prompt);
    
    printMenu(); // ๋ฉ์ธ๋ฉ๋ด ์ถ๋ ฅ
  
    while(true) {
    String input = this.prompt.input("๋ฉ์ธ> ");
      switch (input) {
        case "1":
          assignmentMenu.execute(prompt);
          break;
        case "2":
          boardMenu.execute(prompt);
          break;
        case "3":
          memberMenu.execute(prompt);
          break;
        case "4":
          greetingMenu.execute(prompt);
          break;
        case "5":
          helpMenu.execute(prompt);
          break;
        case "0":
          System.out.println("์ข
๋ฃํฉ๋๋ค.");
          return;
        case "menu":
          this.printMenu();
          break;
        default:
          System.out.println("๋ฉ๋ด ๋ฒํธ๊ฐ ์ณ์ง ์์ต๋๋ค.");
      }
    } 
  }
BoardMenu.class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
public class BoardMenu {
  public void execute(Prompt prompt) {
  
      ...
    Board[] boards = new Board[3]; // ๊ฒ์๊ธ ์ ์ฅํ  ๋ฐฐ์ด
    int length = 0; // ํ์ฌ ๊ฒ์๊ธ ์ ์ฅ ์ธ๋ฑ์ค
    this.printMenu(); // ์๋ธ๋ฉ๋ด ์ถ๋ ฅ
    while (true) {
      String input = prompt.input("๋ฉ์ธ/%s> ", this.title);
      switch (input) {
        case "1":
          this.add();
          break;
        case "2":
          this.view();
          break;
        case "3":
          this.modify();
          break;
        case "4":
          this.delete();
          break;
        case "5":
          this.list();
          break;
        case "0":
          return;
        case "menu":
          this.printMenu();
          break;
        default:
          System.out.println("๋ฉ๋ด ๋ฒํธ๊ฐ ์ณ์ง ์์ต๋๋ค!");
      }
    }
    void add () {
      // ๊ฒ์๊ธ ๋ฑ๋ก  
    }
    void list () {
      // ๊ฒ์๊ธ ๋ชฉ๋ก
    }
    void view () {
      // ๊ฒ์๊ธ ์์ธ์กฐํ  
    }
    void modify () {
      // ๊ฒ์๊ธ ์์ 
    }
    void delete () {
      // ๊ฒ์๊ธ ์ญ์ 
    }
  }
}
์ ์์ค์ฝ๋ ์ฒ๋ผ switch ๋ฌธ์ผ๋ก ์
๋ ฅ ๋ฐ์ดํฐ์ ๋ํ ๋ถ๊ธฐ์ฒ๋ฆฌ๋ฅผ ๊ฐ ๋ฉ๋ด ํด๋์ค๋ง๋ค ์ํํ๊ณ  ์๋ค.
์๋ก์ด ๋ฉ๋ด๊ฐ ์ถ๊ฐ๋  ๋๋ง๋ค MainMenu ์ ์๋ธ๋ฉ๋ด ์ธ์คํด์ค๋ฅผ ์์ฑํด์ผํ๊ณ  case ๋ฅผ ์ถ๊ฐํด์ผ ํ๋ค. 
๊ทธ๋ฆฌ๊ณ  ํ์ฌ ์ฝ๋๋ ๋ชจ๋  ์๋ธ๋ฉ๋ด ํด๋์ค๊ฐ ์ค๋ณต ์ฝ๋๋ฅผ ๋ค์ ํฌํจํ๊ณ  ์๋ค. (switch ๋ก ์
๋ ฅ์ ์ฒ๋ฆฌํ๋ ๋ถ๋ถ, CRUD ๊ธฐ๋ฅ)
๋ํ ๋ฉ๋ดํด๋์ค๊ฐ ์๋ก ๊ณ์ธต๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๊ณ  ์์์๋, ์ด๊ฒ์ด ์ฝ๋์์ ์ ํํ์ด ๋์ง ์๋๋ค.
๋ฐ์ดํฐ ๊ทธ ์์ฒด๋ฅผ ํธ๋ค๋ง ํ๋ ์์ค์ฝ๋๋ ์ ์ง๋ณด์ํ๊ธฐ ํ๋ค๊ณ ๊ฐ์ฒด์งํฅ ํจ๋ฌ๋ค์๊ณผ๋ ๋ง์ง ์๋ค. ์ปดํฌ์งํธ ํจํด์ SOLID ๊ฐ์ฒด์งํฅ ์๋ฆฌ ์ค OCP(Open/Closed Principle) ๋ฅผ ๋ง์กฑํ๋ค.
After
๊ธฐ์กด์ switch ๋ก ๋ฐ์ดํฐ๋ฅผ ์
๋ ฅ ๋ฐ์์ ๋ฉ๋ด๋ฅผ ํธ์ถํ๋ ๋ฐฉ์์ด ์๋, 
๋ฉ๋ด๋ฅผ ์์๋ฉ๋ด ๊ฐ์ฒด์ ํ์ ๋ฉ๋ด ๊ฐ์ฒด๋ก ์บก์ํ ํ๊ณ  ๊ฐ์ฒด๋ผ๋ฆฌ ์ํธ์์ฉํ๋๋ก ์์ ํ๋ค.
ํด๋์ ํ์ผ์ ๊ตฌ์กฐ์ฒ๋ผ ๊ณ์ธต์ ์ธ ํธ๋ฆฌ๊ตฌ์กฐ์ ํ์์ ์ทจํ๋ค.

์ฐ์  Menu์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํด MenuGroup๊ณผ MenuItem์ ๊ณตํต์ ์ผ๋ก ์ฒ๋ฆฌํ  ์ ์๊ฒ ๋ง๋ ๋ค.
MenuGroup ์ ๋ฐ๋ณต์ ์ผ๋ก Menu๋ฅผ ๊ฐ์ง ์ ์๋ค. ์ฆ, MenuGroup ๋๋ MenuItem ์ ๊ฐ์ง ์ ์๋ค.(add() ๋ก ๋ฐฐ์ด์ ์ถ๊ฐ)
๋ฐ์ดํฐ๋ฅผ ์ด๋ฐ์์ผ๋ก ์กฐ์งํํ๋ค ๋ณด๋ฉด ๋ณตํฉ๊ฐ์ฒด(MenuGroup)๋ฅผ ๋ฐ๋ผ์ ๊ฐ์ง๊ฐ ์ฐ๊ฒฐ๋๋ค๊ฐ ๋ง์ง๋ง์๋ ์(MenuItem)์ผ๋ก ๋๋๋ ํธ๋ฆฌ๊ตฌ์กฐ๊ฐ ๋ง๋ค์ด์ง๋ค.
MenuGroup.class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class MenuGroup implements Menu {
  String title;
  Menu[] menus = new Menu[10];
  int menuSize;
  public MenuGroup(String title) {
    this.title = title;
  }
  public void add(Menu menu) {
    // menus ๋ฐฐ์ด์ ์๋ธ๋ฉ๋ด๋ฅผ ๋ฃ๋๋ค
  }
  public void remove(Menu menu) {
    // menus ๋ฐฐ์ด์์ ์๋ธ๋ฉ๋ด ์ญ์ 
  }
  
  @Override
  public void execute(Prompt prompt) {
    this.printMenu();
    // ๋ฉ๋ด ๋ฒํธ ์
๋ ฅ ๋ฐ ๋ฉ๋ด ์ถ๋ ฅ
    // menus ๋ฐฐ์ด์ ์๋ ์๋ธ๋ฉ๋ด ์คํ(execute)
    
  }
}
๊ธฐ์กด ์์ค์์๋ add(),remove() ์ ๊ฐ์ด ๋ฉ๋ด๋ฅผ ์ถ๊ฐํ๊ณ  ์ญ์ ํ๋ ๊ธฐ๋ฅ์ด ๊ฐ๊ฐ์ ๋ฉ๋ด์ ์กด์ฌํ๋ค.
์ด์  ์๋ก์ด ๋ฉ๋ด๋ฅผ ๋ง๋๋ ์์
์ MenuGroup์์ ํ๋ค.
MenuGroup์์ ์๋ธํด๋์ค ์์๋ค์ ๊ด๋ฆฌํ๊ธฐ ์ํ menus ๋ฐฐ์ด์ด ์๊ณ  ํด๋น ๋ฐฐ์ด์ ์ถ๊ฐ/์ญ์ ๋ add(), remove()๋ฅผ ํตํด์ ํ๋ค.
MenuItem
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class MenuItem implements Menu {
  String title;
  MenuHandler menuHandler;
  public MenuItem(String title) {
    this.title = title;
  }
  public MenuItem(String title, MenuHandler menuHandler) {
    this(title);
    this.menuHandler = menuHandler;
  }
  @Override
  public void execute(Prompt prompt) {
    if (this.menuHandler != null) {
      this.menuHandler.action();
    }
  }
  @Override
  public String getTitle() {
    return this.title;
  }
์ปดํฌ์งํธ ํจํด์์ leaf ์ ํด๋นํ๋ ๋ถ๋ถ์ด๋ค. leaf ๋ถ๋ถ์ CRUD ์์
์ ๋ํ MenuHandler ๋ฅผ ์ฃผ์
๋ฐ์ ์ฌ์ฉํ๋ค.
์ด๋ ๊ธฐ์กด์ ๋ฉ๋ด ๊ฐ์ฒด์ ์์ฌ์๋ ๋ฉ๋ด ์ถ๋ ฅ๊ณผ CRUD ๊ธฐ๋ฅ ๋ถ๋ถ์ ๋ถ๋ฆฌ์ํค๊ธฐ ์ํจ์ด๋ค.
Client
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class Client {
  public static void main(String[] args) {
    ...
    Prompt prompt = new Prompt(System.in);
    //    new MainMenu(prompt).execute(); 
    BoardRepository boardRepository = new BoardRepository();
    MenuGroup mainMenu = new MenuGroup("๋ฉ์ธ");
    MenuGroup assignmentMenu = new MenuGroup("๊ณผ์ ");
    MenuGroup boardMenu = new MenuGroup("๊ฒ์๊ธ");
    MenuGroup memberMenu = new MenuGroup("ํ์");
    MenuGroup greetingMenu = new MenuGroup("๊ฐ์
์ธ์ฌ");
    MenuGroup helpMenu = new MenuGroup("๋์๋ง");
    mainMenu.add(assignmentMenu);
    assignmentMenu.add(new MenuItem("๋ฑ๋ก"));
    assignmentMenu.add(new MenuItem("์กฐํ"));
    assignmentMenu.add(new MenuItem("๋ณ๊ฒฝ"));
    assignmentMenu.add(new MenuItem("์ญ์ "));
    assignmentMenu.add(new MenuItem("๋ชฉ๋ก"));
    mainMenu.add(boardMenu);
    boardMenu.add(new MenuItem("๋ฑ๋ก", new BoardAddHandler(boardRepository, prompt)));
    boardMenu.add(new MenuItem("์กฐํ", new BoardViewHandler()));
    boardMenu.add(new MenuItem("๋ณ๊ฒฝ", new BoardModifyHandler()));
    boardMenu.add(new MenuItem("์ญ์ ", new BoardDeleteHandler()));
    boardMenu.add(new MenuItem("๋ชฉ๋ก", new BoardListHandler(boardRepository)));
  }
}
์ปดํฌ์งํธ ํจํด์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ์ฌ์ฉ์๊ฐ ๋จ์ผ ๊ฐ์ฒด์ ๋ณตํฉ ๊ฐ์ฒด ๋ชจ๋ ๋์ผํ๊ฒ ๋ค๋ฃฐ ์ ์๋ค. 
๊ธฐ์กด์ new MainMenu(prompt).execute() ๋ก ๊ฐ ๋ฉ๋ด๋ค์ ํธ์ถํ๋ค๋ฉด ์ด์ ๋ ๋ฉ๋ด์ ์ข
์๊ด๊ณ๊ฐ ์ฝ๋์์ ํ์คํ ํํ๋๊ณ , ์ถ๊ฐ/์ญ์ ๋ฅผ ๊ฐํธํ ํ  ์ ์๋ค.
      
๋๊ธ๋จ๊ธฐ๊ธฐ