As i wish

[Design pattern] Iterator pattern (이터레이터 패턴) 본문

Design Pattern

[Design pattern] Iterator pattern (이터레이터 패턴)

어면태 2019. 5. 13. 23:17

오늘은 이터레이터 패턴에 대하여 포스팅해보겠습니다.

일단 정의부터 보시죠.

이터레이터 패턴

컬렉션을 표현하는 방법을 노출시키지 않으면서도 집합체 내에 있는 모든 객체들에 하나씩 접근하는 방법을 제공합니다.

 

 

반복자를 이용하여 내부 구조를 드러내지 않으면서도 클라이언트로부터 컬렉션 안에 들어 있는 모든 원소들을 접근할 수 있게 만듭니다.

예를 들어 설명해 보겠습니다.

어떤 컬렉션에서는 List로 아이템 원소들을 관리하고, 다른 컬렉션에서는 Dictionary로 아이템을 관리한다고 했을 때에 클라이언트 입장에서는 두 개의 원소들을 나열하려고 할 때 각기 다른 방법으로 나열해야 합니다. 하지만 이터레이터 패턴을 사용하면, 어떤 식으로 내부 구조가 짜여있던지 상관없이 클라이언트는 반복자를 이용하려 아이템을 나열할 수 있습니다.

# -*- coding: utf-8 -*-
# Iterator Pattern
 
import abc

class Menu:
  __metaclass__ = abc.ABCMeta

  @abc.abstractmethod
  def createIterator(self):
    pass

class Iterator:
  __metaclass__ = abc.ABCMeta

  @abc.abstractmethod
  def hasNext(self):
    pass

  @abc.abstractmethod
  def next(self):
    pass

class Waitress:
  def __init__(self, pancakeHouseMenu, dinerMenu):
    self.pancakeHouseMenu = pancakeHouseMenu
    self.dinerMenu = dinerMenu

  def printMenu(self):
    pancakeIterator = self.pancakeHouseMenu.createIterator()
    dinerIterator = self.dinerMenu.createIterator()

    print("MENU PANCAKE")
    self._printMenu(pancakeIterator)
    print("MENU DINER")
    self._printMenu(dinerIterator)

  def _printMenu(self, iterator):
    while iterator.hasNext():
      menuItem = iterator.next()
      print ("%s , " %menuItem.getName())
      print ("%s , " %menuItem.getPrice())
      print ("%s , " %menuItem.getDescription())

class MenuItem:
  def __init__(self, name, description, vegetarian, price):
    self.name = name
    self.description = description
    self.vegetarian = vegetarian
    self.price = price

  def getName(self):
    return self.name;

  def getDescription(self):
    return self.description

  def isVegetarian(self):
    return self.vegetarian

  def getPrice(self):
    return self.price

class PancakeHouseMenu(Menu):
  def __init__(self):
    self.menuItems = []
    self.addMenu('K&B PancakeSet', 'Scramble egg and toast', True, 2.99)
    self.addMenu('Regular PancakeSet', 'Egg fry, suasage', False, 2.99)
    self.addMenu('Blueberry PancakeSet', 'Add blueberry', True, 3.49)

  def addMenu(self, name, description, vegetarian, price):
    self.menuItems.append(MenuItem(name, description, vegetarian, price))

  # def getMenuItems(self):
  #   return self.menuItems
  
  def createIterator(self):
    return PancakeHouseIterator(self.menuItems)


class DinerMenu(Menu):
  def __init__(self):
    self.menuItems = {}
    self.addMenu('Vegetarian BLT', 'Bread and tomato', True, 2.99)
    self.addMenu('BLT', 'Bread and bacon', False, 2.99)
    self.addMenu('Today soup', 'Potato soup', False, 3.49)

  def addMenu(self, name, description, vegetarian, price):
    self.menuItems[name] = MenuItem(name, description, vegetarian, price)

  # def getMenuItems(self):
  #   return self.menuItems

  def createIterator(self):
    return DinerMenuIterator(self.menuItems) 

class PancakeHouseIterator(Iterator):
  def __init__(self, item):
    # List
    self.item = item
    self.position = 0

  def hasNext(self):
    return len(self.item) > 0

  def next(self):
    return self.item.pop()

class DinerMenuIterator(Iterator):
  def __init__(self, item):
    # Dictionary
    self.item = item
    self.itemValues = self.item.values()

  def hasNext(self):
    return len(self.itemValues) > 0

  def next(self):
    return self.itemValues.pop()


# Client

pancakeHouseMenu = PancakeHouseMenu()
dinerMenu = DinerMenu()

waitress = Waitress(pancakeHouseMenu, dinerMenu)
waitress.printMenu()




위처럼  PancakePancakeHouseIterator, DinerMenuIterator를 만들어서 Waitress 가 메뉴를 출력할 때 각각의 메뉴들 (PancakeHouseMenu, DinerMenu)의 메뉴 아이템들이 어떤 형식(PancakeHouseMenu: List, DinerMenu: Dictionary)으로 구성되어있는지 모르고 그냥 반복자를 이용하여 아이템들을 나열할 수 있습니다.

<결과>

파이썬에서는 내장 함수로 iterator를 제공해주고 있는데요.

한번 써보겠습니다.

# -*- coding: utf-8 -*-
# Iterator Pattern
 
import abc

class Menu:
  __metaclass__ = abc.ABCMeta

  @abc.abstractmethod
  def createIterator(self):
    pass

class Iterator:
  __metaclass__ = abc.ABCMeta

  @abc.abstractmethod
  def hasNext(self):
    pass

  @abc.abstractmethod
  def next(self):
    pass

class Waitress:
  def __init__(self, pancakeHouseMenu, dinerMenu):
    self.pancakeHouseMenu = pancakeHouseMenu
    self.dinerMenu = dinerMenu

  def printMenu(self):
    pancakeIterator = self.pancakeHouseMenu.createIterator()
    dinerIterator = self.dinerMenu.createIterator()

    print("MENU PANCAKE")
    self._printMenu(pancakeIterator)
    print("MENU DINER")
    self._printMenu(dinerIterator)

  def _printMenu(self, iterator):
    # while iterator.hasNext():
    #   menuItem = iterator.next()
    #   print ("%s , " %menuItem.getName())
    #   print ("%s , " %menuItem.getPrice())
    #   print ("%s , " %menuItem.getDescription())
    while True:
      try:
        menuItem = next(iterator)  # <--- use python inner function
        print ("%s , " %menuItem.getName())
        print ("%s , " %menuItem.getPrice())
        print ("%s , " %menuItem.getDescription())
      except StopIteration:
        break

class MenuItem:
  def __init__(self, name, description, vegetarian, price):
    self.name = name
    self.description = description
    self.vegetarian = vegetarian
    self.price = price

  def getName(self):
    return self.name;

  def getDescription(self):
    return self.description

  def isVegetarian(self):
    return self.vegetarian

  def getPrice(self):
    return self.price

class PancakeHouseMenu(Menu):
  def __init__(self):
    self.menuItems = []
    self.addMenu('K&B PancakeSet', 'Scramble egg and toast', True, 2.99)
    self.addMenu('Regular PancakeSet', 'Egg fry, suasage', False, 2.99)
    self.addMenu('Blueberry PancakeSet', 'Add blueberry', True, 3.49)

  def addMenu(self, name, description, vegetarian, price):
    self.menuItems.append(MenuItem(name, description, vegetarian, price))

  # def getMenuItems(self):
  #   return self.menuItems
  
  def createIterator(self):
    # return PancakeHouseIterator(self.menuItems)
    return iter(self.menuItems) # <--- python inner function


class DinerMenu(Menu):
  def __init__(self):
    self.menuItems = {}
    self.addMenu('Vegetarian BLT', 'Bread and tomato', True, 2.99)
    self.addMenu('BLT', 'Bread and bacon', False, 2.99)
    self.addMenu('Today soup', 'Potato soup', False, 3.49)

  def addMenu(self, name, description, vegetarian, price):
    self.menuItems[name] = MenuItem(name, description, vegetarian, price)

  # def getMenuItems(self):
  #   return self.menuItems

  def createIterator(self):
    # return DinerMenuIterator(self.menuItems)
    return iter(self.menuItems.values()) # <--- python inner function

class PancakeHouseIterator(Iterator):
  def __init__(self, item):
    # List
    self.item = item
    self.position = 0

  def hasNext(self):
    return len(self.item) > 0

  def next(self):
    return self.item.pop()

class DinerMenuIterator(Iterator):
  def __init__(self, item):
    # Dictionary
    self.item = item
    self.itemValues = self.item.values()

  def hasNext(self):
    return len(self.itemValues) > 0

  def next(self):
    return self.itemValues.pop()



# Client

pancakeHouseMenu = PancakeHouseMenu()
dinerMenu = DinerMenu()

waitress = Waitress(pancakeHouseMenu, dinerMenu)
waitress.printMenu()




해보시면 아시겠지만 당연하게도 결과는 같습니다.!!!

정말 편하네요 위에 코드에서 '<---' 된 부분은 파이썬 내장 함수를 사용하면서 변경된 부분입니다.

Comments