Java から Python へ翻訳しよう
『独習〜』は Java で書いてあります。
Java は Java なのでそのまま Python にコードを書き換えるのは難しい部分が私にはあります。
書き換えるのが難しいと思ったところで、『あの言語でできることがこの言語ではできない』
ということは、難易度の差こそあれそんな場面はあんまりないと聞きます。
聞く機会がなかっただけかもしれんけど。
「デザインパターンってなんぞ?」というのを飲み込んでいこうとしていますが、月刊ペースだと先が思いやられるのでテンポ良く進みたいですね。
今回は Abstract Factory パターンをPythonに書き直していきます。
Abstract Factory とは
具象クラスを明確にせず、関連するオブジェクト群を生成するパターン。
OS毎の違いを吸収するのに使ったりするみたい。
アブストラクトファクトリを適用しない場合
# ! /usr/bin/env python3
from enum import Enum, auto
from typing import Union
class GuitarType(Enum):
Telecaster: int = auto()
LesPaul: int = auto()
# Telecaster の材をクラス化する
class TelecasterNeck:
def feature(self) -> str:
return "Maple, C Shape"
class TelecasterFingerboard:
def feature(self) -> str:
return "Maple, 7.25\" R"
class TelecasterBody:
def feature(self) -> str:
return "Swamp Ash"
# Les Paul の材をクラス化する
class LesPaulNeck:
def feature(self) -> str:
return "Mahogany, Slim taper shape"
class LesPaulFingerboard:
def feature(self) -> str:
return "Rosewood 12\" R"
class LesPaulBody:
def feature(self) -> str:
return "Mahogany and Maple"
class GuitarFactory:
"""Factory クラスから材を呼び出せるようにする"""
# Telecaster の材を呼び出す
def choice_telecaster_neck(self) -> TelecasterNeck:
return TelecasterNeck()
def choice_telecaster_fingerboard(self) -> TelecasterFingerboard:
return TelecasterFingerboard()
def choice_telecaster_body(self) -> TelecasterBody:
return TelecasterBody()
# Les Paul の材を呼び出す
def choice_les_paul_neck(self) -> LesPaulNeck:
return LesPaulNeck()
def choice_les_paul_fingerboard(self) -> LesPaulFingerboard:
return LesPaulFingerboard()
def choice_les_paul_body(self) -> LesPaulBody:
return LesPaulBody()
class Order:
"""Client 側のクラス"""
def __init__(self, type: GuitarType) -> None:
self.type: Enum = type
def create_guitar(self) -> str:
"""
オーダーに合わせて Fender か Gibson タイプを組み立てる
Returns:
str: 完成品の構成材
"""
neck: Union[TelecasterNeck, LesPaulNeck]
fingerboard: Union[TelecasterFingerboard, LesPaulFingerboard]
body: Union[TelecasterBody, LesPaulBody]
factory: GuitarFactory = GuitarFactory()
if self.type == GuitarType.Telecaster:
neck = factory.choice_telecaster_neck()
fingerboard = factory.choice_telecaster_fingerboard()
body = factory.choice_telecaster_body()
if self.type == GuitarType.LesPaul:
neck = factory.choice_les_paul_neck()
fingerboard = factory.choice_les_paul_fingerboard()
body = factory.choice_les_paul_body()
# return で早めに返したいが、共通部分を括りたい気持ちも強い
use_neck: str = neck.feature()
use_fingerboard: str = fingerboard.feature()
use_body: str = body.feature()
return (f"{self.type.name} のネックは {use_neck}、" +
f"フィンガーボードは {use_fingerboard}、" + f"ボディは {use_body} でできています。")
if __name__ == '__main__':
order = Order(GuitarType.Telecaster)
print(order.create_guitar())
Order.create_guitar() の長さをどうにかしたいですね。
それはさておき、このコードでは Stratcaster や SG、PRS などを追加してくれと言われると GuitarFactory クラスがどんどん肥大化していまいます。
各ギターの構成要素は決まっているため、新規商品を追加することを考えて種類を簡単に増やす仕組みに実装したいと考えます。
アブストラクトファクトリを適用した場合
# ! /usr/bin/env python3
from enum import Enum, auto
from typing import Union
class GuitarType(Enum):
Telecaster: int = auto()
LesPaul: int = auto()
class Neck:
def feature(self) -> str:
pass
class Fingerboard:
def feature(self) -> str:
pass
class Body:
def feature(self) -> str:
pass
# Telecaster の材をクラス化する
class TelecasterNeck(Neck):
def feature(self) -> str:
return "Maple, C Shape"
class TelecasterFingerboard(Fingerboard):
def feature(self) -> str:
return "Maple, 7.25\" R"
class TelecasterBody(Body):
def feature(self) -> str:
return "Swamp Ash"
# Les Paul の材をクラス化する
class LesPaulNeck(Neck):
def feature(self) -> str:
return "Mahogany, Slim taper shape"
class LesPaulFingerboard(Fingerboard):
def feature(self) -> str:
return "Rosewood 12\" R"
class LesPaulBody(Body):
def feature(self) -> str:
return "Mahogany and Maple"
class GuitarFactory:
def choice_neck(self) -> Union[TelecasterNeck, LesPaulNeck]:
pass
def choice_fingerboard(
self) -> Union[TelecasterFingerboard, LesPaulFingerboard]:
pass
def choice_body(self) -> Union[TelecasterBody, LesPaulBody]:
pass
class TelecasterFactory(GuitarFactory):
def choice_neck(self) -> TelecasterNeck:
return TelecasterNeck()
def choice_fingerboard(self) -> TelecasterFingerboard:
return TelecasterFingerboard()
def choice_body(self) -> TelecasterBody:
return TelecasterBody()
class LesPaulFactory(GuitarFactory):
def choice_neck(self) -> LesPaulNeck:
return LesPaulNeck()
def choice_fingerboard(self) -> LesPaulFingerboard:
return LesPaulFingerboard()
def choice_body(self) -> LesPaulBody:
return LesPaulBody()
class Order(GuitarFactory):
def __init__(self, factory):
factory: Union[TelecasterFactory, LesPaulFactory]
if factory == GuitarType.Telecaster:
factory = TelecasterFactory()
elif factory == GuitarType.LesPaul:
factory = LesPaulFactory()
self.neck: Union[TelecasterNeck, LesPaulNeck]
self.fingerboard: Union[TelecasterFingerboard, LesPaulFingerboard]
self.body: Union[TelecasterBody, LesPaulBody]
self.neck = factory.choice_neck()
self.fingerboard = factory.choice_fingerboard()
self.body = factory.choice_body()
def make_guitar(self):
return self.neck, self.fingerboard, self.body
if __name__ == '__main__':
# ギターの種類を Order クラスの責任で指定
guitar: GuitarType = GuitarType.Telecaster
order: Order = Order(guitar)
neck: str = order.neck.feature()
fingerboard: str = order.fingerboard.feature()
body: str = order.body.feature()
print(f"{guitar.name}のネックは{neck}、" +
f"フィンガーボードは{fingerboard}、" +
f"ボディは{body}でできています。")
アブストラクトファクトリを適用すると、 GuitarFactoryクラスとその部品を抽象化でき、クライアント側からは具体的な処理が隠蔽されます。
そのため、クライアント側はどのようなものでも同じような対応ができるという利点があります。
また、部品をグループ化して生成処理をまとめることができるため、同時に使用するべきではない部品を組み合わせて使うことが少なくなります。
アブストラクトファクトリの弱点
ただし、要素を追加することが困難。
今回の場合はギターのネック・指板・ボディの3種類の要素で構成されています。
たとえばここにピックアップやペグを追加しようとすると、 GuitarFactory クラスに PickUps や Pegs を追加しなければいけません。
そして、そのサブクラスにも上記の修正を加えていかなければなりません。
以上の理由で、新たな要素を追加すると非常に面倒なため、このパターンを適用するときは元から要素が少ない、またはほとんど増えない見込みのときに適用する方が無難なようで。