Masayan tech blog.

  1. ブログ記事一覧>
  2. Pythonでデザインパターン実践:Strategy

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

公開日

環境

  • Windows 10
  • Python 3.10.1
  • VSCode

Strategyとは

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

このパターンを適用すると、アルゴリズムの切り替えや追加が簡単に行えるようになります

実装する内容

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

クラス図

  • IJudgement
    • 判定に関する戦略のインタフェース。
  • BrowserJudgement
    • IJudgementを実装したクラス。seleniumで使用するブラウザの種別を判定し、それに応じたwebdriverオブジェクトを生成する
  • XDriverFactory
    • Driverオブジェクトを生成するクラス(Factory Methodパターンを使用しています。)

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学習におすすめの書籍

[rakuten id="booxstore:12581340" kw="独習Python"]

まとめ

いかがでしたでしょうか。本記事では、PythonでStrategyパターンを実践する方法について紹介しています。Strategyは、何かのアルゴリズムの事を意味します。このパターンを使用することで、クラスでアルゴリズムを表現できるようになるので、より柔軟でメンテナンスしやすい設計となります。