HomeOur Team
Observer Pattern
Articles
Observer Pattern
phuong.bui
phuong.bui
March 22, 2021
3 min

Bài toán đặt ra.

Trong thời đại cách mạng 4.0 hiện nay, bất kì sự kiện nào, dù là nhỏ nhất cũng rất nhanh được lan toả và cập nhật liên tục, tuy nhiên, giữa muôn vàn các sự kiện, chúng ta phải chắt lọc ra sự kiện mà bản thân quan tâm.

Ví dụ như, bạn quan tâm đến những bài hát mới của Sơn Tùng, bạn muốn bất cứ khi nào Sơn Tùng ra bài hát mới, bạn điều có thể nhận được thông tin nhanh nhất và cài view cho Sếp. Muốn như vậy bạn phải đăng kí kênh Youtube của Sếp để bất cứ khi nào Sếp ra bài mới, bạn đều có thế nhận dc thông báo và có thể ngay lập tức thực hiện action là “cày view”.

Tương tự như vậy, đối với lập trình viên, khi làm một dự án nào đó, sẽ đặt ra một bài toán là làm sao khi một đối tượng đưa ra một sự kiện nào đó, những đối tượng quan tâm đến nó có thể ngay lập tức nhận thông tin và thực hiện các action liên quan.

Một trong những mô hình được sử dụng nhiều nhất để giải quyết các vấn đề trên, là Observer pattern

Observer Pattern

Observer pattern là một mẫu thiết kế phầm mềm, mà trong đó, có một đối tượng gọi là subject , duy trì một danh sách các đối tượng phụ thuộc vào nó gọi là observers, và tự động thông báo đến chúng về bất cứ thay đổi nào

uml

UML class diagram and sequence diagram

  • Trong UML clas diagaram trên, class Subject không trực tiếp update trạng thái của các đối tượng phụ thuộc. Thay vào đó, Subject sử dụng interface Observer (method update()) để cập nhật trạng thái, điều này làm cho Subject độc lập với cách mà trạng thái của các đối tượng phụ thuộc cập nhật. Các class Observer1Observer2 sẽ implement interface Observer, bằng cách này, sẽ tự động đồng bộ trạng thái với Subject.
  • Ở sequence diagram , các đối tượng observer1, observer2 sẽ gọi attach(this) để đăng ký với subject1. Mỗi khi Subject1 thay đổi trạng thái, Subject1 sẽ gọi method notify() trên chính nó. Notify() gọi đến update() trên các đối tượng đã đăng ký là Observer1, Observer2. Các đối tượng này yêu cầu các data đã thay đổi (getState()) từ Subject1 để cập nhật, đồng bộ với trạng thái của Subject1.

Implement Observer pattern

Để bắt đầu implement Observer pattern, mình sẽ bắt đầu bằng ví dụ về Sơn Tùng ở trên.

Như vậy, trong ví dụ này:

- “Sếp” sẽ là Subject

- “Sky” , “Anitifan” chính là Observer

- Sự kiện sẽ là khi sếp tung MV.

  1. Đầu tiên, sẽ tạo 1 Subject la Sep
class Sep {
    var mv: String = ""
    
    // 1. Danh sách các đối tượng phụ thuộc vào Subject 
    private lazy var observers = [Observer]()
    
    // 2. Thêm observer vào list khi nó đăng kí phụ thuộc vào Subject 
    func subscribe(_ observer: Observer) {
        observers.append(observer)
    }
    
    // 3. Xóa observer ra khỏi list khi nó muốn hủy đăng kí và không nhận thông tin từ Subject nữa 
    func unsubcriber(_ observer: Observer) {
        if let idx = observers.firstIndex(where: {$0 === observer}) {
            observers.remove(at: idx)
        }
    }
    
    // 4. Thông báo đến từng observer trong danh sách để chúng thực hiện action 
    func notify() {
        observers.forEach( {$0.viewVideo(for: self)})
    }
    
    // 5. Sự kiện tung MV của Sếp 
    func postMV (with mv: String) {
        self.mv = mv
        notify()
    }
}
  1. Tạo interface Observer: Trong interface sẽ có hành động xem video của Sếp.
protocol Observer: class {
    func viewVideo(for sep: Sep)
}
  1. Khởi tạo các đối tượng muốn cập nhật thông tin từ Sếp, ở đây có 2 đối tượng cần khởi tạo là SkyAntifan. Các đối tượng này sẽ implement interface Observer ở trên, và thực hiện action viewVideo mỗi khi Sếp tung MV
class Sky: Observer {
    func viewVideo(for sep: Sep) {
        switch sep.mv {
        case "Lac troi":
            autoComment(with: "Lac Troi number1")
        case "Chung ta cua hien tai":
            autoComment(with: "Sep number1")
        default:
            break
        }
    }
    
    func autoComment(with comment: String) {
        print("Sky: \(comment)")
    }
}


class Antifan: Observer {
    func viewVideo(for sep: Sep) {
        switch sep.mv {
        case "Chung ta cua hien tai":
            autoComment(with: "Thánh đạo nhạc ")
        case "Lac troi":
            autoComment(with: "Nghe chẳng ra gì")
        default:
            break
        }
    }
    
    func autoComment(with comment: String) {
        print("Antifan \(comment)")
    }
}
  1. Các đối tượng phụ thuộc Sky Antifan đăng ký nhận thông tin từ subject như sau.
func startObserver() {
    // Khai báo các đối tượng, trong đó subject là Sep, observer là Sky và Antifan 
    let subject = Sep()
    let observer1 = Sky()
    let observer2 = Antifan()
    
    // 1. Đăng ký  observer với subject. 
    subject.subscribe(observer1)
    subject.subscribe(observer2)
   
    // 2. Sếp post MV 
    subject.postMV(with: "Chung ta cua hien tai")
    
    print("========")
    // 3. Antifan đã quá chán và hủy đăng ký Sếp, điều này khiến Antifan không nhận dc thông báo nào khi Sếp ra MV 
    subject.unsubcriber(observer2)
    subject.postMV(with: "Lac troi")
}

startObserver()

Kết quả, mỗi khi Sếp thực hiện action postMV, ngay lập tức, SkyAntifan sẽ nhận dc thông báo và comment bình luận như sau :D

screen shot 2021 03 22 at 16 47 57

Observer pattern trong Combine

Apple đã hỗ trợ cho pattern này trong Swift 5.1 bằng cách bổ sung Publisher trong framework Combine . Việc sử dụng Observer pattern bằng cách sử dụng Publiser sẽ dễ dàng và nhanh hơn so với việc triển khai như ở trên.

combine

Trong đó :

  • SubscriberObserver , nhận các thông tin cập nhật
  • Publisher tương ứng với observable : gửi thông tin thay đổi
  • Value là giá trị đối tượng dc thay đổi .

Sau đây là một ví dụ đơn giản về việc sử dụng Publisher trong Swift.

// 1. Phải import Combine thì mới có thể sử dụng Publisher 

import Combine

// 2. Tạo class Sếp, trong đó "mv" là 1 Publisher
 
public class Sep {
    @Published var mv: String = "Chung ta cua hien tai"
}

// 3. khai báo sep và mv của sep 
let sep = Sep()
let mv = sep.$mv

// 4. Khai báo 1 subscriber, subscriber này sẽ thực hiện action mỗi khi mv của Sếp thay đổi 

var subscriber: AnyCancellable? = mv.sink { (name) in
    // TODO: - Action 
    print("\(name) number1 ")
}

sep.mv = "Lac troi"

// 5. Hủy đăng ký, như vậy, subscriber sẽ không nhận dc thông tin và thực hiện action gì khi mv thay đổi. 
subscriber?.cancel()
sep.mv = "Co chac day la yeu "

Kết quả của ví dụ trên, có thể thấy dc là mỗi khi value của publisher thay đổi, ngay lập tức subscriber sẽ thực hiện action. Nhưng nếu subscriber hủy đăng ký, thì nó sẽ không thực hiện action gì nữa. Như sau:

screen shot 2021 03 22 at 17 06 13

Kết luận.

Dựa trên những điều mình đã giới thiệu ở trên, có thể thấy, Observer pattern được dùng để giải quyết các vấn đề sau:

  • Khi có một mối quan hệ một - nhiều giữa các đối tượng, yêu cầu khi một đối tượng có sự thay đổi trạng thái, tất cả các đối tượng phụ thuộc của nó sẽ được thông báo và cập nhật một cách tự động.
  • Một đối tượng có thể thông báo đến một số lượng không giới hạn các đối tượng khác.

Việc áp dụng Observer pattern bằng cách sử dụng Publiser trong Combine có nhiều cách và phức tạp hơn so với ví dụ mà mình đã làm ở trên. Mình sẽ nói thêm vào blog tiếp theo.

Thank m.n vì đã đọc đến đây. hihi :3


Tags

202103observerpattern
phuong.bui

phuong.bui

Developer

Related Posts

Why Protocol-Oriented Programming?
Why Protocol-Oriented Programming?
March 31, 2021
2 min
© 2021, All Rights Reserved.

Quick Links

HomeOur Team

Social Media