Pythonでデザインパターン実践:Strategy

環境

  • Windows 10
  • Python 3.10.1
  • VSCode

使用するソースコード

以下の公開リポジトリに置いています

GitHub - masayan1126/tao-py-py: 作業自動化用 Python ライブラリ
作業自動化用 Python ライブラリ. Contribute to masayan1126/tao-py-py development by creating an account on GitHub.

Strategyとは

  • Strategyとは何かのアルゴリズムの事を意味します。このパターンでは、1つもしくは複数の命令を1つのオブジェクトで表現します
  • 例えば、if 文などで分岐させることでプログラムの処理を変更するような方法はよく行われますが、Strategy パターンでは、この分岐処理のアルゴリズムを別クラスとして作成するようします
  • 上記のような設計により、アルゴリズムを変更したい場合には、利用するクラスを変更するという方法で対応でき、より柔軟でメンテナンスしやすい設計となります
このパターンを適用すると、アルゴリズムの切り替えや追加が簡単に行えるようになります

実装する内容

Pythonの定番ライブラリ(ブラウザ自動化)であるseleniumを使用する際に、使用するブラウザ(chromeやfirefoxなど)の種別に応じてwebdriverと呼ばれるオブジェクトを作成する必要があります。そのため、match~case文で判定している箇所について本パターンを適用し、Judgementクラスとして置き換えたいと思います。

クラス図

judgeメソッドを呼び出す側のクラスは一般的にはContextと呼ばれますが、今回は便宜上、上記のような名称で作成しています

実装

普通に書くとこのようになる

class XDriverFactory(IFactory):
    def create(self, browser_type:BrowserType, is_headless=True):
        match browser_type:
            case browser_type.CHROME:
                // クローム用のwebdriverを返す
            case browser_type.FIREFOX:
                // firefox用のwebdriverを返す
上記では、コードを省略しているのでそこまで気にならないですが、ifやmatch文をメソッドの中に記述してしまうと肥大化しがちです。(特にそのアルゴリズムが複雑であればあるほど巨大なメソッドになってしまいます)

まず、IJudgementクラスを抽象クラスで作成する

IJudgementクラスには、judgeメソッドをかならず実装する必要があるように指定する

import abc

class IJudgement:
    @abc.abstractmethod
    def __init__(self):
      pass
    @abc.abstractmethod
    def judge(self):
      pass

続いて、Judgementインターフェースを実装する具象クラスを作成する

judgeメソッド内でブラウザ種別に応じてwebdriverのオブジェクトを作成するようにします

・・・割愛

class BrowserJudgement(IJudgement):
    def __init__(self, browser_type, is_headless):
      self.browser_type = browser_type
      self.is_headless = is_headless
    def judge(self):
        match browser_type:
            case browser_type.CHROME:
                chrome_options = webdriver.ChromeOptions()
                ・・・割愛
                return webdriver.Chrome(service=chrome_service, options=chrome_options)
            case browser_type.FIREFOX:
                ・・・割愛
            case ・・・

実際に呼び出してみる

呼び出し側は、newする箇所以外は、どのクラスの戦略を使用しているのか意識しなくても使用することが可能になっていることがわかる。

class XDriverFactory(IFactory):
    def create(self, browser_type: BrowserType, is_headless=True):
        judgement = BrowserJudgement(browser_type, is_headless)
        return XDriver(judgement.judge())
judgement変数は、代入されたjudgementクラスのオブジェクトにより動的に振る舞いを変えることが可能です(多態性)

Python学習におすすめの書籍

独習Python/山田祥寛【3000円以上送料無料】
bookfan 1号店 楽天市場店
¥ 3,300(2022/08/04 17:32時点)
タイトルとURLをコピーしました