1. ゴッドクラス(God Class)
一つのクラスがあらゆる処理を担う「何でも屋」になる状態。行数が数千行に膨らみ、単体テストが難しくなり、変更の影響が予測できなくなる。
❌ NGclass OrderManager:
def create_order(self, items): ... # ドメインロジック
def validate_items(self, items): ... # バリデーション
def calculate_tax(self, amount): ... # 税計算
def send_confirmation_email(self): ... # メール送信(外部IO)
def save_to_database(self, order): ... # DB永続化
def generate_pdf_receipt(self): ... # PDFレポート生成
def update_inventory(self, items): ... # 在庫管理
def apply_coupon(self, code): ... # クーポン処理
def log_order_event(self, event): ... # ログ記録
# ...他100メソッド
class OrderService: # 注文ユースケースの調整役
def __init__(self, repo, notifier, tax_calc):
self.repo = repo
self.notifier = notifier
self.tax_calc = tax_calc
def place_order(self, items, coupon=None):
order = Order.create(items)
order.apply_coupon(coupon)
order.total_tax = self.tax_calc.calculate(order)
self.repo.save(order)
self.notifier.send_confirmation(order)
return order
class TaxCalculator: ... # 税計算に専念
class OrderRepository: ... # DB操作に専念
class OrderNotifier: ... # 通知に専念
💡 単一責任の原則(SRP)
クラスを変更する理由は一つだけにする。変更の理由が「税法改正」「メール仕様変更」「DB変更」と複数あるなら、それだけクラスを分ける根拠になる。
2. 散弾銃手術(Shotgun Surgery)
一つの仕様変更を加えると、関係のない複数のクラスに修正が必要になる状態。ゴッドクラスとは逆に「一つの責務が複数クラスに散らばっている」ことが原因。
❌ NG# 消費税率が変わったら 4 ヶ所修正が必要
class CartService:
TAX_RATE = 0.10 # ← ここと
class InvoiceService:
TAX_RATE = 0.10 # ← ここと
class ReceiptPrinter:
TAX_RATE = 0.10 # ← ここと
class TaxReport:
TAX_RATE = 0.10 # ← ここを変える
# tax_config.py(唯一の変更箇所)
TAX_RATE = 0.10
# 各クラスはインポートして参照するだけ
from tax_config import TAX_RATE
class CartService:
def subtotal_with_tax(self, price): return price * (1 + TAX_RATE)
class InvoiceService:
def invoice_total(self, price): return price * (1 + TAX_RATE)
3. 特性の横恋慕(Feature Envy)
あるメソッドが、自分のクラスよりも別クラスのデータやメソッドを多用している状態。そのメソッドは本来「移住すべき場所」が別にある合図だ。
❌ NGclass ReportService:
def generate_summary(self, order):
# Order のデータばかり触っている → Order に移すべき
total = sum(item.price * item.qty for item in order.items)
discount = order.coupon.discount if order.coupon else 0
tax = (total - discount) * order.tax_rate
return {"subtotal": total, "discount": discount, "tax": tax}
class Order:
def summary(self): # データを持つクラス自身がロジックを持つ
total = sum(item.price * item.qty for item in self.items)
discount = self.coupon.discount if self.coupon else 0
tax = (total - discount) * self.tax_rate
return {"subtotal": total, "discount": discount, "tax": tax}
class ReportService:
def generate_summary(self, order):
return order.summary() # 委譲するだけ
4. マジックナンバー・マジック文字列
コード中に意味のない数値・文字列リテラルが直接埋め込まれている状態。「3」が何を意味するか、後から読む人には分からない。また値の変更が漏れやすくなる。
def get_discount(user):
if user.age < 18: # なぜ 18?
return 0.2 # 20% 割引?
if user.tier == 3: # 3 は何のティア?
return 0.15
if user.status == "gold": # "gold" は何を表す?
return 0.1
return 0
from enum import IntEnum
MINOR_AGE_THRESHOLD = 18
MINOR_DISCOUNT_RATE = 0.20
PREMIUM_DISCOUNT_RATE = 0.15
GOLD_DISCOUNT_RATE = 0.10
class UserTier(IntEnum):
STANDARD = 1
PREMIUM = 2
ELITE = 3
class UserStatus:
GOLD = "gold"
SILVER = "silver"
def get_discount(user):
if user.age < MINOR_AGE_THRESHOLD:
return MINOR_DISCOUNT_RATE
if user.tier == UserTier.ELITE:
return PREMIUM_DISCOUNT_RATE
if user.status == UserStatus.GOLD:
return GOLD_DISCOUNT_RATE
return 0
5. 深すぎる継承
継承の階層が 3 層を超えると、親クラスの変更がどこに影響するか追いにくくなる(脆弱な基底クラス問題)。多くの場合、継承よりコンポジション(has-a) が保守しやすい。
❌ NGclass Entity: pass # 第1層
class Person(Entity): pass # 第2層
class User(Person): pass # 第3層
class AdminUser(User): pass # 第4層
class SuperAdmin(AdminUser): pass # 第5層 — 変更が辛い
class User:
def __init__(self, profile, permissions):
self.profile = profile # コンポジション
self.permissions = permissions # コンポジション
# 権限を組み合わせて「管理者ユーザー」を作る
admin = User(
profile=UserProfile(name="田中"),
permissions=PermissionSet([Permission.READ, Permission.WRITE, Permission.ADMIN])
)
6. 命名のアンチパターン
6.1 意味のない名前
❌ NGdef calc(x, y, z):
tmp = x * y
ret = tmp - z
return ret
def calculate_discounted_price(unit_price, quantity, discount_amount):
subtotal = unit_price * quantity
final_price = subtotal - discount_amount
return final_price
6.2 誤解を招くブール名・関数名
| ❌ NG | ✅ OK | 理由 |
|---|---|---|
is_not_invalid | is_valid | 二重否定は混乱を招く |
check_user() | is_user_active() | 戻り値の型が読めない |
data, info | user_profile, order_summary | 何のデータかが不明 |
process() | validate_and_save_order() | 何を処理するか不明 |
flag, flg | has_paid, is_cancelled | 意味のないフラグ名 |
⚠️ 「後でリファクタリングすれば OK」は幻想
意味不明な名前は書いた直後なら本人にしか分からない。1ヶ月後は本人も忘れる。名前を付ける時間のケチりが、デバッグで何倍もの時間を奪う。