ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 행위패턴 > 이터레이터 *
    기타/디자인패턴 2021. 2. 11. 15:19

    객체를 컬렉션에 저장할 때는 배열, 스택, 해시테이블 등 장단점을 따져 적합한 컬렉션을 선택해 이용한다.

    클라이언트에서 모든 객체들에 일일이 접근하는 작업을 하고자할 때, 어떤 식으로 저장했는지 노출하지 않으면서 접근할 수 있도록 하는 방법과 객체들로 구성된 슈퍼 컬렉션에 대해 알아본다.

     

    A 식당과 B 식당이 합병하기로 했는데 A 식당은 메뉴를 ArrayList 에 B식당은 배열에 저장한다.

    Waitress 는 메뉴를 출력하려면 배열과 리스트 각각의 순환문을 작성해야 한다.

    Waitress 가 구상클래스 MenuItem[] 과 ArrayList 에 직접 연결되어있다.

     

    public class Waitress {
        PancakeHouseMenu pancake;
        DinerMenu diner;
        
        public Waitress(PancakeHouseMenu pancake,DinerMenu diner){
        	this.pancake=pancake;
        	this.diner=diner;
        }
        
        public void printMenu() {
        
           List<MenuItem> list = pancake.getMenuItems();
           for(MenuItem m : list){
                System.out.println(m.name);
                System.out.println(m.desc);
                System.out.println(m.price);
            }
    
            MenuItem[] menu = diner.getMenuItems();
            for(int i=0;i<menu.length;i++){
                System.out.println(menu[i].name);
                System.out.println(menu[i].desc);
                System.out.println(menu[i].price);
            }
    
        }   
    }
    public class DinerMenu {
        static final int MAX_ITEMS = 6;
        int numOfItems = 0;
        MenuItem [] menu;
    
        public DinerMenu(){
            menu = new MenuItem[MAX_ITEMS];
    
            addItem("채식 BLT", "콩고기", 2000, true);
            addItem("BLT", "돼지고기", 2000, false);
            addItem("햄버거", "새우", 5000, false);
            addItem("피자", "페퍼로니", 3000, false);
            addItem("파스타", "알리오올리오", 5000, true);
        }
    
        public void addItem(String name,String desc,double price,boolean vegeterian){
            if(numOfItems>=MAX_ITEMS){
              System.out.println("no more");  
            } else {
                MenuItem m = new MenuItem(name, desc, price, vegeterian);
                menu[numOfItems++] = m;
            }
        }
    //배열로 된 메뉴 반환
        public MenuItem [] getMenuItems(){
            return menu;
        }
    }

     

    다른 컬렉션을 하나의 반복문으로 처리하기 위해 Iterator 인터페이스를 만든다.

    // Iterator 직접 만들지 않고 java.util 에 있는 iterator 를 이용해도 무관하다
    package iterator;
    
    public interface Iterator {
        public boolean hasNext();
        public Object next();
    }
    // Concrete Iterator
    public class DinerMenuIterator implements Iterator{
    
        MenuItem[] items;
        int position =0;
    
        public DinerMenuIterator(MenuItem[] items){
            this.items = items;
        }
    
        @Override
        public boolean hasNext() {
            if(position>=items.length || items[position]==null){
                return false;
            }
            return true;
        }
    
        @Override
        public Object next() {
            return (MenuItem)items[position++];
        }    
    }
    public class DinerMenu {
        static final int MAX_ITEMS = 6;
        int numOfItems = 0;
        MenuItem [] menu;
    
        public DinerMenu(){   ..    }
    
        public void addItem(String name,String desc,double price,boolean vegeterian){   ..     }
    
    // 이터레이터 반환으로 수정 
        public Iterator getMenuItems(){
            return new DinerMenuIterator(menu);
        }
    }

    * Watiress 는 더이상 Diner 와 Pancake 이 어떤 컬렉션으로 메뉴를 저장하고 있는지 알 필요가 없어졌다.

    * Watiress 는 오직 Iterator 만 알고 있으며, 컬렉션 내부에 접근하기 위한 반복 작업의 캡슐화 되어있다.

    public class Waitress {
        
        PancakeHouseMenu pancakeHouseMenu;
        DinerMenu dinerMenu;
    
        public Waitress(PancakeHouseMenu pancakeHouseMenu,DinerMenu dinerMenu){
            this.pancakeHouseMenu = pancakeHouseMenu;
            this.dinerMenu = dinerMenu;
        }
    
        public void printMenu(){
            Iterator pancakeIT = pancakeHouseMenu.getMenuItems();
            Iterator dinerMenuIT = dinerMenu.getMenuItems();
            printMenu(pancakeIT);
            printMenu(dinerMenuIT);
        }
    
    //리스트, 배열 다른 컬렉션 형태를 반복문을 하나로 통합! 
        public void printMenu(Iterator it){
            while(it.hasNext()){
                MenuItem m = (MenuItem) it.next();
                System.out.println(m.name);
                System.out.println(m.desc);
                System.out.println(m.price);
            }
        }
    }

     

    하지만 아직 두 개의 구상 메뉴 클래스에 의존하고 있다는 문제는 갖고 있다.

    두 메뉴 클래스에 getMenuItems 는 각각 Panacke 와 Diner 의 Iterator를 반환하는 공통 메서드이다.

    공통 메서드를 가진 Menu 인터페이스를 만들고 Panacke 와 Diner 에서 이를 상속한다.

    // Aggregate
    public interface Menu {
        public Iterator getMenuItems();
    }
    // Concrete Aggregate
    public class DinerMenu implements Menu {
        static final int MAX_ITEMS = 6;
        int numOfItems = 0;
        MenuItem [] menu;
    
        public DinerMenu(){ .. }
    
        public void addItem(String name,String desc,double price,boolean vegeterian){  ..   }
    
        public Iterator getMenuItems(){
            return new DinerMenuIterator(menu);
        }
    }

    * 그럼 Waitress 에서 두 개의 구상클래스 대신 한 개의 인터페이스에만 의존할 수 있게 된다.

    특정 구현이 아닌 인터페이스에 맞춰 프로그래밍하는 원칙을 지키게 되고 의존성이 줄일 수 있다.

    // Client
    public class Waitress {
        
        Menu pancakeHouseMenu;
        Menu dinerMenu;
    
        public Waitress(Menu pancakeHouseMenu,Menu dinerMenu){
            this.pancakeHouseMenu = pancakeHouseMenu;
            this.dinerMenu = dinerMenu;
        }
    
        public void printMenu(){   ..  }
    
        public void printMenu(Iterator it){  ..  }
    
     
    }

     

     

    * 반복자를 이용하면 다형적인 코드를 만들 수 있다는 것의 의미

    Iterator 를 매개변수로 받는 메서드를 만들면 다형적인 반복작업을 사용한다고 할 수 있다.

    Iterator 를 지원하기만 하면 어떤 컬렉션에도 사용 가능한 코드가 만들어지기 때문이다. 컬렉션의 구체적인 구현 방식에는 신경쓰지 않아도 된다.

     

    * 집합체 - Diner,Pancake 내에서 내부 컬렉션과 관련된 반복자 메서드를 구현하는 것 

    클래스의 역할은 집합체를 관리하는 데 있다.

    집합체 관리 와 반복자 메서드는 두 개의 다른 역할이다.

    클래스가 수정되는 것은 다른 영향도를 만들 수 있기 때문에 수정을 가능한 줄이는 것이 좋다.

    클래스가 컬렉션 뿐만 아니라 반복자 관련 기능도 갖고 있을 때, 클래스는 두 가지 이유로 수정될 수 있다.

    만약 새로운 요구사항에 의해 집합체의 컬렉션의 종류가 달라지게 되는 경우, 반복자 관련 기능이 변경되었을 경우.

    이는 클래스를 바꾸는 이유는 한가지 뿐이여야 한다는 디자인 원칙에 위반된다.

     

     

    출처:

    Head First Design Patterns

    반응형

    '기타 > 디자인패턴' 카테고리의 다른 글

    행위패턴 > 상태(State) *  (0) 2021.02.12
    행위패턴 > 옵저버 *  (0) 2021.02.12
    행위패턴 > 커맨드 *  (0) 2021.02.11
    구조패턴 > 퍼싸드 *  (0) 2021.02.11
    구조패턴 > 데코레이터 *  (0) 2021.02.11
Designed by Tistory.