12/08/2018, 14:38

Object Pool Pattern

Chúng ta đều quen thuộc với singleton - một đối tượng một khi đã được khởi trị sẽ tồn tại suốt vòng đời chương trình. Tuy nhiên, trong một số trường hợp, chúng ta cần khởi tạo và sử dụng một tập hợp các đối tượng, mà với số lượng lớn, thì việc khởi tạo nhiều lần sẽ gây lãng phí không cần thiết, ...

Chúng ta đều quen thuộc với singleton - một đối tượng một khi đã được khởi trị sẽ tồn tại suốt vòng đời chương trình. Tuy nhiên, trong một số trường hợp, chúng ta cần khởi tạo và sử dụng một tập hợp các đối tượng, mà với số lượng lớn, thì việc khởi tạo nhiều lần sẽ gây lãng phí không cần thiết, trong những trường hợp như vậy, chúng ta dùng object pool pattern

Chúng ta dùng Object Pool Pattern quản lý một tập hợp các objects mà sẽ được tái sử dụng trong chương trình. Chúng được gọi ra từ pool, sử dụng trong một khoảng thời gian nhất định rồi trả về pool. Trong khoảng thời gian vắng mặt đó của object, không thành phần nào có thể sử dụng tận khi nó được quay trở về pool.

Object Pool Pattern được coi là sử dụng thích hợp khi có nhiều hơn một đối tượng và số đối tượng được khởi tạo là hạn chế.

Trong iOS, chúng ta đều đã quen thuộc với tableView, và đều biết cell trong tableview có thể reuse, về bản chất chính là dùng Object Pool Pattern.

Một OPP có 4 hành động quan trọng, bao gồm:

  1. Khởi tạo --- Một tập hợp các đối tượng được tạo ra
  2. Xuất --- Lấy một đối tượng ra khỏi pool trong một khoảng thời gian nhất định
  3. Sử dụng --- Sau khi lấy đối tượng ra khỏi pool, đối tượng sẽ được sử dụng cho những mục đích cụ thể
  4. Nhập --- Đối tượng được trả lại pool

4 hành động trên được thể hiện cụ thể qua generic class gọi là Pool, class này có hàm init khởi trị, hàm checkOut để xuất đối tượng khỏi pool, hàm checkIn để trả đối tượng về pool sau khi sử dụng xong. Cụ thể như sau:

class Pool<T> {
private var data = [T]()
private let arrayQ = dispatch_queue_create("arrayQ", DISPATCH_QUEUE_SERIAL); private let semaphore:dispatch_semaphore_t

    init(items:[T]) {
        data.reserveCapacity(data.count)
         for item in items {
            data.append(item)
        }
        semaphore = dispatch_semaphore_create(items.count)
    }
    
    func getFromPool() -> T? {
        var result:T?
        if (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) == 0) {
            dispatch_sync(arrayQ, {() in
                result = self.data.removeAtIndex(0)
            })
        }
        return result
    }
    
    func returnToPool(item:T) {
        dispatch_async(arrayQ, {() in
            self.data.append(item)
            dispatch_semaphore_signal(self.semaphore)
        })
    }
}

Trong đoạn code trên, chúng ta tạo một serial queue gọi là arrayQ để chứa các hành động xuất - nhập object, việc dùng serial queue đảm bảo rằng các hành động xuất - nhập sẽ luôn được thực hiện trên một thread, và điều này tránh được việc 2 thread cùng thay đổi giá trị của mảng chứa các objects ở cùng một thời điểm, nguyên nhân khiến app bị crash.

Đem áp dụng pool trên vào ứng dụng quản lý thư viện, nơi mà các đầu sách thường xuyên được xuất - nhập (mượn - trả), ta có:

final class Library {
    private let books:[Book]
    private let pool:Pool<Book>
    
    private init(stockLevel:Int) {
        books = [Book]()
        for count in 1 ... stockLevel {
            books.append(Book(author: "Dickens, Charles", title: "Hard Times",
                stock: count))
        }
        pool = Pool<Book>(items:books);
    }
    
    private class var singleton:Library {
        struct SingletonWrapper {
            static let singleton = Library(stockLevel:2);
        }
        return SingletonWrapper.singleton;
    }
    
    class func checkoutBook(reader:String) -> Book? {
        var book = singleton.pool.getFromPool()
        book?.reader = reader
        book?.checkoutCount += 1
        return book
    }

     class func returnBook(book:Book) {
        book.reader = nil
        singleton.pool.returnToPool(book)
    }
    
    class func printReport() {
        for book in singleton.books {
            println("...Book#(book.stockNumber)...")
            println("Checked out (book.checkoutCount) times")
            if (book.reader != nil) {
                println("Checked out to (book.reader!)")
            } else {
                println("In stock")
            }
        }
    }
}

Trong đoạn code trên, chúng ta tạo đối tượng Libary quản lý thư viện, trong đó có chứa một mảng cách đầu sách (books) và một pool. Vì đối tượng thư viện sẽ được dùng chung toàn ứng dụng nên chúng ta khởi tạo nó như một singleton. Library có các hàm init() với stockLevel được coi như số bản copy của một quyển sách, checkoutBook(            </div>
            
            <div class=

0