GoFのデザインパターンのイーテレータ(Iterator)のRubyコードを使った紹介記事です。イーテレーターパターンは次のような場合に使います。
- 要素の集まったオブジェクト(配列など)にアクセスする
- 集合の要素に順にアクセスする必要がある
😎 イーテレータとは?
GoFではイーテレータを「集約オブジェクトがもとにある内部表現を公開せずに、その要素に順にアクセスする方法を提供する」と定義しています。
言い換えると、要素の集まりをもつオブジェクトの各要素に、順番にアクセスする方法を提供するためのデザインパターンです。
🚕 内部イーテレータ
Rubyのeachと同じもの。コードブロックベースのイーテレータのこと。
🐯 外部イーテレータ
ここでは次のようなモデルで外部イーテレータを説明します。
Blog
クラス(集約オブジェクト): 複数のArticle
クラスをもつオブジェクト
Article
クラス(集約オブジェクト内の要素): Blog
クラスの個別の要素
BlogIterator
クラス(外部イーテレータ): Blog
の要素Article
にアクセスするためクラス
まず、記事を表すArticle
クラスです。
class Article def initialize(title) @title = title end
attr_reader :title end
|
次は、記事を複数もつブログを表すBlog
クラスです。
class Blog def initialize @articles = [] end
def get_article_at(index) @articles[index] end
def add_article(article) @articles << article end
def length @articles.length end
def iterator BlogIterator.new(self) end end
|
最後に外部イーテレータのBlogIterator
クラスです。
class BlogIterator def initialize(blog) @blog = blog @index = 0 end
def has_next? @index < @blog.length end
def next_article article = self.has_next? ? @blog.get_article_at(@index) : nil @index = @index + 1 article end end
|
ソースコードは以上です。では、実際の動作を確認してみます。
blog = Blog.new blog.add_article(Article.new("デザインパターン1")) blog.add_article(Article.new("デザインパターン2")) blog.add_article(Article.new("デザインパターン3")) blog.add_article(Article.new("デザインパターン4"))
iterator = blog.iterator while iterator.has_next? article = iterator.next_article puts article.title end
|
blog.iterator
で生成した外部イーテレータによって、Blog
クラスの要素Article
に順番にアクセスできていることがわかります。
🍮 Enumerableモジュール
唐突ですが、Rubyの便利モジュールEnumerableの紹介です。Enumerableモジュールをインクルードすると、集約オブジェクト向けの「all?
やany?
、include?
」といった便利なメソッドを取り込むことができます。
以下は取り込んだ場合のサンプルソースです。
class Account attr_accessor :name, :balance
def initialize(name, balance) @name = name @balance = balance end
def <=>=>(other) @balance <=> other.balance=> end end
class Portfolio include Enumerable
def initialize @accounts = [] end
def each(&block) @accounts.each(&block) end
def add_account(account) @accounts << account end end
portfolio = Portfolio.new portfolio.add_account(Account.new("account1", 1000)) portfolio.add_account(Account.new("account2", 2000)) portfolio.add_account(Account.new("account3", 3000)) portfolio.add_account(Account.new("account4", 4000)) portfolio.add_account(Account.new("account5", 5000))
puts portfolio.any? { |account| account.balance > 3000 }
puts portfolio.all? { |account| account.balance >= 2000 }
|
😸 サンプルソース
🤔 参考リンク
🖥 VULTRおすすめ
「VULTR」はVPSサーバのサービスです。日本にリージョンがあり、最安は512MBで2.5ドル/月($0.004/時間)で借りることができます。4GBメモリでも月20ドルです。
最近はVULTRのヘビーユーザーになので、「ここ」から会員登録してもらえるとサービス開発が捗ります!