【C#】Head First デザインパターン9章　Iteratorパターン


# デザインパターンIterator Pattern

今回は9章イテレータパターンをC#で勉強していきます。

https://www.oreilly.co.jp/books/4873112494/


### 二つのレストランが合併
ランチを提供するダイナーと朝ご飯を提供するパンケーキ屋の合併が決まり、朝はパンケーキを、昼はダイナーメニューを出すことに決まりました。合併と運営方針が決まったのは良いですが、なんだかメニュー表示に問題がありそうです。

ダイナーはアメリカの料理屋で、映画パルプフィクションに出てくるようなレストランです。まずはメニューを配列に登録します。
```DinerMenu.cs
public class DinerMenu{
        static int MAX_ITEMS = 6;
        int numberOfItems = 0;
        MenuItem[] menuItems;

        public DinerMenu()
        {
            menuItems = new MenuItem[MAX_ITEMS];
            addItem("Vegetarian BLT",
                "(Fakin’) Bacon with lettuce & tomato on whole wheat", true, 2.99);
            addItem("BLT",
                "Bacon with lettuce & tomato on whole wheat", false, 2.99);
            addItem("Soup of the day",
                "Soup of the day, with a side of potato salad", false, 3.29);
            addItem("Hotdog",
                "A hot dog, with saurkraut, relish, onions, topped with cheese",
                false, 3.05);


        public void addItem(String name, String description,
            bool vegetarian, double price)
        {
            MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
            if (numberOfItems >= MAX_ITEMS)
            {
                Console.WriteLine("Sorry, menu is full!Can’t add item to menu");
            }
            else
            {
                menuItems[numberOfItems] = menuItem;
                numberOfItems = numberOfItems + 1;
            }
        }

        public MenuItem[] getMenuItems()
        {
            return menuItems;
        }
    }
```

同様にパンケーキ屋さんのメニューも登録しますが、今回はリストに登録します。
```PancakeHouseMenu.cs
 public class PancakeHouseMenu
    {
        private ArrayList menuItems;

        public PancakeHouseMenu()
        {
            menuItems = new ArrayList();

            addItem("K & B’s Pancake Breakfast",
                "Pancakes with scrambled eggs, and toast",
                true,
                2.99);
            addItem("Regular Pancake Breakfast",
                "Pancakes with fried eggs, sausage",
                false,
                2.99);
            addItem("Blueberry Pancakes",
                "Pancakes made with fresh blueberries",
                true,
                3.49);
            addItem("Waffles",
                "Waffles, with your choice of blueberries or strawberries",
                true,
                3.59);
        }

        public void addItem(String name, String description,
            bool vegetarian, double price)
        {
            MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
            menuItems.Add(menuItem);
        }

        public ArrayList getMenuItems()
        {
            return menuItems;
        }

    }
```

メニューアイテムクラスも作ります。
```MenuItem.cs
public class MenuItem
    {
        private string name;
        private string description;
        private bool vegetarian;
        private double price;

        public MenuItem(string name,
            string description,
            bool vegetarian,
            double price)
        {
            this.name = name;
            this.description = description;
            this.vegetarian = vegetarian;
            this.price = price;
        }

        public string getName()
        {
            return name;
        }

        public string getDescription()
        {
            return description;
        }

        public double getPrice()
        {
            return price;
        }

        public bool isVegetarian()
        {
            return vegetarian;
        }
    }
```

各メニューの内容をひとつずつ確認しようと思った場合、配列とArrayListに分かれているため、リストを網羅する際に少し違う書き方をしなければなりません。
この場合breakfastItemsはcountを、lunchItemsはLengthを使ってリストの長さを取得しています。

```Program.cs
   class Program
    {
        static void Main(string[] args)
        {
            PancakeHouseMenu pancake = new PancakeHouseMenu();
            ArrayList breakfastItems = pancake.getMenuItems();

            for (int i = 0; i < breakfastItems.Count; i++)
            {
                MenuItem menuItem = (MenuItem)breakfastItems[i];
                Console.WriteLine(menuItem.getName());
            }

            DinerMenu diner = new DinerMenu();
            MenuItem[] lunchItems = diner.getMenuItems();

            for (int i = 0; i < lunchItems.Length; i++)
            {
                MenuItem menuItem = lunchItems[i];
                Console.WriteLine(menuItem.getName());
            }

        }
    }
```

リストがどのようなものでも同じ方法でリストの中身を取得できるようにするデザインパターンがiteratorパターンです。

### Iteratorパターンを使う

```Itarator.cs
    public interface Iterator
    {
        bool hasNext();
        object next();
    }
```

今回ダイナーのメニューのイテレータと、一部を変更したダイナーメニュークラスを準備します。
```DinerMenuIterator.cs
public class DinerMenuIterator : Iterator
    {
        private MenuItem[] items;
        private int position = 0;

        public DinerMenuIterator(MenuItem[] items)
        {
            this.items = items;
        }

        public object next()
        {
            MenuItem menuItem = items[position];
            position = position + 1;
            return menuItem;
        }

        public bool hasNext()
        {
            if (position >= items.Length || items[position] == null)
            {
                return false;
            }
            else
            {
                return true;
            }
        }
    }
```

```DinerMenu.cs
 public class DinerMenu{
        static int MAX_ITEMS = 6;
        int numberOfItems = 0;
        MenuItem[] menuItems;

        public DinerMenu()
        {
            menuItems = new MenuItem[MAX_ITEMS];
            addItem("Vegetarian BLT",
                "(Fakin’) Bacon with lettuce & tomato on whole wheat", true, 2.99);
            addItem("BLT",
                "Bacon with lettuce & tomato on whole wheat", false, 2.99);
            addItem("Soup of the day",
                "Soup of the day, with a side of potato salad", false, 3.29);
            addItem("Hotdog",
                "A hot dog, with saurkraut, relish, onions, topped with cheese",
                false, 3.05);
            addItem("Hotdog",
                "A hot dog, with saurkraut, relish, onions, topped with cheese",
                false, 3.05);
            addItem("Hotdog",
                "A hot dog, with saurkraut, relish, onions, topped with cheese",
                false, 3.05);
        }

        public void addItem(String name, String description,
            bool vegetarian, double price)
        {
            MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
            if (numberOfItems >= MAX_ITEMS)
            {
                Console.WriteLine("Sorry, menu is full!Can’t add item to menu");
            }
            else
            {
                menuItems[numberOfItems] = menuItem;
                numberOfItems = numberOfItems + 1;
            }
        }

        public MenuItem[] getMenuItems()
        {
            return menuItems;
        }

        public Iterator createIterator()
        {
            return new DinerMenuIterator(menuItems);
        }

    }
```

同様にパンケーキイテレータとパンケーキハウスクラスを用意します。
```PancakeHouseMenuIterator.cs
    public class PancakeHouseMenuIterator : Iterator
    {
        private ArrayList items;
        private int position = 0;

        public PancakeHouseMenuIterator(ArrayList items)
        {
            this.items = items;
        }

        public object next()
        {
            MenuItem menuItem = (MenuItem)items[position];
            position = position + 1;
            return menuItem;
        }

        public bool hasNext()
        {
            if (position >= items.Count || items[position] == null)
            {
                return false;
            }
            else
            {
                return true;
            }
        }
    }
```

```PancakeHouseMenu.cs
public class PancakeHouseMenu
    {
        private ArrayList menuItems;

        public PancakeHouseMenu()
        {
            menuItems = new ArrayList();

            addItem("K & B’s Pancake Breakfast",
                "Pancakes with scrambled eggs, and toast",
                true,
                2.99);
            addItem("Regular Pancake Breakfast",
                "Pancakes with fried eggs, sausage",
                false,
                2.99);
            addItem("Blueberry Pancakes",
                "Pancakes made with fresh blueberries",
                true,
                3.49);
            addItem("Waffles",
                "Waffles, with your choice of blueberries or strawberries",
                true,
                3.59);

        }

        public void addItem(String name, String description,
            bool vegetarian, double price)
        {
            MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
            menuItems.Add(menuItem);
        }

        public Iterator createIterator()
        {
            return new PancakeHouseMenuIterator(menuItems);
        }
    }
```

ウェイトレスクラスを作り、そのインスタンスにパンケーキハウスメニューとダイナーメニューを渡すことで、どのようなリストであってもウェイトレスが処理できるようになりました。
リストの数が増え、リストの形が変更になったとしても、イテレータインターフェイスが実装されればウェイトレスはその中身を知らずとも使うことができます。
```Waitress.cs
public class Waitress
    {
        private PancakeHouseMenu pancakeHouseMenu;
        private DinerMenu dinerMenu;

        public Waitress(PancakeHouseMenu pancakeHouseMenu, DinerMenu dinerMenu)
        {
            this.pancakeHouseMenu = pancakeHouseMenu;
            this.dinerMenu = dinerMenu;
        }

        public void printMenu()
        {
            Iterator pancakeIterator = pancakeHouseMenu.createIterator();
            Iterator dinerIterator = dinerMenu.createIterator();
            Console.WriteLine("MENU\n----\nBREAKFAST");
            printMenu(pancakeIterator);
            Console.WriteLine("\nLUNCH");
            printMenu(dinerIterator);

        }

        private void printMenu(Iterator iterator)
        {
            while (iterator.hasNext())
            {
                MenuItem menuItem = (MenuItem)iterator.next();
                Console.WriteLine(menuItem.getName() + ", ");
                Console.WriteLine(menuItem.getPrice() + " -- ");
                Console.WriteLine(menuItem.getDescription());
            }
        }
    }
```

```Program.cs
 class Program
    {
        static void Main(string[] args)
        {

            PancakeHouseMenu pancake = new PancakeHouseMenu();
            DinerMenu diner = new DinerMenu();

            Waitress waitress = new Waitress(pancake, diner);
            waitress.printMenu();
        }
    }
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64828/fa7e8ecf-f5ff-7de9-6d1a-ba0259c0e596.png)

次回は10章です。