「ヘッドファーストデザインパターン」の研究ノート - イテレータパターン

、あなたは私たちがオブジェクトを格納する方法を知ることができない、私たちは、今日にイテレータパターンを依存している私たちは、お客さまの配列、スタック、リスト、またはハッシュテーブルをトラバースしてみましょう。

ケース

私たちは、次のように我々は両方の店のクラスは、我々は別のクラスのメニューを作成し、共通メニューのプロパティを必要とし、彼らの料理に共通している、操作をマージする2軒のレストランがあります。

public class MenuItem {
    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 String getName() {
        return name;
    }

    public String getDescription() {
        return description;
    }

    public boolean isVegetarian() {
        return vegetarian;
    }

    public double getPrice() {
        return price;
    }
}

今、2つの店舗のカテゴリを見てください。

public class DinerMenu {
    static final 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, toped with cheese",
                false,
                3.05);
    }

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

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

public class PancakeHouseMenu {
    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, boolean vegetarian, double price) {
        MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
        menuItems.add(menuItem);
    }

    public ArrayList getMenuItems() {
        return menuItems;
    }
}

いくつかの料理だけでなく、初期化中にゲッターのメニュー()メソッドの戻り値を追加し、構造は、メニューのプロパティがあり、似ているが、1列のコレクションです。

次に我々は、ウェイトレスのクラスを操作する必要がメインイベントで、メニューを印刷する方法があります。

public class Waitress {

    public void printMenu() {
        PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();
        DinerMenu dinerMenu = new DinerMenu();

        ArrayList houseMenuMenuItems = pancakeHouseMenu.getMenuItems();
        MenuItem[] dinerMenuMenuItems = dinerMenu.getMenuItems();

        // 循环遍历
        for (int i = 0; i < houseMenuMenuItems.size(); i++) {
            MenuItem menuItem = (MenuItem) houseMenuMenuItems.get(i);
            System.out.println(menuItem.getName() + " ");
            System.out.println(menuItem.getPrice() + " ");
            System.out.println(menuItem.getDescription());
        }

        for (int i = 0; i < dinerMenuMenuItems.length; i++) {
            MenuItem dinerMenuMenuItem = dinerMenuMenuItems[i];
            System.out.println(dinerMenuMenuItem.getName() + " ");
            System.out.println(dinerMenuMenuItem.getPrice() + " ");
            System.out.println(dinerMenuMenuItem.getDescription());
        }
    }
}

これは、ループを反復処理するために使用するのが最も簡単である私たちは店組合に新店舗を持っている場合、我々はこの方法では、ループを反復処理のために増加し続ける必要があり、それは非常に冗長な繰り返しである、我々はパッケージの一部を変更する必要があり、我々が使用します改善するためのイテレータパターン。まず、Iteratorインターフェイスを作成します。

public interface Iterator {
    boolean hasNext();
    Object next();
}

その後、一方は素子のhasNext()メソッド戻り、次のいずれかである次の要素()メソッドがまだあるか否かを判定し、それらは上記の方法を達成するため、別々のイテレータクラスコレクションと配列を作成。

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;
        } else {
            return true;
        }
    }

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

public class PancakeHouseMenuIterator implements Iterator {
    ArrayList items;
    int position = 0;

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

    @Override
    public boolean hasNext() {
        if (position >= items.size() || items.get(position) == null) {
            return false;
        } else {
            return true;
        }
    }

    @Override
    public Object next() {
        MenuItem menuItem = (MenuItem)items.get(position);
        position += 1;
        return menuItem;
    }
}

それが私たちの内部コードを達成公開し、その後、メソッドを追加しますので、その後イテレータを返し、私たちはこのイテレータを介してデータを横断する2つのメニューのカテゴリ、削除getMenuItems()メソッドを、変更します。

public class DinerMenu {
    static final 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, toped with cheese",
                false,
                3.05);
    }

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

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

public class PancakeHouseMenu {
    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, boolean vegetarian, double price) {
        MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
        menuItems.add(menuItem);
    }

    public Iterator creatorIterator() {
        return new PancakeHouseMenuIterator(menuItems);
    }
}

最後は、私たちのウェイトレスクラスであり、データはイテレータの使用を横断して、印刷メニューを変更するためのプライベートメソッドを作成し、creatorIteratorを利用だけで()メソッドは、プライベートメソッドは、データを横断して、イテレータを返します。

public class Waitress {

    public void printMenu() {
        DinerMenu dinerMenu = new DinerMenu();
        PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();
        Iterator dinerIterator = dinerMenu.createIterator();
        Iterator pancakeIterator = pancakeHouseMenu.creatorIterator();
        System.out.println("MENU\n----\nBREAKFAST");
        printMenu(dinerIterator);
        System.out.println("\nLUNCH");
        printMenu(pancakeIterator);
    }

    private void printMenu(Iterator iterator) {
        while (iterator.hasNext()) {
            MenuItem menuItem = (MenuItem)iterator.next();
            System.out.println(menuItem.getName() + " ");
            System.out.println(menuItem.getPrice() + " -- ");
            System.out.println(menuItem.getDescription());
        }
    }
}

私たちは、もはや必要getMenuItem()メソッドは、イテレータオブジェクトを取得し、次のprintMenu(イテレータイテレータ)メソッドを使用することで置き換え、削除しました。

私達はちょうど、各店舗がそれにイテレータオブジェクトを返すメソッドを増大させ、それらを2回の反復子を与える必要があります。私たちのウェイトレスはそう簡単に拡張やメンテナンス、新店舗だった、店はそれがイテレータ、コールprintMenu(イテレータイテレータ)メソッドを返し、オブジェクトを作成する必要があります。

このイテレータは、彼女がいる限り、彼女はイテレータのことを知ってもらうことができますよう、データはメニュー構造に使用されているかを知る必要はありません、特定のクラスを達成するため、私たちはウエイトレスから分離することができます。イテレーションは、私たちは、むしろメソッド内トラバーサルを実装するより、重合の各要素を横断することができ、ちょうど私たちはトラバーサルをカプセル化し、私たちはデータ集約のすべての要素にアクセスすることができます。

我々はcreateIteratorのPancakeHouseMenu()メソッド、最初にインポートするjava.util.Iteratorを変更するので、私たちは私たちのデザインを改善していき、実際には、ArrayListのは、私たちのためにIteratorを取得するための方法を提供している、我々は、Javaが提供するイテレータインターフェースを使用しています。

public Iterator creatorIterator() {
        return menuItems.iterator();
    }

ここでは、最初のDinerMenuIteratorからの変更を必要とするjava.util.Iteratorインタフェースのニーズを満たすために、DinerMenuを変更します。java.util.Iteratorのご紹介、remove()メソッドを達成。

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;
        } else {
            return true;
        }
    }

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

    @Override
    public void remove() {
        if (position <= 0) {
            throw new IllegalStateException("You can't remove an item until you've done at least one next()");
        }
        if (items[position - 1] != null) {
            for (int i = position - 1; i < items.length - 1; i++) {
                items[i] = items[i+1];
            }
            items[items.length-1] = null;
        }
    }
}

DinerMenuは、導入するjava.util.Iteratorに追加しました。我々はカップリングとメイドクラスの具体的な実現削減、プログラミング・インタフェースを向いているので、我々はまた、このインターフェースは、便利な方法は、我々はメイドクラスを呼び出す実装することをメニューインタフェースメイク2レストランのメニュークラスを追加することができます。

public interface Menu {
    public Iterator createIterator();
}

public class DinerMenu implements Menu {...}

public class PancakeHouseMenu implements Menu {...}

このインタフェースを実装した後、我々はメイドクラスを見てください。

public class Waitress {

    public void printMenu() {
        Menu dinerMenu = new DinerMenu();
        Menu pancakeHouseMenu = new PancakeHouseMenu();
        Iterator dinerIterator = dinerMenu.createIterator();
        Iterator pancakeIterator = pancakeHouseMenu.createIterator();
        System.out.println("MENU\n----\nBREAKFAST");
        printMenu(dinerIterator);
        System.out.println("\nLUNCH");
        printMenu(pancakeIterator);
    }

    private void printMenu(Iterator iterator) {
        while (iterator.hasNext()) {
            MenuItem menuItem = (MenuItem)iterator.next();
            System.out.println(menuItem.getName() + " ");
            System.out.println(menuItem.getPrice() + " -- ");
            System.out.println(menuItem.getDescription());
        }
    }
}

インポートするjava.util.Iterator、メニューを使用するには、「インターフェイスのプログラミングではなく、プログラミングのために。」、2つのメニューのカテゴリではなく、具体的なクラスを宣言しました 我々はウェイトレスと具象クラス間の依存を減らすことができます。

イテレータパターン定義

反復モードが露出している、その内部を示すことなく、ポリマー物体の各要素に対してシーケンシャル方法を提供します。

イテレータパターンは、私たちは、データセットの各要素を横断することができ、そのデータ構造を公開していない、むしろ凝集オブジェクトよりも、要素イテレータを横断する責任、インタフェースおよび実装の凝集がより簡潔になることができるいますが、また、それはより多くの集約作業に焦点を当てたこと、およびトラバースデータというものを無視することができます。

:「シングル責任」のデザイン原則クラスの理由は、唯一の原因を変更する必要があります。

コードが多くの潜在的なエラーを引き起こし変更することは簡単ですので、あなたは、クラスを変更することは避けるべきです。クラスが変更に二つの理由がある場合は、その変更のこの種の確率が上昇すると、それは本当に私たちはこの原則に従うので、のみに責任を割り当てるには、影響を受けることになりますどちらも、変更されたときクラス。

私たちは、区別するために学ばなければならない、彼らが二つ以上の異なる責任に属していても、それらを一緒に焦点を当て、その後、大規模なグループの行動を見てすることは非常に困難であり、設計の責任であるが、我々は疲れを知らずに検査を働いていますシステムの成長に複数ある場合は独自のデザイン、理由私たちは常にクラスの変化を観察します。

新しいカフェ

今、私たちはそれをどのように設計するか、その後、私たちのシステムにカフェを追加したいですか?

public class CafeMenu implements Menu {
    Hashtable menuItems = new Hashtable();

    public CafeMenu() {
        addItem("Veggie Burger and Air Fries",
                "Veggie burger on a whole wheat bun, lettuce, tomato, and fries",
                true,
                3.99);
        addItem("Soup of the day",
                "A cup of the soup of the day, with a side salad",
                false,
                3.69);
        addItem("Burrito",
                "A large burrito, with whole pinto beans, salsa, guacamole",
                true,
                4.29);
    }

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

    @Override
    public Iterator createIterator() {
        return menuItems.values().iterator();
    }
}

これは、コーヒーメニューのクラスであり、最初の2つのクラスが同様のメニューは、メニューインターフェースが実現され、イテレータを返します。その後、ウェイトレスprintMenuクラス()メソッドは、コーヒーのようなメニューを作成するには、いくつかの行を追加するイテレータを取得するために、データを通してprintMenu(イテレータイテレータ)メソッドを呼び出します。

public void printMenu() {
        Menu dinerMenu = new DinerMenu();
        Menu pancakeHouseMenu = new PancakeHouseMenu();
        CafeMenu cafeMenu = new CafeMenu();
        Iterator dinerIterator = dinerMenu.createIterator();
        Iterator pancakeIterator = pancakeHouseMenu.createIterator();
        Iterator cafeMenuIterator = cafeMenu.createIterator();
        System.out.println("MENU\n----\nBREAKFAST");
        printMenu(dinerIterator);
        System.out.println("\nLUNCH");
        printMenu(pancakeIterator);
        System.out.println("\nDINNER");
        printMenu(cafeMenuIterator);
    }

これは、新しいメニューを追加する目的を完了しますが、問題があり、プログラムは(printMenu回を呼び出します)、それはかなりではない、とそれぞれ新たに追加されたメニューだけでなく、よりクラスコードを追加するためにウェイトレスを開きますどのように我々は、設計を変更することができますか?コードを見てください:

public class Waitress {

    ArrayList menus;

    public Waitress(ArrayList menus) {
        this.menus = menus;
    }

    public void printMenu() {
        Iterator iterator = menus.iterator();
        while (iterator.hasNext()) {
            Menu next = (Menu) iterator.next();
            printMenu(next.createIterator());
        }
    }

    private void printMenu(Iterator iterator) {
        while (iterator.hasNext()) {
            MenuItem menuItem = (MenuItem)iterator.next();
            System.out.println(menuItem.getName() + " ");
            System.out.println(menuItem.getPrice() + " -- ");
            System.out.println(menuItem.getDescription());
        }
    }
}

()メソッドは、より単純なコードに私たちを可能にする、我々はウェイトレスのオブジェクトメニューを提供するだけで、デバッグのセットを通過したとき、メニューのクラスのコレクションプロパティを作成し、その後、printMenuでコレクションをトラバース。

今、彼らはのデザートに続いて欲しい「サブメニュー。」ツリー構造と少し似て、私たちは、メニュー項目の配列にデザートメニューを割り当てるので、変更することはできません!変更は、次のブログを参照してください。

公開された26元の記事 ウォンの賞賛2 ビュー2319

おすすめ

転載: blog.csdn.net/qq_42909545/article/details/105024998