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

差分

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

この比較画面へのリンク

両方とも前のリビジョン前のリビジョン
次のリビジョン
前のリビジョン
study:java:design_pattern:composite [2008/09/14 06:55] bananastudy:java:design_pattern:composite [2008/09/14 09:08] (現在) banana
行 24: 行 24:
  
 이제 컴포지트 패턴의 클래스 다이어그램을 살표볼 차례입니다. 이제 컴포지트 패턴의 클래스 다이어그램을 살표볼 차례입니다.
 +
  
  
行 40: 行 41:
 메뉴를 어떤 식으로 컴포지트 패턴에 끼워 맞출 수 있을지 생각해 봅시다. 메뉴를 어떤 식으로 컴포지트 패턴에 끼워 맞출 수 있을지 생각해 봅시다.
  
 +{{:study:java:design_pattern:menucomposite.jpg|Menu Composite}}
  
  
行 82: 行 84:
 </code> </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)