Pythonリファクタリング ― 飲食店注文システム:実例
By JoeVu, at: 2024年11月15日13:07
Estimated Reading Time: __READING_TIME__ minutes


Pythonリファクタリング - 実際のシナリオ:レストラン注文システム
1. はじめに
複雑さが増していくにつれて、入れ子になったif-else文で埋め尽くされた、散らかったコードは実世界のアプリケーションに忍び寄ることがよくあります。時間が経つにつれて、これによりシステムの保守、デバッグ、スケーリングが困難になります。この投稿では、レストランの注文システムというシナリオに取り組み、オブジェクト指向設計を使用してそれをリファクタリングすることで、ロジックを簡素化し、スケーラビリティを向上させる方法を示します。
2. 問題点
私たちのレストラン注文システムは以下を処理します。
- 顧客の種類(VIP、初回など)に基づく割引。
- デリバリー、店内飲食、テイクアウトの注文に対する追加料金。
- 場所によって異なる税金。
元の散らかった実装を以下に示します。
class Order:
def __init__(self, order_type, customer_type, base_price, location=None):
self.order_type = order_type
self.customer_type = customer_type
self.base_price = base_price
self.location = location
def calculate_total(self):
total = self.base_price
# 顧客の種類に基づく割引
if self.customer_type == "VIP":
total *= 0.9 # 10%割引
elif self.customer_type == "first-time":
total *= 0.85 # 15%割引
# 注文の種類に基づく追加料金
if self.order_type == "delivery":
if self.location == "urban":
total += 5
elif self.location == "rural":
total += 10
elif self.order_type == "dine-in":
total += 2
# 税金
if self.location in ["urban", "rural"]:
total *= 1.05
elif self.order_type == "takeout":
total *= 1.02
# 追加条件
if self.customer_type == "VIP" and self.order_type == "dine-in":
total -= 5 # VIPは店内飲食で5ドル割引
return total
order = Order(order_type="delivery", customer_type="VIP", base_price=100, location="rural")
print(f"Total Order Price: ${order.calculate_total():.2f}")
このコードが悪い理由:
-
深く入れ子になったロジック:
- 複数のレベルの
if-else
文により、コードの読み取りと追跡が困難になります。
- ロジックがメソッド全体に散らばっているため、コードが何をしているかを一目で理解することが困難です。
- 複数のレベルの
-
低いスケーラビリティ:
- 新しい顧客の種類、割引、または注文の種類を追加するには、既に複雑なこのメソッドを変更する必要があります。
- このアプローチは、オープン・クローズド原則(コードは拡張に対して開かれているべきだが、変更に対しては閉じられているべきである)に違反しています。
- 新しい顧客の種類、割引、または注文の種類を追加するには、既に複雑なこのメソッドを変更する必要があります。
-
単一責任の原則の違反:
calculate_total
メソッドは、割引、追加料金、税金を1つの場所で処理するため、多くのことを行う「ゴッドメソッド」になります。
- ロジックの一部の変更は、メソッドの関連のない部分を壊すリスクがあります。
-
テストが困難:
- この関数をテストするには、顧客の種類、注文の種類、場所の幅広い条件を網羅する必要があります。
- 条件ブロックの1つのバグは、メソッド全体にカスケードする可能性があります。
- この関数をテストするには、顧客の種類、注文の種類、場所の幅広い条件を網羅する必要があります。
-
保守性の低さ:
- ビジネスルールが変更された場合(例:新しい割引または税率)、コードの関連する部分を注意深く見つけて更新する必要があります。
- このような変更中にエラーが発生するリスクは高くなります。
- ビジネスルールが変更された場合(例:新しい割引または税率)、コードの関連する部分を注意深く見つけて更新する必要があります。
-
強い結合:
- すべてのビジネスロジックは
calculate_total
メソッドに強く結合されています。
- レポートツールやその他のサービスなど、他の場所で再利用することはできません。
- すべてのビジネスロジックは
3. リファクタリング
元のインプリメンテーションで特定された問題に対処するために、戦略パターンとオブジェクト指向設計を使用してシステムをリファクタリングします。これらの改善は、モジュール性、カプセル化、懸念事項の分離などの重要な原則に従って、ロジックを再利用可能なクラスに分割することに重点を置いています。
リファクタリングにおける主な改善点
1. 割引のための戦略クラスの導入
- 割引ロジックは、顧客の種類に基づいて個別のクラス(例:
VIPDiscount
、FirstTimeDiscount
)に抽出されます。
- これにより、
Order
クラス内の入れ子になったif-else
条件の必要性がなくなります。
- 改善点:各割引戦略は、その特定の動作をカプセル化するため、再利用可能で拡張が容易になります。
2. 注文の種類のための戦略クラスの作成
delivery
、dine-in
、takeout
の追加料金計算は、独自の戦略クラス(例:DeliveryOrder
、DineInOrder
)に移動されます。
- これらのクラスは、メインの
Order
クラスを混乱させることなく、場所ベースのロジック(例:都市部または地方部の配達料金)を処理します。
- 改善点:「CateringOrder」などの新しい注文の種類を追加するには、既存のコードを変更することなく、新しい戦略クラスを作成するだけです。
3. コアOrder
クラスの簡素化
Order
クラスは、軽量なオーケストレータとしての役割を果たすようになりました。- 割引計算を
DiscountStrategy
に委任します。
- 追加料金計算を
OrderTypeStrategy
に委任します。
- これらの戦略の結果に基づいて税金計算を処理します。
- 割引計算を
- 改善点:
Order
クラスは、クリーンで、焦点を絞っており、単一責任の原則に準拠しています。
4. 継承よりも合成を使用する
- ビジネスロジックを単一クラスにハードコーディングする代わりに、リファクタリングでは合成を使用して、戦略を動的に組み合わせます。
- 改善点:これにより、システムは柔軟になり、さまざまなシナリオに合わせてカスタマイズしやすくなります。
リファクタリングされたコード
class DiscountStrategy:
"""割引戦略の基底クラス"""
def apply_discount(self, order):
return order.base_price
class VIPDiscount(DiscountStrategy):
def apply_discount(self, order):
return order.base_price * 0.9 # VIPの場合10%割引
class FirstTimeDiscount(DiscountStrategy):
def apply_discount(self, order):
return order.base_price * 0.85 # 初回のお客様の場合15%割引
class NoDiscount(DiscountStrategy):
def apply_discount(self, order):
return order.base_price
class OrderTypeStrategy:
"""注文の種類の戦略の基底クラス"""
def apply_surcharge(self, order):
return 0
class DeliveryOrder(OrderTypeStrategy):
def apply_surcharge(self, order):
return 5 if order.location == "urban" else 10 # 配達料金は場所によって異なる
class DineInOrder(OrderTypeStrategy):
def apply_surcharge(self, order):
return 2 # 店内飲食注文のサービス料金
class TakeoutOrder(OrderTypeStrategy):
def apply_surcharge(self, order):
return 0 # テイクアウトには追加料金なし
class Order:
def __init__(self, base_price, discount_strategy, order_type_strategy, location=None):
self.base_price = base_price
self.discount_strategy = discount_strategy
self.order_type_strategy = order_type_strategy
self.location = location
def calculate_total(self):
# 割引と追加料金の計算を委任する
discounted_price = self.discount_strategy.apply_discount(self)
surcharge = self.order_type_strategy.apply_surcharge(self)
# 税金のロジック
tax_rate = 0.05 if self.location in ["urban", "rural"] else 0.02
tax = discounted_price * tax_rate
# 合計計算
return discounted_price + surcharge + tax
4. なぜこれが優れているのか
リファクタリングされたコードは、いくつかの利点を提供します。
- 可読性:各責任は、そのクラスで明確に定義されています。
- スケーラビリティ:新しい割引や注文の種類を追加するのは簡単です。新しいクラスを作成するだけです。
- テスト容易性:個々の戦略は独立してテストできます。
- 保守性:ロジックの一部の変更は、他の部分に影響を与えません。
5. まとめ
レストランの注文システムをリファクタリングすることにより、散らかった入れ子になったif-elseブロックを、モジュール化されたオブジェクト指向設計に変換しました。このアプローチにより、コードはクリーンで、スケーラブルで、保守が容易になります。
システムが複雑さに溺れている場合は、同様の設計原則を採用して、システムを簡素化し、将来に備えましょう!