迭代器模式

简介

面向对象编程思想以来,程序员们将常见的面向对象编程范式总结起来,就形成了23种设计模式,因为是适用于面向对象编程思想的,所以设计模式种最本质的核心就是继承、接口、封装、高内聚和低耦合的思想。所以在了解设计模式的过程中,多多思考这几个方面,可以帮助大家更好的理解它们。废话不多说,下面就开始介绍迭代器模式

声明: 本次的介绍,借鉴了图灵书籍《图解设计模式》(结城浩·著),示例基本一致;如果大家有兴趣,可以直接阅读该书

迭代器模式最主要的用途是方便我们进行遍历访问;其本质作用类似于下面的代码:

arr = ["三国演义", "红楼梦", "水浒传", "西游记"]
for name in arr:
	print(name)

输出结果:

三国演义
红楼梦
水浒传
西游记

Process finished with exit code 0

迭代器模式就是将某个对象聚合起来,封装成一个聚合类中,该聚合类暴露出来一个能生成可迭代对象的方法;通过访问生成的可迭代对象进行遍历操作。简单来说,就是将上面代码片段中的name封装成类,arr封装成聚合类(内部存放name类实例),以方便我们将遍历操作同聚合方式解耦合。
下面我们来看一下具体实现例子。

接口和具体类

模块说明
aggregateAggregate.聚合类的接口
iterator.Iterator.表示遍历的类的接口
bookBook表示书的类
bookshelf.BookShelf.表示书架的类
bookshelfiteratorBookShelfIterator遍历书架的迭代器类

接口设计说明

本次示例用一个遍历书架上的书,并打印出书的名字来展示;
书是一个独立的对象,有很多属性,比如名称、页数、目录等,而书架则可以聚合很多书,也可以没有书,书架销毁后,并不影响书本身的存在,因为书还可以放在箱子里、柜子里等,所以书架同书之间属于聚合关系。
想象一个场景:现在我们有一整个书架的书,我们想知道其中每一本书的名字,访问顺序应该是这样的:先访问书架-再访问书-再访问书属性name;重复以上操作直到书架上的最后一本书。

这里可以用简单的for循环,但是今天我们用迭代器模式来实现。

UML图示

Iterator模式类图

aggregate模块

提供了一个Aggregate的聚合抽象类,将聚合类的公共方法抽取出来,形成统一的接口。
iterator方法返回一个可迭代对象,访问者可用于遍历操作

iterator模块

提供Iterator迭代器抽象类,定义可迭代对象的接口,当然python中有特殊的魔术方法能够用于实现这个功能,我们为了展示设计模式的思想,暂时用普通方法来实现

Iterator类有两个公有方法has_next()和next(),其中has_next()返回一个bool值,表示迭代器是否存在下一个元素,用来判断是否遍历到末尾;next()方法用来取出下一个对象,实现方式为先取出对象,然后将索引值前移。

bookshelf模块

实现BookShelf类,继承自Aggregate,并实现iterator()方法。返回Iterator对象。
这里只是Aggregate的具体类的其中之一,实际应用中,你可以实现任何你想聚合的对象聚合类

bookshelfiterator模块

实现BookShelfIterator类,继承自Iterator,并实现has_next()和next()方法。
这里一般是同BookShelf一一对应,每实现一个BookShelf类就实现一个对应的BookShelfIterator类。确保对象的聚合和迭代操作的一致性。

book模块

实现了Book类,表示被聚合的对象。

具体实现

Aggregate抽象类

import abc
from abc import ABCMeta


class Aggregate(metaclass=ABCMeta):
    """
    表示某个对象集合的接口类
    """
    @abc.abstractmethod
    def iterator(self):
        pass

Iterator抽象类

import abc
from abc import ABCMeta


class Iterator(metaclass=ABCMeta):
    """
    表示遍历Aggregate的接口类
    """
    @abc.abstractmethod
    def has_next(self) -> bool:
        pass

    @abc.abstractmethod
    def next(self):
        pass

Book类

class Book:
    """
    表示书的类
    """
    def __init__(self, book_name):
        self.name = book_name

    def get_name(self):
        return self.name

BookShelf类

from DesignMode.IteratorMode.aggregate import Aggregate
from DesignMode.IteratorMode.book import Book
from DesignMode.IteratorMode.bookshelfiterator import BookShelfIterator


class BookShelf(Aggregate):
    """
    表示书架的类
    """
    def __init__(self):
        self.__length = 0
        self.__books: [Book] = list()

    def get_book_at(self, index: int) -> Book:
        return self.__books[index]

    def append_book(self, book: Book) -> bool:
        self.__books.append(book)
        self.__length = len(self.__books)
        return True

    def get_length(self):
        return self.__length

    def iterator(self) -> BookShelfIterator:
        return BookShelfIterator(self)

BookShelfIterator类

from DesignMode.IteratorMode.iterator import Iterator


class BookShelfIterator(Iterator):
    """
    表示遍历书架的类
    """
    def __init__(self, book_shelf):
        self.__book_shelf = book_shelf
        self.__current_index = 0

    def has_next(self) -> bool:
        if self.__current_index < self.__book_shelf.get_length():
            return True
        return False

    def next(self):
        book = self.__book_shelf.get_book_at(self.__current_index)
        self.__current_index += 1
        return book

Client类

from DesignMode.IteratorMode.book import Book
from DesignMode.IteratorMode.bookshelf import BookShelf


class Client:
    """
    表示测试设计模式的客户端类;可以理解为一个用户角色
    """
    @staticmethod
    def run(*args, **kwargs):
        book_shelf = BookShelf()
        book_shelf.append_book(Book("三国演义"))
        book_shelf.append_book(Book("红楼梦"))
        book_shelf.append_book(Book("水浒传"))
        book_shelf.append_book(Book("西游记"))

        book_shelf_iterator = book_shelf.iterator()
        while book_shelf_iterator.has_next():
            book = book_shelf_iterator.next()
            print(book.get_name())


if __name__ == '__main__':
    Client.run()

运行结果

三国演义
红楼梦
水浒传
西游记

Process finished with exit code 0

重新出发

上面是迭代器模式的简单示例,看到这里有些小伙伴可能觉得别扭,上面的编程风格非常的不python,python里面提供了很多魔术方法,能够更加方便的实现迭代器模式。的确,我们来重新实现一版更python化的代码

Aggregate类

class Aggregate(metaclass=ABCMeta):
    @abc.abstractmethod
    def __getitem__(self, item):
        pass
    
    @abc.abstractmethod
    def __len__(self):
        pass
    
    @abc.abstractmethod
    def append(self, item):
        pass
    
    @abc.abstractmethod
    def iterator(self):
        pass

Iterator类

class Iterator(metaclass=ABCMeta):

    @abc.abstractmethod
    def __next__(self):
        pass

    @abc.abstractmethod
    def __iter__(self):
        pass

BookShelf类

class BookShelf(Aggregate):
    def __init__(self):
        self.__books = list()

    def __getitem__(self, item):
        return self.__books[item]

    def __len__(self):
        return len(self.__books)

    def append(self, book):
        self.__books.append(book)

    def iterator(self):
        return BookShelfIterator(self)

BookShelfIterator类

class BookShelfIterator(Iterator):
    def __init__(self, book_shelf):
        self.__book_shelf = book_shelf
        self.__current_index = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.__current_index < len(self.__book_shelf):
            book = self.__book_shelf[self.__current_index]
            self.__current_index += 1
            return book
        return None

Book类

class Book:
    def __init__(self, book_name):
        self.__name: str = book_name

    @property
    def name(self):
        return self.__name

    def __str__(self):
        return self.__name
class Client:
    """
    表示测试设计模式的客户端类;可以理解为一个用户角色
    """
    @staticmethod
    def run(*args, **kwargs):
        book_shelf = BookShelf()
        book_shelf.append(Book("三国演义"))
        book_shelf.append(Book("红楼梦"))
        book_shelf.append(Book("水浒传"))
        book_shelf.append(Book("西游记"))

        book_shelf_iterator = book_shelf.iterator()
        book = next(book_shelf_iterator)
        while book:
            print(book)
            book = next(book_shelf_iterator)

在第二种方式中我们使用了很多python的魔术方法,代码量变多了,看起来似乎还是第一种更好,不过如果是在实际项目中,不可能会像我们所举的例子这么简单,运用魔术方法不仅可以让我们的代码看起来简洁,而且不需要定义很多额外的方法,诸如get_book_at、get_length、get_name等,相比python常用的调用方式和调用方法,这些额外方法显得冗余而又格格不入。使用魔术方法能够保证调用方式的一致性。

第二种方式实现的BookShelf实际上更像是一个列表了,除了我们没有实现的方法之外,它的使用同列表基本没差,你也可以在BookShelf内部实现__iter__、__next__方法,将Iterator和BookShelf合二为一,不过这样一来,就等同于实现一个书架列表。
iterator是可以用来直接for循环的

class BookShelfIterator:
	# ...
	def __next__(self):
		if self.__current_index < len(self.__book_shelf):
            book = self.__book_shelf[self.__current_index]
            self.__current_index += 1
            return book
        return StopIteration # 使用for循环一定要抛出这个异常,才能停止

for book in book_shelf_iterator:
	print(book)

上面代码的输出结果是一样的

总结

其实说白了,迭代器模式就是包装一个类似容器的类,然后通过一个迭代器类来遍历它。就好比文章开头的for循环。既然一个for循环就可以搞定的逻辑,何必要大费周章的搞一个设计模式出来呢?

迭代器模式最大的优点是将 遍历和实现分开!这也就是为什么第二种方式中没有将BookShelf和BookShelfIterator合二为一的原因。
遍历和实现分开后,无论通过哪种循环遍历,都不依赖聚合类,只依赖Iterator的实现。

比较常见的场景是

1.我们可能会更改book的存储方式,比如通过队列、栈、字典等方式,此时只需要修改BookShelf类即可,其他代码可复用
2.相比常规的遍历方式,还可以从后往前遍历、支持双向遍历、支持跳跃式遍历

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值