设计模式(十):从电影院中认识"迭代器模式"(Iterator Pattern)

上篇博客咱们从醋溜土豆丝与清炒苦瓜中认识了“模板方法模式”,那么在今天这篇博客中咱们要从电影院中来认识"迭代器模式"(Iterator Pattern)。“迭代器模式”顾名思义就是经过迭代的形式来取出容器中的值。若是你对Java语言熟悉的话,那么你应该使用过Java中的迭代器,迭代器通常使用hasNext()方法来判断是否有下一个值,若是有下一个值的话,那么就使用next()方法来获取下一个值。本篇博客中就从“电影院”中来认识一下这种“迭代器模式”,而且将数组与字典使用迭代器进行遍历。具体说来使用迭代器的数组与字典对外所展示的遍历方式是一致的,也就是说用户在遍历字典或者数组时,所使用的方法是一致的。当然咱们今天的任务是使用Swift语言去实现属于Swift的迭代器,使用该迭代器来遍历字典和数组html

今天博客中所使用的场景与电影院有关,咱们假设在一家商场中有两家电影院,而商场中间的大屏幕会滚动展现两家电影院所放映的电影。两家电影院的区别就是其存储电影资源的方式不一样,一个是使用数组来存储的电影,一个是使用字典来存储的电影。若是不使用迭代器模式的话,那么两家电影院所遍历电影的方式确定是不一样的。今天博客中咱们先给出无“迭代器模式”中遍历两家电影资源的方式,而后在给出“迭代器模式”下遍历两家电影院中电影资源的方式。固然,在下方使用迭代器遍历时,咱们也使用了“工厂方法模式”,具体请参见下详细实现。git

下方是迭代器模式的定义:github

迭代器模式:提供一种方法顺序访问一个聚合对象中的每一个元素,而又不暴漏其内部的表示。编程

 

1、无“迭代器”的电影院设计模式

1.无“迭代器”电影院的类图数组

在本篇博客中的第一部分咱们先给出无“迭代器”的电影院遍历其电影资源的方式。由于不一样电影院存储电影资源的方式不一样,因此在没有迭代器模式下二者的遍历方式是不一样的。在代码设计中咱们一向遵循“依赖接口编程,而不依赖具体实现”的原则,下方虽然没有使用“迭代器模式”,可是咱们仍是要定义接口的。下方的类图中的CinemaType01接口就是咱们全部影院要实现的接口。在该协议中有一个display()方法,用来展现该影院正在放映的电影,而咱们的Market(商场)类就是依赖于这个影院接口,在Market的类中要调用具体影院的display()方法来展现放映的电影。要说明一点就是CinemaType01协议是咱们商场中给商场中的电影院所制定的规则,由于咱们的Market要调用display()方法将商场中的影院所热播的电影投影到大屏幕上,只要是入驻该Market的影院就得遵循CinemaType01协议,并实现display()方法。由于影院存储电影的方式不一样因此display()的实现也不一样。post

经过下方的类图,咱们容易看到,Market虽然是调用具体影院的display()方法。可是Market不依赖于影院的具体实现,而是依赖于影院的接口,也就是下方的CinemaType01协议。Cinema01和Cinema01两个类就是咱们商场中的两个具体的电影院了。Cinema01中的items是个数组,Cinema02中的是个字典。没有使用“迭代器”的商场影院的总体设计的类图以下所示:测试

 

 

2. 代码实现url

上面的类图是咱们的设计,也就是相似于设计图纸。接下来咱们开始搬砖,要对上面的设计图纸进行实现,也就是咱们的代码实现。因该示例比较简单,因此在该部分咱们实现完毕后会给出相应的测试用例。测用例就至关于盖好的商品房要作样板间同样呢。下方片断就是咱们给出的代码实现。spa

在下方代码片断中,最上方就是咱们全部电影院都要实现的协议CinemaType01,在该协议中咱们声明了display()方法来展现当前该影院放映的电影。紧接着该协议下方的两个实现就是咱们的两个电影院Cinema01和Cinema02。在这两个电影院中一个使用的Array来存储的影片资源,一个使用的是Dictionary来存储的电影资源。Cinema01中的display()所作的事情就是对数组的遍历,而Cinema02所作的事情就是对字典的遍历。而咱们的商场类Market就负责调用相应电影院中的display()方法来展现商场中电影院所热播的电影。

在Market类中咱们须要注意一下,全部影院是存在cinemas数组中的。而咱们在声明cinemas的数组类型时,为该数组的泛型指定的是CinemaType01协议(也就是是接口),这说明cinemas存储的是遵循CinemaType01协议的全部电影院,而不只仅是这两个电影院。这样若是来了第三家电影院就能够无缝的给咱们商场的大屏幕进行对接了。下方就是咱们的具体实现。

   

 

三、测试用例

咱们搬完砖盖完房子了,接下来到了测试的时间了,下方就是咱们的测试用例。由于该示例比较简单,因此咱们的测试用例也是比较简单的,就下边简简单单的几行代码。虽然简单,可是测试用例仍是比较简单的。下方咱们建立了一个商场对象,而且给该商场对象指定了两家电影院,也就是咱们商场中所入驻的电影院。而后咱们的商场会调用display()方法来打印所播放的电影,具体以下所示:

    

 

2、使用“迭代器”来规范商场中的影院

上面的设计彷佛没有什么问题,也便于后期的扩充,在来一家影院的话,只要实现商场中定义的CinemaType01规则便可,也就是实现display()方法便可。由于咱们的Market要调用这个display()方法。可是每一个电影院中存储电影资源的方式不一样,致使display()方法各有不一样。商场为了规范电影院,提出每一个电影院要使用同一个的display()方法。由于这个要求是经过迭代器来实现的,因此咱们将电影院中的display()方法进行了一个重命名,咱们将其重命名为iteratorItem()

下面我要引入迭代器,而后就能够将每一个电影院中的display()方法进行提取了。咱们引入迭代器后将display()方法重命名为iteratorItem()。咱们统一影院中的iteratorItem()方法的解决方案是引入迭代器。由于迭代器对外使用的方式是同样的,咱们能够为不一样的数据类型指定不一样的迭代器,好比数组迭代器,字典迭代器。不管是什么迭代器,外部使用该迭代器的方式是一致的,这样就能够作到不管使用什么类型来存储电影资源,只要使用迭代器遍历,其遍历方式是不变的。可能此处说的有些抽象,接下来将会给出具体的设计细节与实现细节,来直观的感觉一下迭代器的魅力。有了迭代器咱们就可使用一个display()方法(也就是该部分的iteratorItem()方法)来遍历不一样类型的数据了。

1.引入“迭代器”后的电影院

仍是老套路,首先咱们会给出类图的设计,而后再给出具体实现呢。下方就是咱们引入“迭代器”后的类图。大眼一看也许会有些抽象,客官莫着急,听我慢慢道来。在下方类图中大致分为三个模板一个是Market类,这个类与以前没有什么区别。绿框中是咱们引入的迭代器,黄框中是咱们重构后的电影院,在电影院使用迭代器后,咱们在此使用了工厂方法模式,具体请看下方详述。

咱们先来看今天的主题,也就是迭代器的设计思路。下方绿框中是咱们引入的迭代器。固然咱们是依赖接口编程的,迭代器怎么能没有接口呢。在迭代器设计之初咱们先给出迭代器的协议,也就是绿框中的Iterator协议。该协议中有两个方法,一个是hasNext(),用来判断是否有下一项。另外一个是next()方法,若是有下一项,那么就使用next()方法来获取下一项。在遵循Iterator协议的基础上咱们给出了数组迭代器ArrayIterator和字典迭代器DictionaryIterator的实现。在代码实现中咱们会给出详细的实现方式。

接着咱们看黄框中的部分,也就是咱们使用工厂方法模式重构后的电影院。在电影院协议CinemaType中定义了电影院中要实现的方法,其中的iteratorItem()方法就是咱们上一部分的display()方法。在引入迭代器后,全部电影院使用迭代器进行遍历元素的方式都是同样的(都是使用hasNext()和next()方法),全部咱们将iteratorItem()方法的默认实现放在了CinemaType协议的默认延展中。而createIterator()方法就是咱们的工厂方法,它负责建立相应的迭代器。createIterator()方法依赖于迭代器的接口而不依赖于迭代器的具体实现。若是是对数组进行遍历,那么该方法建立的就是数组迭代器,若是是对字典遍历,那么建立的就是字典迭代器。

    

 

2. 上方类图的具体实现

接下来咱们有到了搬砖的时刻了,在该部分将会对上述类图进行具体的代码实现,固然咱们使用的仍然是Swift语言。若是你对其余语言使用起来更为驾轻就熟,你就使用你拿手的面向对象语言来实现呢。下方咱们会先给出迭代器的实现,而后在给出电影院的实现。Market类的结构基本不变。

(1)、实现咱们的迭代器

从上面类图的绿框中咱们不难看出,咱们要先给出迭代器协议的实现,而后给出数组迭代器和字典迭代器的具体实现。下方代码片断就是对应着上方类图中绿框部分的实现。Iterator协议中的内容比较简单,就是声明了外部使用迭代器的两个方法:hasNext()和next()。

ArrayIterator就是咱们实现的数组迭代器。在ArrayIterator中对数组进行了遍历,position记录了下一个元素的下标,hasNext()中所作的事情就是经过position来判断是否有下个元素。next()就是经过position来获取数组中的值。DictionaryIterator就是咱们建立的字典迭代器,该迭代器的功能是对字典进行遍历的。其中的position也是用来记录下一个元素下标的,其中的allKeys数组用来获取当前字典的全部key的,allKeys数组经过当前的position来获取key,而后经过key获取该字典相应的值。具体实现方式以下所示:

    

 

(2)、在影院中使用上述迭代器

接下来就是规范影院的时候到了,商场规定入驻本商城的电影商家只须要选择相应的迭代器便可。遍历的默认实现会在电影院协议的默认延展中给出。下方代码片断就是使用迭代器后的影院实现。

CinemaType协议就是商场规定的影院协议,其中定义了两个方法,createIterator()方法负责来建立特定的迭代器,iteratorItem()方法则负责使用createIterator()建立的迭代器来变量相应的数据。此处的createIterator()方法就是咱们“工厂方法模式”中的工厂方法,也就是说在咱们设计影院结构时咱们使用了“工厂方法模式”。关于工厂方法模式的更多的细节,请参考以前的工厂模式主题的博客《设计模式(四):从“兵工厂”中探索简单工厂、工厂方法和抽象工厂模式》,关于工厂模式在此就不作过多的赘述了。

extension CinemaType就是电影院协议的默认延展。其中给出了iteratorItme()方法的具体实现,该方法适用于全部迭代器的遍历。由于全部迭代器都遵循于Iterator协议,都有hasNext()方法与next()方法,全部全部电影院均可以使用该方法来迭代遍历本身的元素。

Cinema01和Cinema02这两个类则是咱们电影院的具体实现。两个类都遵循CinemaType协议,并给出了createIterator()方法。Cinema01使用的是数组来存储的电影资源,全部建立的是数组迭代器。Cinema02使用的是字典存储的电影资源,因此建立的是字典迭代器。不管建立什么样的类型的迭代器,iteratorItme()方法都是能够正常使用的,这也就是使用迭代器的好处。

     

 

(3)、商场类与测试用例

通过上面的两步,咱们已经将迭代器的核心实现完毕。下方的代码就是咱们的Market类与测试用例。Market类几乎没有变化,咱们以前在Market中调用的是电影院中的display()方法,只不过如今咱们是调用电影院的iteratorItem()方法。而Market类的使用方式没有任何的变化,也就是咱们的测试用例没有任何的变化。下方就是咱们的Market类与测试用例以及输出结果。

    

 

至此咱们的迭代器模式的完整实例已经实现完毕,其好处就是在于若是商场进入了第三家电影院,只须要遵循相应的协议并指定相应的迭代器便可。至于如何遍历,交给咱们的默认实现来作。

今天博客中的代码仍然会在Github上进行分享,分享地址为:https://github.com/lizelu/DesignPatterns-Swift

相关文章
相关标签/搜索