Writing /volume1/Web/Public/dokuwiki/data/log/deprecated/2024-11-15.log failed

差分

このページの2つのバージョン間の差分を表示します。

この比較画面へのリンク

両方とも前のリビジョン前のリビジョン
次のリビジョン
前のリビジョン
study:java:design_pattern:composite [2008/09/14 06:45] bananastudy:java:design_pattern:composite [2008/09/14 09:08] (現在) banana
行 24: 行 24:
  
 이제 컴포지트 패턴의 클래스 다이어그램을 살표볼 차례입니다. 이제 컴포지트 패턴의 클래스 다이어그램을 살표볼 차례입니다.
 +
 +
 +
  
  
行 37: 行 40:
 메소드도 있을 것입니다. 하지만 그런 문제를 해결하는 방법은 잠시 후에 알아보도록 하겠습니다. 일단 지금은 메소드도 있을 것입니다. 하지만 그런 문제를 해결하는 방법은 잠시 후에 알아보도록 하겠습니다. 일단 지금은
 메뉴를 어떤 식으로 컴포지트 패턴에 끼워 맞출 수 있을지 생각해 봅시다. 메뉴를 어떤 식으로 컴포지트 패턴에 끼워 맞출 수 있을지 생각해 봅시다.
 +
 {{:study:java:design_pattern:menucomposite.jpg|Menu Composite}} {{:study:java:design_pattern:menucomposite.jpg|Menu Composite}}
 +
 +
 +===== MenuComponent =====
 +이제 **%%MenuComponent%%** 추상 클래스부터 시작해 봅시다. 메뉴 구성요소는 잎 노드와 복합노드 모두에서
 +쓰이는 인터페이스 역할을 한다는 점을 꼭 기억해 둡시다. 어쩌면 "이 **%%MenuComponent%%**에서 두가지 
 +역할을 맡고 있는 것이 아닌가?"라는 생각을 하실지 모르겠습니다. 물론 그렇게 생각할 수 있지만, 그 점에
 +대해서는 나중에 다시 생각해 보도록 하겠습니다. 일단 지금은 %%MenuItem%%(잎)이나 Menu(복합 객체)에서 각자
 +용도에 맞지 않아서 구현할 필요가 없는 메소드에 대해서는 그냥 기본 메소드를 그대로 쓸 수 있도록 기본 구현
 +을 만들어 보도록 하겠습니다.
 +
 +<code java>
 +public abstract class MenuComponent{
 +    
 +    public void add(MenuComponent menuComponent) {
 +         throw new UnsupportedOperationException();
 +    }
 +    public void remove(MenuComponent menuComponent) {
 +         throw new UnsupportedOperationException();
 +    }
 +    public MenuComponent getChild(int i) {
 +         throw new UnsupportedOperationException();
 +    }
 +
 +    public String getName() {
 +         throw new UnsupportedOperationException();
 +    }
 +    public String getDescription() {
 +         throw new UnsupportedOperationException();
 +    }
 +    public double getPrice() {
 +         throw new UnsupportedOperationException();
 +    }
 +    public boolean isVegetarian(){
 +         throw new UnsupportedOperationException();
 +    }
 +
 +    public void print() {
 +         throw new UnsupportedOperationException();
 +    }
 +}
 +</code>
 +
 +
 +
 +
 +===== MenuItem Class =====
 +이제 **%%MenuItem%%** 클래스를 만들어 봅시다. 이 클래스는 컴포지트 패턴 다이어그램에서 앞에 해당하는
 +클래스라는 점, 그리고 복합 객체의 원소에 해당하는 행동을 구현해야 한다는 점을 잘 기억해 둡시다.
 +
 +<code java>
 +public class MenuItem extends MenuComponent {
 +    String name;
 +    String description;
 +    boolean vegetarian;
 +    double price;
 +
 +    public MenuItem(String name, String description, boolean vegetarian, double price) {
 +        this.name = name;
 +        this.description = description;
 +        this.vegetarian = vegetarian;
 +        this.price = price;
 +    } 
 +
 +    public Iterator createIterator() {
 +        return new NullIterator();
 +    }
 +
 +    public String getName(){
 +        return name;
 +    }
 +
 +    public String getDescription() {
 +        return description;
 +    }
 +
 +    public double getPrice() {
 +        return price;
 +    }
 +
 +    public boolean isVegetarian() {
 +        return vegetarian;
 +    }
 +
 +    public void print() {
 +        System.out.print(" " + getName());
 +        if(isVegetarian()) {
 +            System.out.print("(v)");
 +        }
 +        System.out.println(", " + getPrice());
 +        System.out.println("    -- " + getDescription());
 +    }
 +}
 +</code>
 +
 +복합 객체에 대한 반복자를 구현하기 위해서는 모든 구성요소에 %%createIterator()%%메소드
 +를 추가해야 합니다. 잉? 그런데 %%NullIterator%%는 도대체 뭘까요? 잠시 후에 알아보도록 하겠습니다.
 +
 +
 +===== Menu Class =====
 +**%%MenuItem%%**도 다 준비됐으니 이제 복합 객체 클래스인 **%%Menu%%**만 준비하면 됩니다. 복합 객체 클래스에는
 +%%MenuItem%%는 물론 다른 Menu도 저장할 수 있습니다. %%MenuComponent%%에 있는 메소드 가운데 getPrice()와 
 +isVegetarian()메뉴에서는 별 의미가 없기 때문에 구현하지 않습니다.
 +
 +<code java>
 +public class Menu extends MenuComponent {
 +    ArrayList menuComponents = new ArrayList();
 +    String name;
 +    String description;
 +
 +    public Menu(String name, String description) {
 +        this.name = name;
 +        this.description = description;
 +    }
 +
 +    public Iterator createIterator(){
 +        return new CompositeIterator(menuComponents.iterator());
 +    }
 +
 +    public void add(MenuComponent menuComponent) {
 +        menuComponents.add(menuComponent);
 +    }
 +
 +    public void remove(MenuComponent menuComponent) {
 +        menuComponents.remove(menuComponent);
 +    }
 +
 +    public MenuComponent getChild(int i) {
 +        return (MenuComponent)menuComponents.get(i);
 +    }
 +
 +    public String getName() {
 +        return name;
 +    }
 +
 +    public getDescription() {
 +        return description;
 +    }
 +
 +    public void print() {
 +        System.out.print("\n" + getName());
 +        System.out.println(", " + getDescription());
 +        System.out.println("---------------------");
 +
 +        Iterator iterator = menuComponents.iterator();
 +        while (iterator.hasNext()) {
 +            MenuComponent menuComponent = (MenuComponent) iterator.next();
 +            menuComponent.print();
 +        }
 +    }
 +}
 +</code>
 +
 +%%getPrice()%%와 %%isVegetarian()%%메소드는 Menu에는 어울리지 않는 메소드이므로 그냥 구현하지 않습니다.
 +(어쩌면 %%isVegetarian%%은 메뉴에도 적용할 수 있을 것 같지만, 여기에서는 그렇게 하지 않겠습니다.) Menu에
 +대해서 이 메소드를 호출하면 %%UnsupportedOperationException%%이 던져질 것입니다.
 +
 +print()메소드를 살펴보면 Iterator패턴이 사용되었음을 알 수 있습니다. 반복작업을 수행하는 중에 다른 메뉴가 
 +나타난다면 그 메뉴에서 또 다른 반복잡업을 실행하게 됩니다. 서브 메뉴가 여러 단계로 중첩되어 있으면 그런 과정이
 +여러번 반복되겠죠.
 +
 +
 +
 +
 +===== CompositeIterator Class =====
 +**%%CompositeIterator%%**는 중책을 맡고 있는 반복자입니다. 복합 객체 안에 들어있는
 +%%MenuItem%%에 대해 반복작업을 할 수 있게 해 주는 기능을 제공하죠. 모든 자식 메뉴들 및 자식의 자식
 +등도 빠짐없이 챙겨야 합니다.
 +
 +코드는 다음과 같습니다. 코드 자체는 그리 길지 않지만 조금 이해하기 힘들 수도 있습니다. 계속 머릿속으로
 +"나는 재귀호출이랑 친하다. 나는 재귀호출이랑 친하다..."라고 되뇌어 봅시다.
 +
 +<code java>
 +import java.util.*
 +
 +public class CompositeIterator implements Iterator {
 +    Stack stack = new Stack();
 +
 +    public CompositeIterator(Iterator iterator) {
 +        stack.push(iterator);
 +    }
 +
 +    public Object next() {
 +        if (hasNext()) {
 +            Iterator iterator = (Iterator) stack.peek();
 +            MenuComponent component = (MenuComponent) iterator.next();
 +            if (component istanceof Menu) {
 +                stack.push(component.createIterator());
 +            }
 +            return component;
 +        } else {
 +            return null;
 +        }
 +    }
 +
 +    public boolean hasNext() {
 +        if (stack.empty()) {
 +            return false;
 +        } else {
 +            Iterator iterator = (Iterator) stack.peek();
 +            if (!iterator.hasNext()){
 +                stack.pop();
 +                return hasNext();
 +            } else {
 +                return true;
 +            }
 +        }
 +    }
 +
 +    public void remove() {
 +        throw new UnsupportedOperationException();
 +    }
 +}
 +</code>
 +
 +
 +
 +===== NullIterator Class =====
 +이제 널 반복자(Null Iterator)가 왜 필요한지 알아봐야 할 때가 되었군요. %%MenuItem%%에 대해서 생각해 보변,
 +반복작업을 할 대상이 없다는 것을 알 수 있습니다. 상황이 그렇다 보니 %%createIterator()%%메소드를 구현하기가
 +애매해집니다. 이런 경우에 두가지 방법을 떠올릴 수 있을 것입니다.
 +
 +**첫 번째 방법:**\\
 +널을 리턴한다.
 +
 +createIterator()에서 그냥 널을 리턴할 수도 있을 것입니다. 하지만 그렇게 하면 클라이언트에서
 +리턴된 값이 널인지 아닌지를 판단하기 위한 조건문을 써야 한다는 단점이 있죠.
 +
 +**두 번째 방법: **\\
 +%%hasNext()%%가 호출되었을 때 무조건 false를 리턴하는 반복자를 리턴한다.
 +
 +이 방법이 좀 나아 보이는군요. 이렇게 하면 여전히 반복자를 리턴할 수 있기 때문에
 +클라이언트에서는 리턴된 객체가 널 객체인지에 대해 신경 쓸 필요가 없습니다. "아무 일도 하지 않는"
 +반복자를 만든다고 생각하면 됩니다.
 +
 +두 번째 방법이 확실히 좋아 보이는군요. 아무 일도 하지 않는 반복자를 %%NullIterator%%라고 부르고,
 + 다음과 같은 식으로 구현합시다.
 +
 +<code java>
 +import java.util.Iterator;
 +
 +public class NullIterator implements Iterator {
 +   
 +    public Object next() {
 +        return null;
 +    }
 +
 +    public boolean hasNext() {
 +        return false;
 +    }
 +
 +    public void remove() {
 +        throw new UnsupportedOperationException();
 +    }
 +}
 +</code>
 +
 +
 +===== Waitress Class =====
 +여기까지 오는데 여정이 길었습니다. 이제 마지막으로 Client인 **%%Waitress%%**클래스를 구현할 차례입니다.
 +앞서 메뉴에 있는 모든 항목에 대해서 반복작업을 수행할 수 있는 방법이 생겼으므로 그 방법을 활용해서 
 +**%%Waitress%%**에 어떤 항목이 채식주의자용 메뉴인지 알아내기 위한 메소드를 추가해보도록 하겠습니다.
 +
 +<code java>
 +public class Waitress {
 +    MenuComponent allMenus;
 +
 +    public Waitress(MenuComponent allMenus) {
 +        this.allMenus = allMenus;
 +    }
 +
 +    public void printMenu() {
 +        allMenus.print();
 +    }
 +
 +    public void printVegetarianMenu() {
 +        Iterator iterator = allMenus.createIterator();
 +        System.out.println("\nVEGETARIAN MENU\n----");
 +        while (iterator.hasNext()) {
 +            MenuComponent menuComponent = (MenuComponent) iterator.next();
 +            try {
 +                if (menuComponent.isVegetarian()) {
 +                    menuComponent.print();
 +                }
 +            } catch (UnsupportedOperationException e) {}
 +        }
 +    }
 +}
 +</code>
 +
 +%%Menu%%의 %%isVegetarian()%%에서는 항상 예외를 던지도록 만들어 놨습니다. 그래서 예외가 발생하면 그 예외를
 +잡긴 하지만, 아무 일 없이 반복 작업을 계속 수행하도록 했습니다.

QR Code
QR Code study:java:design_pattern:composite (generated for current page)