Pythonでデザインパターンを学ぶ(Adapterパターン)
こんにちは。深緑です。
チーム内で改めてデザインパターンを学び始めました。
記録のために記事に残しておこうと思います。
はじめに
GoFのデザインパターンを一つずつ学んでいきます。
今回はAdapterパターンです。
Wikipedia - Adapter パターン
言語はPythonを使用します。
サンプルコードのシチュエーション
学校において、現在生徒を特定するコードを和暦+学部+学科+数字4桁で管理しています。
令和5年度から西暦+学部+学科+数字4桁で管理するとします。
年度 | コードのフォーマット | 例 |
---|---|---|
令和4年度まで | 和暦+学部+学科+数字4桁 | 04TEIT0001 |
令和5年度以降 | 西暦+学部+学科+数字4桁 | 2023TEIT0001 |
サンプルコードにおいては、既存クラスには最後の数字を和暦+学部+学科ごとに自動採番するロジックがあり、
これを作り直すとなるとそれなりに手間がかかるとします。
サンプルコード
Adapterパターンには、「継承を利用したAdapter」と「委譲を利用したAdapter」があります。
今回は、「継承を利用したAdapter」で実装してみます。
Adapterパターン適用前
class Student:
"""
Adapterパターン 生徒クラス
"""
year: int = 0
department: str
course: str
num: int = 0
def __init__(self, year: int, department: str, course: str):
"""コンストラクタ
Args:
year (int): 年度
department (str): 学部
course (str): 学科
"""
self.year = year
self.department = department
self.course = course
def code(self) -> str:
"""生徒コードを返す
Returns:
str: 生徒コード
"""
# まだ連番が振られてない場合、年度・学部・学科ごとの連番を自動採番する
# このソースでは長くなるので省略
return str(self.year).zfill(2) \
+ self.department \
+ self.course \
+ str(self.num).zfill(4)
Adapterパターン適用後
from abc import ABCMeta, abstractmethod
class SeirekiInterface(metaclass=ABCMeta):
"""
Adapterパターン 西暦インターフェース
"""
@abstractmethod
def seireki_cd(self) -> str:
"""生徒コード(西暦版)を取得する
Returns:
str: 生徒コード(西暦版)
"""
from student import Student
from seireki_interface import SeirekiInterface
class SeirekiStudent(Student, SeirekiInterface):
"""
Adapterパターン 生徒(西暦Ver)クラス
"""
def seireki_cd(self) -> str:
"""生徒コード(西暦版)を取得する
Returns:
str: 生徒コード(西暦版)
"""
code: str = self.code()
# コードが12桁以上なら西暦+4桁の番号になってるのでそのまま返却
if (len(code) >= 12):
return code
year: int = int(code[0:2])
# 令和元年〜10年の場合
# 令和5年以降は年そのものに西暦が入力されていく予定
if (year <= 10):
year = year + 2018
# 平成元年〜31年の場合
if (year <= 31):
year = year + 1988
return str(year).zfill(2) \
+ self.department \
+ self.course \
+ str(self.num).zfill(4)
解説
この例では、Studentクラスが和暦ベースでコードを生成しています。
学校の業務全体で各種コードが西暦ベースのコードに変わったら、
今のStudentクラスでは対応できないので新しいルールのコードを返すメソッドを追加しています。
新しいメソッドは、あえて既存の関数の戻り値を加工して新しいルールに合わせています。
既存のStudentクラスは自動採番などあり、それなりに変えるのは面倒だと思ってください。
インターフェースは、仮に西暦ベースのコードのルールをStudentクラス以外にも適用する場合、
西暦ベースのコードを返す関数を作るのを強要するのに役立ちます。
このように、インターフェースが変わってしまった場合に、既存クラスに修正を加えることなくインターフェースを合わせるための実装方法がAdapterパターンです。
実務ではAPIのバージョン変更などで使うことがあります。
補足
実務では新しい関数を追加すると言うよりも、
既存の関数をoldにしてから、新たなルールの関数を同名で追加し直す方が多いかもしれませんね。
その他、誤りや解釈違いがあれば随時修正します。
Discussion