Pythonのテンプレートメソッドデザインパターン

ソフトウェアエンジニアリングのプロセスでは、ソフトウェアの構造やコードの内部に多くの冗長な要素があることがしばしば明らかになります。

そのため、開発者は単なるソリューションではなく、理解しやすく保守しやすいコードを書くことが重要な責務となります。

しかし、プロジェクトは時間とともに複雑になることが多く、最初のソフトウェア構造は、最初からよく考え、実装しなければならない重要な要素となっています。

デザインパターンは、この問題を解決するためのものです。

デザインパターンは、オブジェクト指向プログラミング(OOP)パラダイムに関連する特定のタスクを最適化するために使用される標準のセットである。

また、このような設計は、コード行数を減らし、構造を最適化し、ソフトウェアアーキテクチャを標準化することを目的としています。

オブジェクト指向プログラミングのパラダイムは、クラスに基づいた構造を提供し、各クラスは独自の属性とメソッドを持つオブジェクト(そのクラスのインスタンス)の青写真を表しています。

これらのクラスは互いに関連し、依存関係、合成、継承などを持つ。

現実の問題や構造をソフトウェアソリューションに置き換えることが、この構造を実装する主な動機となります。

このガイドでは、Behavioral Design Patternsの1つとPythonでの実装であるTemplate Methodを探求していきます。

このデザインパターンは、複数のステップから構成される一般的なメソッドを与えてくれるでしょう。

テンプレートメソッドクラスに関連するクラスは、これらのステップを個別に呼び出したり、上書きしたりすることができます。

さらに、抽象ベースクラス(ABC)の継承関係を定義したPythonのABCライブラリの使用方法を学びます。

このライブラリを使って、簡単なテンプレートメソッドの例を作成します。

行動デザインパターン

テンプレートメソッドはBehavioral Design Patternの1つです。

広く認識されているソフトウェアデザインパターンは3つあります。

創造的、構造的、そして行動的です。

  • 創造的デザインパターンは、オブジェクトの作成ロジックを抽象化/隠蔽しながら、オブジェクトの作成を可能にすることを目的としています。また、このパターンでは、オブジェクトの作成ロジックを抽象化・隠蔽することで、オブジェクトの作成を可能にする。
  • 構造的デザインパターンは、オブジェクトとクラスの構成を扱うもので、オブジェクトの生成と機能の付与を制御するために継承に依存する。
  • オブジェクト間の通信、オブジェクト間のデータ移動の制御、クラス間の動作の分散に焦点を当てたデザインパターンである。

テンプレートメソッドは、クラス間で関数やメソッドを分散させることに重点を置いています。

テンプレートメソッドデザインパターン

テンプレートメソッドデザインパターンは、ある処理を完了するために必要ないくつかのステップを含む基底クラスを作成することを可能にします。

テンプレートを使ってこれらのステップを定義すると、1つ以上の具象クラスを作成し、テンプレートのステップを上書きすることが可能になります。

これにより、プロセス全体を上書きすることなく、具象クラスに応じてステップの一部または全部を実装することができます。

テンプレートメソッドを使用するには、抽象クラスが必要です。

抽象クラスは、基本的に1つの大きな処理を小さなステップやマイナーな処理に分割したものです。

言い換えれば、抽象クラスはテンプレートメソッド(主要な処理)を利用し、テンプレートの中に主要な処理を完成させるための細かいステップの呼び出しを見出すことになります。

この細かい処理は、具象クラスが呼び出すことのできるメソッド/関数になります。

抽象クラスを使うと、テンプレートメソッドで定義された処理にアクセスするために、ベースクラス全体をインスタンス化する必要がありません。

その代わり、抽象クラスからサブクラスを作成し、個々のサブクラスで必要なステップだけを上書きすることができます。

抽象クラスを定義したら、必要なステップを上書きする具象クラスを作成することができます。

これを実現するために、継承関係を利用します。

具象クラスのコンテキストに応じて、すべてのステップを上書きするか、一部のステップだけを上書きするかを決めます。

このようなテンプレートメソッドの構造を、OOPパラダイムに則ってクラス図で表すと、次のようになります。

ここでは、まず、複数のステップ/関数からなるテンプレートメソッドを持つ抽象クラスを作成することから始めます。

この抽象クラスから、テンプレート・メソッドの異なるステップを使用する2つの具象クラスを作成します。

テンプレートメソッドとファクトリーメソッドの比較

Templateメソッド・パターンとFactoryメソッド・パターンの違いについて、混乱が見られることがある。

これは、同じものではないものの、その構造が似ているからである。

ファクトリーメソッドは、スーパークラスからオブジェクトを作成するために使われるCreational Patternである。

これに対して、Template Methodは、テンプレートメソッドを含む抽象クラスのサブクラスが修正できるステップからなる一般的なメソッドを定義するために使用されるBehavioral Patternである。

つまり、ファクトリーメソッドがオブジェクトを生成するのに対して、テンプレートメソッドはメジャー/ベースプロセスの機能を上書きするのです。

さて、これらのパターンの違いを明確にしたところで、PythonでTemplate Method Design Patternを実装する方法を探ってみましょう。

注意:Pythonは特定のライブラリを使用しない抽象クラスはサポートしていません。

抽象クラスの関連付けを使用するには、ABCライブラリをインポートする必要があります。

ABCライブラリー

ABCライブラリは、Pythonの抽象ベースクラスを管理するためのインフラストラクチャを提供します。

つまり、抽象クラスに対して継承や実装のようなクラス関係を作ることができます。

これはほとんどのデザインパターンを実装するために必要なことで、テンプレートメソッドの場合は特に重要です。

テンプレートメソッドパターンはいつ使うのか?

アルゴリズムの一部または全部のステップを使用または変更する必要がある場合、テンプレートメソッドを使用したいと思うでしょう。

このような場合、アルゴリズムやプロセスのステップを区別し、継承や実装によって個別にアクセスできるようにする必要があります。

実際の例を見てみましょう。

A大学の研究者とB大学の研究者の2つのグループがあります。

この2つのグループは、SARS-CoV-2の大流行に対して政府が実施した検疫の効果について研究しています。

どちらのグループも基本的な研究プロセスは同じである。

基本的な研究プロセスは、2つの研究グループが調査を進めるための雛形である。

しかし、研究グループは、次のような点で研究プロセスをカスタマイズすることができます。

  • 調査中にどのステップを実行するか。
  • 各調査ステップをどのように行うか。

Pythonのコードでモデルを作成する前に、この研究をクラス図で表現してみましょう。

研究指針は4つのステップで構成されています。

  • A大学は4つのステップのうち2つ(2と3)を適用することを決定する。
  • B大学は3つのステップ(1、3、4)を適用する。
  • 両グループとも、選択したステップをすべて変更した。
  • 最後に、ステップ番号 3 は必須であるため、両グループで適用する必要があります。

私たちはすでにダイアグラムクラスを持っているので、問題に合うようにそれを変更するだけです。

私たちが指定した条件に合うようにダイアグラムを更新すると、次のようなモデルができあがります。

Pythonによるテンプレートメソッドデザインパターンの実装

抽象クラスと具象クラスのアウトラインができたので、それらをPythonで実装してみましょう。

まず、抽象クラスである researchGuideline.py から始めましょう。

このクラスには、研究の4つの主要なステップを表すテンプレートメソッドが含まれます。

まず、ABC ライブラリをインポートします。

このライブラリには ABC というクラスがあり、これを研究テンプレートのスーパークラスとして使用し、抽象的な基底クラスとします。

次に、ステップをクラスメソッドとして定義します。

これらのメソッドは今のところ空ですが、サブクラスを定義したときに、ステップを上書きすることになります。

# Importing the ABC library
from abc import ABC, abstractmethod


# Creating our abstract class:
class ResearchGuideline(ABC):

    # Template Method definition:
    def templateMethod(self):
        # Calling all the steps
        self.step1()
        self.step2()
        self.step3()
        self.step4()

    # Defining the Template Method Steps
    def step1(self):
        pass


def step2(self):
        pass

    @abstractmethod
    def step3(self):
        pass


def step4(self):
        pass


ステップ 3 に @abstractmethod デコレーターを追加していることに注目してください。

これは、抽象クラスのサブクラスは常にそのメソッドを上書きしなければならないことを表しています。

このデコレータは ABC ライブラリの一部でもあるので、import に含める必要があります。

では、具体的なクラスを定義してみましょう。

ここでは、大学Aと大学B、それぞれのステップについて説明します。

ここでは、ResearchGuidelineというテンプレートを使って、両方の大学のサブクラスを作成します。

どちらのクラスも ResearchGuideline クラスをインポートし、スーパークラスとサブクラスの間に継承を作成する必要があります。

これにより、ガイドライン/テンプレートで定義したステップを上書きして使うことができます。

ステップの応用として、今回は単純なlog/printを行うことにします。

まず、最初のサブクラスから見てみましょう。

from researchGuideline import ResearchGuideline


class UniversityA(ResearchGuideline):
    def step2(self):
        print("Step 2 - Applied by University A")

    def step3(self):
        print("Step 3 - Applied by University A")


これをuniversityAというPythonファイルに保存しておきます。

では、2番目のサブクラスを設定しましょう。

from researchGuideline import ResearchGuideline


class UniversityB(ResearchGuideline):
    def step1(self):
        print("Step 1 - Applied by University B")

    def step3(self):
        print("Step 3 - Applied by University B")


def step4(self):
        print("Step 4 - Applied by University B")


これは universityB という名前のPythonファイルに保存します。

どの大学がどのステップを適用しているかを示していることに注目してください。

これは2つの具象クラス間の差異を理解するのに役立ちます。

抽象クラスと具象クラスを含むテンプレートメソッドのモデルは完成しました。

では、このモデルを適用するためのクライアントスクリプトを作成しましょう。

まず、クラスをインポートします。

これは抽象クラスと2つの具象クラスをインポートすることです。

そして、テンプレート/抽象クラスである ResearchGuideline オブジェクトをパラメータとして受け取る関数を作成します。

ここが継承関係の素晴らしいところで、大学クラスは ResearchGuideline のサブクラスなので、同じオブジェクトタイプを共有します。

テンプレートメソッドを呼び出す関数(以下の client_call() )に UniversityA または UniversityB オブジェクトを引数として渡すことができ、具象クラスで上書きされるステップによってテンプレートメソッドの実行方法が変更されます。

ここでは、両方のクラスを使用して、出力を比較することができます。

# Imports
from researchGuideline import *
from universityA import UniversityA
from universityB import UniversityB


# Auxiliary function
def client_call(research_guideline: ResearchGuideline):
    research_guideline.templateMethod();


# Entry point
if __name__ == '__main__':
    # Calling the Template Method using the University A class as parameter
    print("University A:")
    client_call(UniversityA())

    # Calling the Template Method using the University A class as parameter
    print("University B:")
    client_call(UniversityB())


このコードを実行すると、次のような出力が得られます。

University A:
Step 2 - Applied by University A
Step 3 - Applied by University A
University B:
Step 1 - Applied by University B
Step 3 - Applied by University B
Step 4 - Applied by University B


結論

テンプレートメソッドは、タスクをクラス間で分散させ、プロセスを再定義し、コードを削減する効果的な方法です。

このデザインパターンをアルゴリズムやソリューションに適用することで、冗長なメソッドを回避し、長い実行プロセスを合理化することができます。

また、テンプレートメソッドは、OOPパラダイムを適切に利用するための例でもあります。

このモデルはすべてのケースに適用できるわけではないので、使用する前にプロジェクトのニーズを理解しておいてください。

タイトルとURLをコピーしました