Design Pattern - Iterator

(史帝芬, 2007/05/26, hi.steven@gmail.com)
    Iterator在C#、Java等語言中使用的相當普遍,使用Iterator pattern的目的 在於穩藏聚合物件的實作細節,僅提供外界一個可存取聚合物件內部元素的介面,程式只要透過Iterator 介面,即可走訪聚合物件的各個元素。
    現在假設有家銀行,代理了許多債券基金,並寫了如程式1所示的程式用ArrayList 來儲存其代理的基金,測試程式顯示,當要存取這些基金時,我們需要了解ArrayList的用法,才能巡訪並存取 每支基金。

  • 程式1 (C#)

  • using System;
    using System.Collections;
    
    namespace Iterator
    {
        public class Debenture
        {
            private string id; //基金編號
            private string name; //基金名稱
            private double currentValue; //現值
            private double rate; //配息率
    
            public Debenture(string id, string name, double initialValue, double rate)
            {
                this.id = id;
                this.name = name;
                this.currentValue = initialValue;
                this.rate = rate;
            }
    
            public string Id
            {
                get { return id; }
            }
    
            public string Name
            {
                get { return name; }
            }
    
            public double CurrentValue
            {
                get { return currentValue; }
                set { currentValue = value; }
            }
    
            public double Rate
            {
                get { return rate; }
                set { rate = value; }
            }
        }
    
        public class DebentureList
        {
            private ArrayList debenture;
    
            public DebentureList()
            {
                debenture = new ArrayList();
                debenture.Add(new Debenture("1616", "聯博全球高收益債券基金AT", 12.3, 0.003));
                debenture.Add(new Debenture("1313", "富蘭克林坦伯頓全球債券基金", 11.5, 0.0012));
                debenture.Add(new Debenture("1258", "瑞銀新興市場債券基金", 15.9, 0));
            }
    
            public ArrayList getAllItem()
            {
                return debenture;
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                DebentureList debentureList = new DebentureList();
                ArrayList funds = debentureList.getAllItem();
                for (int i = 0; i < funds.Count; i++)
                {
                    Debenture item = (Debenture)funds[i];
                    Console.WriteLine("債券基金 -- {0} ({1}): 現值 {2} 配息率 {3}", item.Id, item.Name, item.CurrentValue, item.Rate);
                }
    
                Console.ReadLine();
            }
        }
    }
    

        程式2,我們使用Iterator pattern改寫了上面的程式,穩藏了ArrayList, 測試程式只要知道IList及IIterator兩個介面,即可走訪所有債券基金。以後如果程式改寫了,改 用Hashtable來儲存所有基金,我們並不需要改寫程式,仍是透過Iterator走訪所有元素。 要特別注意的是,使用Iterator Pattern存取元素時,不可預設取得之元素的順序, 因為隨著使用的資料結構的不同,內含的元素即有可能會有不同的排列順序。


  • 程式2 (C#)

  • using System;
    using System.Collections;
    
    namespace Iterator
    {
        public interface IList
        {
            IIterator getIterator();
        }
    
        public interface IIterator
        {
            bool hasNext();
            Object next();
        }
    
        #region 債券基金
        public class Debenture
        {
            private string id; //基金編號
            private string name; //基金名稱
            private double currentValue; //現值
            private double rate; //配息率
    
            public Debenture(string id, string name, double initialValue, double rate)
            {
                this.id = id;
                this.name = name;
                this.currentValue = initialValue;
                this.rate = rate;
            }
    
            public string Id
            {
                get { return id; }
            }
    
            public string Name
            {
                get { return name; }
            }
    
            public double CurrentValue
            {
                get { return currentValue; }
                set { currentValue = value; }
            }
    
            public double Rate
            {
                get { return rate; }
                set { rate = value; }
            }
        }
    
        public class DebentureList : IList
        {
            private ArrayList debenture;
    
            public DebentureList()
            {
                debenture = new ArrayList();
                debenture.Add(new Debenture("1616", "聯博全球高收益債券基金AT", 12.3, 0.003));
                debenture.Add(new Debenture("1313", "富蘭克林坦伯頓全球債券基金", 11.5, 0.0012));
                debenture.Add(new Debenture("1258", "瑞銀新興市場債券基金", 15.9, 0));
            }
    
            public ArrayList getAllItem()
            {
                return debenture;
            }
    
            public IIterator getIterator()
            {
                return new DebentureListIterator(this);
            }
        }
    
        public class DebentureListIterator : IIterator
        {
            private DebentureList list;
            private int index;
    
            public DebentureListIterator(DebentureList list)
            {
                this.list = list;
                index = 0;
            }
    
            public bool hasNext()
            {
                return index < list.getAllItem().Count;
            }
    
            public object next()
            {
                return list.getAllItem()[index++];
            }
        }
        #endregion
    
    
        class Program
        {
            static void Main(string[] args)
            {
                IList debentureList = new DebentureList();
                IIterator iterator = debentureList.getIterator();
                while (iterator.hasNext())
                {
                    Debenture item = (Debenture)iterator.next();
                    Console.WriteLine("債券基金 -- {0} ({1}): 現值 {2} 配息率 {3}", item.Id, item.Name, item.CurrentValue, item.Rate);
                }
                Console.ReadLine();
            }
        }
    }
    

        C# 2.0 微軟加入了對Iterator的支援,我們只要繼承IEnumerable介面,並 實作其中的GetEnumerator method,再配合yield return語法即可輕鬆實作出Iterator pattern, 而且繼承IEnumerable的類別,還可以用foreach巡訪所有元素,以下將使用C# 2.0的特性改寫程式1。

  • 程式3 (C#)

  • using System;
    using System.Collections;
    
    namespace Iterator
    {
        public class Debenture
        {
            private string id; //基金編號
            private string name; //基金名稱
            private double currentValue; //現值
            private double rate; //配息率
    
            public Debenture(string id, string name, double initialValue, double rate)
            {
                this.id = id;
                this.name = name;
                this.currentValue = initialValue;
                this.rate = rate;
            }
    
            public string Id
            {
                get { return id; }
            }
    
            public string Name
            {
                get { return name; }
            }
    
            public double CurrentValue
            {
                get { return currentValue; }
                set { currentValue = value; }
            }
    
            public double Rate
            {
                get { return rate; }
                set { rate = value; }
            }
        }
    
        public class DebentureList : IEnumerable
        {
            private ArrayList debenture;
    
            public DebentureList()
            {
                debenture = new ArrayList();
                debenture.Add(new Debenture("1616", "聯博全球高收益債券基金AT", 12.3, 0.003));
                debenture.Add(new Debenture("1313", "富蘭克林坦伯頓全球債券基金", 11.5, 0.0012));
                debenture.Add(new Debenture("1258", "瑞銀新興市場債券基金", 15.9, 0));
            }
    
            #region IEnumerable 成員
    
            public IEnumerator GetEnumerator()
            {
                for (int i = 0; i < debenture.Count; i++)
                {
                    yield return debenture[i];
                }
            }
    
            #endregion
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                IEnumerable debentureList = new DebentureList();
                foreach (Debenture item in debentureList)
                {
                    Console.WriteLine("債券基金 -- {0} ({1}): 現值 {2} 配息率 {3}", item.Id, item.Name, item.CurrentValue, item.Rate);
                }
    
                Console.ReadLine();
            }
        }
    }