Pythonによるデザインパターン【Observer】-本日のニュースをお届けします-


参考

@kidachi1さんの記事のRubyの記事のソースコードをpythonに書き換えてます。
https://qiita.com/kidach1/items/ce18d2a926c558159689
@kidachi1さん、いつもありがとうございます。

概要

Rubyによるデザインパターン第5章。
Observer Pattern。

Rubyによるデザインパターン5原則に則って理解する。
この記事はPython

どんなパターンか

あるオブジェクトの状態に関心のあるオブジェクトに、都度通知を送る。

ニュースの発信源(Subject)とニュースの消費者(Observer)間に綺麗なインターフェイスを用意する。

Subject

あるニュースを配信するクラス

Observer

あるニュースを得ることに関心があるクラス

メリット

ニュースの発信者と受信者の間の依存関係を排除する。

問題のあるコード

従業員の給与の変化を経理部門に伝えるシステム

従業員クラス

class Employee:
  def __init__(self, name, title, salary, payroll):
    self.name = name
    self.title = title
    self.salary = salary
    self.payroll = payroll

  def update_salary(self, new_salary):
    self.salary = new_salary
    self.payroll.update(self)

経理部門クラス

class Payroll:
  def __init__(self):
    pass
  def update(self, changed_employee):  # Subjectオブジェクトを受け取る
    print ( changed_employee.name + "の給料が" + str(changed_employee.salary) + "ドルに上がりました!" )    

クラスの利用

payroll = Payroll()
employee = Employee( "tsuji","leader", 1000, payroll  )
employee.update_salary(2000)

#tsujiの給料が2000ドルに上がりました!

問題

もし経理部門以外にも通知したくなったら?

→今はEmployeeクラスに手を入れる必要がある。
本質的にはEmployeeに対する変更など何もないにも関わらず・・。

そこで、

変化する事項(「従業員の給与の変更」というニュースを誰が受け取るか)を、Employeeオブジェクトから分離する。
→必要なのは、Employeeオブジェクトの変化に関心のあるオブジェクトの一覧。

従業員クラス

class Employee2:
  def __init__(self, name, title, salary ):
    self.name = name
    self.title = title
    self.salary = salary
    self.observers = []

  def add_observer(self, observer):
    self.observers.append(observer) 

  def update_salary(self, new_salary):
    self.salary = new_salary
    self.notify_observers()

  def notify_observers(self):
    for  observer  in  self.observers :
      observer.update(self)

経理部クラス

class Payroll:
  def __init__(self):
    pass
  def update(self, changed_employee):  # Subjectオブジェクトを受け取る
    print ( changed_employee.name + "の給料が" + str(changed_employee.salary) + "ドルに上がりました!" )    
    print ( "経理部門は" + changed_employee.name + "に小切手を切ります!")    

税務署員クラス

class Taxman:
  def __init__(self):
    pass
  def update(self, changed_employee):  # Subjectオブジェクトを受け取る
    print ( changed_employee.name + "の給料が" + str(changed_employee.salary) + "ドルに上がりました!" )    
    print ( "税務署員は" + changed_employee.name + "に新しい税金請求書を送ります!") 

クラスの利用


taxman = Taxman()
payroll = Payroll()
employee2 = Employee2("daisuke","member", 3000)

employee2.add_observer(payroll)
employee2.add_observer(taxman)

employee2.update_salary(4000)

まとめ

変わるもの(オブザーバー)と変わらないもの(サブジェクト)を分離して、変化に強い構造へ

Strategyパターンとの比較

形は似ている

Observerパターン:サブジェクトがオブザーバーを呼び出す
Strategyパターン:コンテキストがストラテジを呼び出す

違いは目的

Observerパターン:サブジェクトで発生したイベントをオブザーバーへ通知する
Strategyパターン:コンテキストが、何か特定の処理を行うためにストラテジを呼び出す