The Iterator Pattern trong Ruby
Bài đăng này đã không được cập nhật trong 3 năm
Mình có ví dụ như này:
User.all.each do |u|
puts u.name
end
Kết quả của khối lệnh này bạn sẽ thấy
Nguyen Van A
Nguyen Thi B
Bạn code Ruby, bạn đã bao giờ tự hỏi: tại sao khi sử dụng vòng lặp each do
mà bạn có thể chạy từ đầu mảng đến cuối mảng chưa?
Câu trả lời là: Trong khi định nghĩa hàm each do
, Ruby đã setup sẵn việc lặp cho bạn rồi! Vậy nếu không dùng hàm này, bạn có thể dùng cách nào để in ra như vậy?
"Dùng Iterator" đó là cách mình nghĩ đến! Vậy Iterator là gì?
1. Iterator là gì?
Iteractor cung cấp một cách để truy cập vào dữ những phần tử của một tập các đối tượng tuần tự mà không làm lộ cách thể hiện (VD kiểu dữ liệu) của chúng. Hay có thể nói rằng, Iteractor cung cấp cho bạn một phương pháp, một cách thức định sẵn, mà không cần phải hiểu rõ về những chi tiết bên trong của những tập hợp.
2. UML class diagram
3. Participants
Các lớp và các đối tượng tham gia trong mô hình này là:
Iterator (AbstractIterator): định nghĩa một interface để truy cập và đi qua các phần tử. ConcreteIterator (Iterator): implements interface Iterator, theo dõi vị trí hiện tại trong vòng lặp aggregate Aggregate (AbstractCollection): định nghĩa một interface để tạo một đối tượng Iterator ConcreteAggregate (Collection): implements phương thức tạo interface Iterator để trả về return biến instance của ConcreteIterator riêng
4.Ví dụ
Ví dụ về ứng dụng của Iteractor Pattern bạn có thể nhìn rõ nhất trong Enumerable của Ruby.
Do vậy khi thực hiện các bạn đều có thể dùng như sau:
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
Bây giờ, Iterator có thể cho bạn một cách để có thể chạy qua của hai class là Portfolio và Account. Nhìn xem cách ta sử dụng nhé!
my_portfolio = Portfolio.new
my_portfolio.add_account(Account.new('Bonds', 200))
my_portfolio.add_account(Account.new('Stocks', 100))
my_portfolio.add_account(Account.new('Real Estate', 1000))
my_portfolio.any? { |account| account.balance > 2000 }
my_portfolio.all? { |account| account.balance >= 10 }
my_portfolio.each { |account| puts "#{account.name}: #{account.balance}" }
my_portfolio.map { |account| account.balance }
my_portfolio.max
my_portfolio.min
Các bạn hãy chạy thử nhé!
5.Thêm về Iterators
5.1. External Iterator
Logic của Iterator chứa các class riêng biệt. Class interation này có thể tổng quát hóa để handle nhiều kiểu đối tuonwjg miễn là chúng cho phép đánh chỉ mục (index) External Iterator yêu cầu các lớp bổ sung để thực hiện việc lặp, nhưng chúng cực kì linh hoạt bởi vì bạn có thể control vòng lặp như thế nào, các phần tử nào được lặp lại và lặp theo thứ tự nào.
5.2. Internal Iterator
Tất cả việc xử lý của vòng lặp xảy ra ở bên trong đối tượng của Aggregate. Sử dụng code block để pass qua việc xử lý logic của aggregate sau đó gọi block với mỗi elements Ví dụ:
colors = ['red', 'green', 'blue']
colors.each { |color| puts color }
5.3. Lỗi
Đôi khi, với việc tùy chỉnh các iterator, nếu lớp collection gốc thay đổi trong khi bạn lặp lại thông qua các phần tử của nó, nó có thể tạo ra kết quả không mong muốn. Để khắc phục điều này, bạn có thể có các Iterator hoạt động trên một bản sao của bộ collection ban đầu.
class ArrayIterator
def initialize(array)
@array = Array.new(array)
@index = 0
end
…
6. Tài liệu tham khảo
https://github.com/nslocum/design-patterns-in-ruby#iterator http://index-of.es/Ruby/Design Patterns in Ruby (2007).pdf
All rights reserved