プログラミングの「神クラス」を作らないための指針
神クラス(God Class)を避けるための設計指針を解説。単一責任の原則、クラス分割、リファクタリング手法など、保守性の高いコード設計を詳しく紹介します
みなさん、プログラミングで「何でもできる巨大なクラス」を作ってしまった経験はありませんか?
コードを書き続けていると、一つのクラスに機能を追加し続けて、気がつくと何百行もの巨大なクラスになってしまうことがありますよね。
これが「神クラス(God Class)」と呼ばれる問題のあるコード設計です。 この記事では、神クラスを作らないための指針と、既存の神クラスを改善する方法を詳しく解説します。
神クラスとは?
基本的な概念
神クラスとは、一つのクラスが多すぎる責任を持ち、多くの機能を詰め込んだ巨大なクラスのことです。
簡単に言うと、「何でもできるけれど、複雑で理解しにくいクラス」です。
神クラスの特徴
典型的な特徴
- 数百から数千行のコード
- 多数のメソッドとプロパティ
- 複数の異なる責任を持つ
- 多くのクラスから依存される
- テストが困難
- 変更時の影響範囲が広い
神クラスの例
悪い例:何でもできるUserクラス
# 悪い例:神クラスになってしまったUserクラスclass User: def __init__(self, user_id, username, email): self.user_id = user_id self.username = username self.email = email self.password_hash = None self.profile = {} self.posts = [] self.friends = [] self.notifications = [] self.settings = {} self.payment_info = {} self.subscription = None # 認証関連 def set_password(self, password): self.password_hash = self.hash_password(password) def verify_password(self, password): return self.check_password_hash(self.password_hash, password) def generate_auth_token(self): # JWT トークン生成 pass def validate_auth_token(self, token): # トークン検証 pass # プロファイル関連 def update_profile(self, profile_data): self.profile.update(profile_data) def get_profile_picture_url(self): return self.profile.get('picture_url') def update_profile_picture(self, image_file): # 画像アップロード処理 pass # 投稿関連 def create_post(self, content, images=None): post = { 'id': self.generate_post_id(), 'content': content, 'images': images or [], 'created_at': datetime.now(), 'likes': 0, 'comments': [] } self.posts.append(post) return post def delete_post(self, post_id): self.posts = [p for p in self.posts if p['id'] != post_id] def like_post(self, post_id): # いいね処理 pass # フレンド関連 def send_friend_request(self, target_user): # フレンドリクエスト送信 pass def accept_friend_request(self, from_user): self.friends.append(from_user) def get_mutual_friends(self, other_user): # 共通の友達を取得 pass # 通知関連 def send_notification(self, message, notification_type): notification = { 'message': message, 'type': notification_type, 'created_at': datetime.now(), 'read': False } self.notifications.append(notification) def mark_notification_read(self, notification_id): # 通知を既読にする pass def get_unread_notifications(self): return [n for n in self.notifications if not n['read']] # 設定関連 def update_settings(self, settings_data): self.settings.update(settings_data) def get_privacy_settings(self): return self.settings.get('privacy', {}) def get_notification_settings(self): return self.settings.get('notifications', {}) # 決済関連 def add_payment_method(self, payment_data): self.payment_info['methods'] = self.payment_info.get('methods', []) self.payment_info['methods'].append(payment_data) def process_payment(self, amount, payment_method_id): # 決済処理 pass def get_payment_history(self): # 決済履歴取得 pass # サブスクリプション関連 def subscribe_to_premium(self, plan): self.subscription = { 'plan': plan, 'started_at': datetime.now(), 'status': 'active' } def cancel_subscription(self): if self.subscription: self.subscription['status'] = 'cancelled' # メール関連 def send_welcome_email(self): # ウェルカムメール送信 pass def send_password_reset_email(self): # パスワードリセットメール送信 pass # データベース関連 def save_to_database(self): # データベース保存 pass def load_from_database(self, user_id): # データベースから読み込み pass # バリデーション関連 def validate_email(self, email): # メールアドレス検証 pass def validate_username(self, username): # ユーザー名検証 pass # ログ関連 def log_activity(self, activity): # アクティビティログ pass def get_activity_log(self): # アクティビティ履歴取得 pass # ... さらに多くのメソッドが続く
この例では、一つのUserクラスが以下のような多すぎる責任を持っています:
- ユーザー認証
- プロファイル管理
- 投稿機能
- フレンド機能
- 通知機能
- 設定管理
- 決済処理
- サブスクリプション管理
- メール送信
- データベース操作
- バリデーション
- ログ管理
神クラスの問題点
保守性の問題
変更が困難
- 一つの変更が他の機能に影響する
- テストが複雑になる
- バグの原因特定が困難
- コードレビューが大変
理解しやすさの問題
認知負荷が高い
- クラス全体を理解するのに時間がかかる
- どのメソッドがどの責任に属するか分からない
- 新しいチームメンバーが理解しにくい
再利用性の問題
部分的な利用が困難
- 必要のない機能も一緒に持ち込まれる
- 他のプロジェクトでの再利用が困難
- 依存関係が複雑になる
パフォーマンスの問題
リソース使用量が大きい
- 不必要な機能もメモリに読み込まれる
- 初期化時間が長い
- テスト実行時間が長い
神クラスを避ける設計原則
単一責任の原則(SRP)
責任の明確化
一つのクラスは一つの責任
# 良い例:責任を分離したクラス設計
class User: """ユーザーの基本情報のみを管理""" def __init__(self, user_id, username, email): self.user_id = user_id self.username = username self.email = email self.created_at = datetime.now() def update_email(self, new_email): if self.validate_email(new_email): self.email = new_email else: raise ValueError("Invalid email format") def validate_email(self, email): # メールアドレスの形式チェック import re pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' return re.match(pattern, email) is not None
class UserAuthentication: """ユーザー認証に特化""" def __init__(self, user): self.user = user self.password_hash = None self.auth_tokens = [] def set_password(self, password): if self.validate_password_strength(password): self.password_hash = self.hash_password(password) else: raise ValueError("Password does not meet requirements") def verify_password(self, password): return self.check_password_hash(self.password_hash, password) def generate_auth_token(self): token = self.create_jwt_token(self.user.user_id) self.auth_tokens.append(token) return token def validate_auth_token(self, token): return token in self.auth_tokens and self.verify_jwt_token(token)
class UserProfile: """ユーザープロファイル管理に特化""" def __init__(self, user): self.user = user self.profile_data = {} self.profile_picture_url = None def update_profile(self, profile_data): allowed_fields = ['first_name', 'last_name', 'bio', 'location'] for field, value in profile_data.items(): if field in allowed_fields: self.profile_data[field] = value def upload_profile_picture(self, image_file): # 画像アップロード処理 uploaded_url = self.upload_image_to_storage(image_file) self.profile_picture_url = uploaded_url return uploaded_url def get_public_profile(self): return { 'username': self.user.username, 'profile_picture': self.profile_picture_url, 'bio': self.profile_data.get('bio', ''), 'location': self.profile_data.get('location', '') }
class PostManager: """投稿管理に特化""" def __init__(self, user): self.user = user self.posts = [] def create_post(self, content, images=None): if not content.strip(): raise ValueError("Post content cannot be empty") post = Post( user_id=self.user.user_id, content=content, images=images or [] ) self.posts.append(post) return post def delete_post(self, post_id): post = self.find_post_by_id(post_id) if post and post.user_id == self.user.user_id: self.posts.remove(post) return True return False def get_user_posts(self, limit=10): return sorted(self.posts, key=lambda p: p.created_at, reverse=True)[:limit]
class Post: """投稿データの表現""" def __init__(self, user_id, content, images=None): self.id = self.generate_id() self.user_id = user_id self.content = content self.images = images or [] self.created_at = datetime.now() self.likes_count = 0 self.comments = [] def add_like(self): self.likes_count += 1 def remove_like(self): self.likes_count = max(0, self.likes_count - 1)
開放閉鎖の原則(OCP)
拡張に開放、変更に閉鎖
# 良い例:拡張可能な設計from abc import ABC, abstractmethod
class NotificationSender(ABC): """通知送信の抽象基底クラス""" @abstractmethod def send(self, user, message, notification_type): pass
class EmailNotificationSender(NotificationSender): """メール通知の実装""" def send(self, user, message, notification_type): email_service = EmailService() email_service.send_email( to=user.email, subject=f"Notification: {notification_type}", body=message )
class PushNotificationSender(NotificationSender): """プッシュ通知の実装""" def send(self, user, message, notification_type): push_service = PushNotificationService() push_service.send_push( user_id=user.user_id, title=notification_type, body=message )
class SMSNotificationSender(NotificationSender): """SMS通知の実装""" def send(self, user, message, notification_type): sms_service = SMSService() sms_service.send_sms( phone_number=user.phone_number, message=f"{notification_type}: {message}" )
class NotificationManager: """通知管理クラス""" def __init__(self): self.senders = {} def register_sender(self, notification_type, sender): """新しい通知方法を追加(拡張に開放)""" self.senders[notification_type] = sender def send_notification(self, user, message, notification_type): """通知送信(変更に閉鎖)""" sender = self.senders.get(notification_type) if sender: sender.send(user, message, notification_type) else: raise ValueError(f"Unsupported notification type: {notification_type}")
# 使用例notification_manager = NotificationManager()notification_manager.register_sender('email', EmailNotificationSender())notification_manager.register_sender('push', PushNotificationSender())notification_manager.register_sender('sms', SMSNotificationSender())
# 新しい通知方法の追加(既存コードを変更せずに拡張)class SlackNotificationSender(NotificationSender): def send(self, user, message, notification_type): slack_service = SlackService() slack_service.send_message( channel=user.slack_channel, text=f"{notification_type}: {message}" )
notification_manager.register_sender('slack', SlackNotificationSender())
依存関係逆転の原則(DIP)
抽象に依存し、具象に依存しない
# 良い例:依存関係逆転を適用した設計from abc import ABC, abstractmethod
class UserRepository(ABC): """ユーザーデータアクセスの抽象化""" @abstractmethod def save(self, user): pass @abstractmethod def find_by_id(self, user_id): pass @abstractmethod def find_by_email(self, email): pass
class DatabaseUserRepository(UserRepository): """データベース実装""" def __init__(self, database): self.database = database def save(self, user): query = "INSERT INTO users (id, username, email) VALUES (?, ?, ?)" self.database.execute(query, (user.user_id, user.username, user.email)) def find_by_id(self, user_id): query = "SELECT * FROM users WHERE id = ?" result = self.database.fetch_one(query, (user_id,)) return self.map_to_user(result) if result else None def find_by_email(self, email): query = "SELECT * FROM users WHERE email = ?" result = self.database.fetch_one(query, (email,)) return self.map_to_user(result) if result else None
class InMemoryUserRepository(UserRepository): """メモリ実装(テスト用)""" def __init__(self): self.users = {} def save(self, user): self.users[user.user_id] = user def find_by_id(self, user_id): return self.users.get(user_id) def find_by_email(self, email): for user in self.users.values(): if user.email == email: return user return None
class UserService: """ユーザービジネスロジック(抽象に依存)""" def __init__(self, user_repository: UserRepository): self.user_repository = user_repository # 具象ではなく抽象に依存 def create_user(self, username, email): # 重複チェック existing_user = self.user_repository.find_by_email(email) if existing_user: raise ValueError("Email already exists") # ユーザー作成 user = User( user_id=self.generate_user_id(), username=username, email=email ) # 保存 self.user_repository.save(user) return user def get_user_by_email(self, email): return self.user_repository.find_by_email(email)
# 使用例:依存注入により柔軟な構成が可能database = DatabaseConnection()db_repository = DatabaseUserRepository(database)user_service = UserService(db_repository) # 本番環境
# テスト時は異なる実装を注入test_repository = InMemoryUserRepository()test_user_service = UserService(test_repository) # テスト環境
神クラスの検出方法
メトリクスによる検出
定量的な指標
# クラス分析ツールの例class ClassAnalyzer: def __init__(self): self.thresholds = { 'max_lines': 200, 'max_methods': 20, 'max_fields': 15, 'max_cyclomatic_complexity': 10, 'max_dependencies': 10 } def analyze_class(self, class_definition): """クラスの分析実行""" metrics = self.calculate_metrics(class_definition) issues = self.identify_issues(metrics) recommendations = self.generate_recommendations(metrics, issues) return { 'metrics': metrics, 'issues': issues, 'recommendations': recommendations, 'god_class_score': self.calculate_god_class_score(metrics) } def calculate_metrics(self, class_def): """メトリクスの計算""" return { 'lines_of_code': self.count_lines_of_code(class_def), 'number_of_methods': self.count_methods(class_def), 'number_of_fields': self.count_fields(class_def), 'cyclomatic_complexity': self.calculate_complexity(class_def), 'number_of_dependencies': self.count_dependencies(class_def), 'cohesion': self.calculate_cohesion(class_def), 'coupling': self.calculate_coupling(class_def) } def identify_issues(self, metrics): """問題の特定""" issues = [] if metrics['lines_of_code'] > self.thresholds['max_lines']: issues.append({ 'type': 'excessive_size', 'severity': 'high', 'description': f"クラスが大きすぎます ({metrics['lines_of_code']} 行)", 'threshold': self.thresholds['max_lines'] }) if metrics['number_of_methods'] > self.thresholds['max_methods']: issues.append({ 'type': 'too_many_methods', 'severity': 'high', 'description': f"メソッド数が多すぎます ({metrics['number_of_methods']} 個)", 'threshold': self.thresholds['max_methods'] }) if metrics['cohesion'] < 0.3: issues.append({ 'type': 'low_cohesion', 'severity': 'medium', 'description': f"凝集度が低いです ({metrics['cohesion']:.2f})", 'threshold': 0.3 }) if metrics['coupling'] > 0.7: issues.append({ 'type': 'high_coupling', 'severity': 'medium', 'description': f"結合度が高いです ({metrics['coupling']:.2f})", 'threshold': 0.7 }) return issues def calculate_god_class_score(self, metrics): """神クラス度の計算(0-100)""" score = 0 # サイズによるスコア size_score = min(50, (metrics['lines_of_code'] / 500) * 50) score += size_score # 複雑度によるスコア complexity_score = min(25, (metrics['cyclomatic_complexity'] / 20) * 25) score += complexity_score # 結合度によるスコア coupling_score = metrics['coupling'] * 25 score += coupling_score # 凝集度によるスコア(低いほど問題) cohesion_score = (1 - metrics['cohesion']) * 25 score += cohesion_score return min(100, score) def calculate_cohesion(self, class_def): """クラスの凝集度計算""" # LCOM (Lack of Cohesion in Methods) メトリクスの実装 methods = self.get_methods(class_def) fields = self.get_fields(class_def) if not methods or not fields: return 1.0 field_usage = {} for field in fields: field_usage[field] = set() for method in methods: used_fields = self.get_fields_used_by_method(method) for field in used_fields: if field in field_usage: field_usage[field].add(method.name) # 共通のフィールドを使うメソッドペアの数 common_pairs = 0 different_pairs = 0 for i, method1 in enumerate(methods): for method2 in methods[i+1:]: method1_fields = self.get_fields_used_by_method(method1) method2_fields = self.get_fields_used_by_method(method2) if method1_fields & method2_fields: # 共通フィールドがある common_pairs += 1 else: different_pairs += 1 total_pairs = common_pairs + different_pairs if total_pairs == 0: return 1.0 return common_pairs / total_pairs
責任の分析
責任の特定
class ResponsibilityAnalyzer: def __init__(self): self.responsibility_keywords = { 'data_access': ['save', 'load', 'find', 'query', 'database', 'repository'], 'validation': ['validate', 'check', 'verify', 'ensure'], 'authentication': ['login', 'logout', 'authenticate', 'authorize', 'token'], 'notification': ['notify', 'send', 'email', 'push', 'message'], 'formatting': ['format', 'render', 'display', 'view', 'template'], 'calculation': ['calculate', 'compute', 'process', 'transform'], 'logging': ['log', 'trace', 'debug', 'info', 'error'], 'caching': ['cache', 'store', 'retrieve', 'expire'], 'serialization': ['serialize', 'deserialize', 'json', 'xml', 'parse'] } def analyze_responsibilities(self, class_definition): """クラスの責任分析""" methods = self.extract_methods(class_definition) responsibilities = {} for method in methods: method_responsibilities = self.identify_method_responsibilities(method) for responsibility in method_responsibilities: if responsibility not in responsibilities: responsibilities[responsibility] = [] responsibilities[responsibility].append(method.name) return { 'responsibilities': responsibilities, 'responsibility_count': len(responsibilities), 'violation_severity': self.calculate_violation_severity(responsibilities), 'suggestions': self.generate_separation_suggestions(responsibilities) } def identify_method_responsibilities(self, method): """メソッドの責任特定""" method_text = method.name.lower() + ' ' + ' '.join(method.body_text.lower().split()) identified_responsibilities = [] for responsibility, keywords in self.responsibility_keywords.items(): for keyword in keywords: if keyword in method_text: identified_responsibilities.append(responsibility) break return identified_responsibilities def calculate_violation_severity(self, responsibilities): """違反の深刻度計算""" responsibility_count = len(responsibilities) if responsibility_count <= 1: return 'none' elif responsibility_count <= 3: return 'low' elif responsibility_count <= 5: return 'medium' else: return 'high' def generate_separation_suggestions(self, responsibilities): """分離提案の生成""" suggestions = [] for responsibility, methods in responsibilities.items(): if len(methods) >= 3: # 3つ以上のメソッドがある責任 suggestions.append({ 'responsibility': responsibility, 'methods': methods, 'suggestion': f"{responsibility.replace('_', ' ').title()}Manager クラスを作成", 'rationale': f"{len(methods)}個のメソッドが{responsibility}の責任を持っています" }) return suggestions
神クラスのリファクタリング手法
クラス分割のパターン
Extract Class パターン
# リファクタリング前:神クラスclass OrderProcessor: def __init__(self): self.orders = [] self.customers = {} self.inventory = {} self.payment_methods = {} # 注文処理 def create_order(self, customer_id, items): # 注文作成ロジック pass def cancel_order(self, order_id): # 注文キャンセルロジック pass # 顧客管理 def add_customer(self, customer_data): # 顧客追加ロジック pass def update_customer(self, customer_id, data): # 顧客更新ロジック pass # 在庫管理 def check_inventory(self, item_id): # 在庫確認ロジック pass def update_inventory(self, item_id, quantity): # 在庫更新ロジック pass # 決済処理 def process_payment(self, order_id, payment_method): # 決済処理ロジック pass def refund_payment(self, order_id): # 返金処理ロジック pass # 通知機能 def send_order_confirmation(self, order_id): # 確認メール送信 pass def send_shipping_notification(self, order_id): # 発送通知送信 pass
# リファクタリング後:責任別に分割class Order: """注文データの表現""" def __init__(self, order_id, customer_id, items): self.order_id = order_id self.customer_id = customer_id self.items = items self.status = 'pending' self.created_at = datetime.now() self.total_amount = self.calculate_total() def calculate_total(self): return sum(item['price'] * item['quantity'] for item in self.items) def cancel(self): if self.status == 'pending': self.status = 'cancelled' return True return False
class OrderRepository: """注文データアクセス""" def __init__(self): self.orders = {} def save(self, order): self.orders[order.order_id] = order def find_by_id(self, order_id): return self.orders.get(order_id) def find_by_customer(self, customer_id): return [order for order in self.orders.values() if order.customer_id == customer_id]
class CustomerManager: """顧客管理に特化""" def __init__(self): self.customers = {} def add_customer(self, customer_data): customer_id = self.generate_customer_id() customer = Customer(customer_id, customer_data) self.customers[customer_id] = customer return customer def update_customer(self, customer_id, data): customer = self.customers.get(customer_id) if customer: customer.update(data) return customer return None def get_customer(self, customer_id): return self.customers.get(customer_id)
class InventoryManager: """在庫管理に特化""" def __init__(self): self.inventory = {} def check_availability(self, item_id, quantity): current_stock = self.inventory.get(item_id, 0) return current_stock >= quantity def reserve_items(self, items): for item in items: if not self.check_availability(item['id'], item['quantity']): return False # 在庫を予約 for item in items: self.inventory[item['id']] -= item['quantity'] return True def release_reservation(self, items): for item in items: self.inventory[item['id']] += item['quantity']
class PaymentProcessor: """決済処理に特化""" def __init__(self): self.payment_gateway = PaymentGateway() def process_payment(self, order, payment_method): try: transaction = self.payment_gateway.charge( amount=order.total_amount, payment_method=payment_method ) return PaymentResult(success=True, transaction_id=transaction.id) except PaymentException as e: return PaymentResult(success=False, error=str(e)) def refund_payment(self, transaction_id, amount): try: refund = self.payment_gateway.refund(transaction_id, amount) return RefundResult(success=True, refund_id=refund.id) except PaymentException as e: return RefundResult(success=False, error=str(e))
class OrderNotificationService: """注文関連通知に特化""" def __init__(self, email_service): self.email_service = email_service def send_order_confirmation(self, order, customer): template = self.get_confirmation_template() email_content = template.render(order=order, customer=customer) self.email_service.send_email( to=customer.email, subject="注文確認", content=email_content ) def send_shipping_notification(self, order, customer, tracking_number): template = self.get_shipping_template() email_content = template.render( order=order, customer=customer, tracking_number=tracking_number ) self.email_service.send_email( to=customer.email, subject="商品発送のお知らせ", content=email_content )
class OrderService: """注文処理のオーケストレーション""" def __init__(self, order_repository, customer_manager, inventory_manager, payment_processor, notification_service): self.order_repository = order_repository self.customer_manager = customer_manager self.inventory_manager = inventory_manager self.payment_processor = payment_processor self.notification_service = notification_service def create_order(self, customer_id, items, payment_method): # 1. 顧客確認 customer = self.customer_manager.get_customer(customer_id) if not customer: raise ValueError("Customer not found") # 2. 在庫確認と予約 if not self.inventory_manager.reserve_items(items): raise ValueError("Insufficient inventory") try: # 3. 注文作成 order = Order(self.generate_order_id(), customer_id, items) # 4. 決済処理 payment_result = self.payment_processor.process_payment(order, payment_method) if not payment_result.success: self.inventory_manager.release_reservation(items) raise ValueError(f"Payment failed: {payment_result.error}") # 5. 注文保存 order.status = 'confirmed' self.order_repository.save(order) # 6. 確認メール送信 self.notification_service.send_order_confirmation(order, customer) return order except Exception as e: # エラー時は在庫予約を解除 self.inventory_manager.release_reservation(items) raise e
Facade パターンによる統合
# 複雑な分割されたクラス群を簡単に使えるようにするFacadeclass OrderProcessingFacade: """注文処理の統合インターフェース""" def __init__(self): # 各種サービスの初期化 self.order_repository = OrderRepository() self.customer_manager = CustomerManager() self.inventory_manager = InventoryManager() self.payment_processor = PaymentProcessor() self.notification_service = OrderNotificationService(EmailService()) self.order_service = OrderService( self.order_repository, self.customer_manager, self.inventory_manager, self.payment_processor, self.notification_service ) def process_order(self, customer_id, items, payment_method): """ワンストップで注文処理""" try: order = self.order_service.create_order(customer_id, items, payment_method) return OrderResult(success=True, order=order) except Exception as e: return OrderResult(success=False, error=str(e)) def get_customer_orders(self, customer_id): """顧客の注文履歴取得""" return self.order_repository.find_by_customer(customer_id) def cancel_order(self, order_id): """注文キャンセル""" order = self.order_repository.find_by_id(order_id) if order and order.cancel(): # 在庫を戻す self.inventory_manager.release_reservation(order.items) # 返金処理 # ... 返金ロジック return True return False
# 使用例:複雑な内部構造を隠して簡単に使用facade = OrderProcessingFacade()result = facade.process_order( customer_id="CUST123", items=[{"id": "ITEM1", "quantity": 2, "price": 1000}], payment_method="credit_card")
段階的リファクタリング
ステップバイステップの改善
# Step 1: 大きなメソッドの分割class UserManager: def process_user_registration(self, user_data): # リファクタリング前:巨大なメソッド # ... 100行以上のコード pass # Step 1: メソッドを機能別に分割 def register_user(self, user_data): """メインのワークフロー""" self.validate_user_data(user_data) user = self.create_user_object(user_data) self.save_user_to_database(user) self.send_welcome_email(user) self.log_registration(user) return user def validate_user_data(self, user_data): """データ検証""" if not user_data.get('email'): raise ValueError("Email is required") if not self.is_valid_email(user_data['email']): raise ValueError("Invalid email format") if self.email_exists(user_data['email']): raise ValueError("Email already exists") def create_user_object(self, user_data): """ユーザーオブジェクト作成""" return User( username=user_data['username'], email=user_data['email'], password_hash=self.hash_password(user_data['password']) ) def save_user_to_database(self, user): """データベース保存""" self.database.save(user) def send_welcome_email(self, user): """ウェルカムメール送信""" self.email_service.send_welcome_email(user.email) def log_registration(self, user): """登録ログ""" self.logger.info(f"User registered: {user.username}")
# Step 2: 責任の分離class UserValidator: """ユーザーデータ検証に特化""" def __init__(self, user_repository): self.user_repository = user_repository def validate_registration_data(self, user_data): self.validate_required_fields(user_data) self.validate_email_format(user_data['email']) self.validate_email_uniqueness(user_data['email']) self.validate_password_strength(user_data['password']) def validate_required_fields(self, user_data): required_fields = ['username', 'email', 'password'] for field in required_fields: if not user_data.get(field): raise ValueError(f"{field} is required") def validate_email_format(self, email): import re pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' if not re.match(pattern, email): raise ValueError("Invalid email format") def validate_email_uniqueness(self, email): if self.user_repository.find_by_email(email): raise ValueError("Email already exists") def validate_password_strength(self, password): if len(password) < 8: raise ValueError("Password must be at least 8 characters")
class UserFactory: """ユーザーオブジェクト作成に特化""" def create_user(self, user_data): return User( username=user_data['username'], email=user_data['email'], password_hash=self.hash_password(user_data['password']), created_at=datetime.now() ) def hash_password(self, password): import hashlib return hashlib.sha256(password.encode()).hexdigest()
class UserRegistrationService: """ユーザー登録の統合サービス""" def __init__(self, validator, factory, repository, email_service, logger): self.validator = validator self.factory = factory self.repository = repository self.email_service = email_service self.logger = logger def register_user(self, user_data): # 1. 検証 self.validator.validate_registration_data(user_data) # 2. ユーザー作成 user = self.factory.create_user(user_data) # 3. 保存 self.repository.save(user) # 4. 通知 self.email_service.send_welcome_email(user.email) # 5. ログ self.logger.info(f"User registered: {user.username}") return user
予防策とベストプラクティス
設計時の注意点
クラス設計のチェックリスト
## クラス設計チェックリスト
### 単一責任の確認- [ ] クラスの責任を一文で説明できるか?- [ ] クラス名が責任を適切に表現しているか?- [ ] 変更理由が複数ないか?
### サイズの確認- [ ] クラスのサイズは適切か?(目安:200行以下)- [ ] メソッド数は適切か?(目安:20個以下)- [ ] フィールド数は適切か?(目安:15個以下)
### 結合度と凝集度- [ ] クラス内のメソッドは関連性が高いか?- [ ] 他のクラスへの依存は最小限か?- [ ] インターフェースが明確に定義されているか?
### 拡張性- [ ] 新しい機能追加時に既存コードを変更する必要がないか?- [ ] 抽象化が適切に行われているか?- [ ] 依存関係が適切に管理されているか?
継続的なモニタリング
# 開発過程でのクラス品質監視class ClassQualityMonitor: def __init__(self): self.quality_rules = [ MaxLinesRule(threshold=200), MaxMethodsRule(threshold=20), MaxFieldsRule(threshold=15), CohesionRule(min_threshold=0.3), CouplingRule(max_threshold=0.7) ] def monitor_class_quality(self, codebase): """コードベース全体のクラス品質監視""" quality_report = {} for class_file in codebase.get_class_files(): class_def = codebase.parse_class(class_file) quality_score = self.evaluate_class_quality(class_def) quality_report[class_file] = quality_score return self.generate_quality_summary(quality_report) def evaluate_class_quality(self, class_def): """個別クラスの品質評価""" violations = [] for rule in self.quality_rules: violation = rule.check(class_def) if violation: violations.append(violation) return { 'class_name': class_def.name, 'violations': violations, 'quality_score': self.calculate_quality_score(violations), 'recommendations': self.generate_recommendations(violations) } def generate_quality_summary(self, quality_report): """品質サマリーの生成""" total_classes = len(quality_report) problematic_classes = sum(1 for report in quality_report.values() if report['quality_score'] < 70) return { 'total_classes': total_classes, 'problematic_classes': problematic_classes, 'problem_ratio': problematic_classes / total_classes if total_classes > 0 else 0, 'top_violations': self.get_top_violations(quality_report), 'recommendations': self.get_global_recommendations(quality_report) }
class MaxLinesRule: def __init__(self, threshold=200): self.threshold = threshold def check(self, class_def): lines = class_def.count_lines() if lines > self.threshold: return { 'rule': 'max_lines', 'severity': 'high' if lines > self.threshold * 1.5 else 'medium', 'message': f"クラスが大きすぎます: {lines}行 (上限: {self.threshold}行)", 'suggestion': "クラスを複数の小さなクラスに分割することを検討してください" } return None
コードレビューでの観点
神クラス防止のレビュー観点
class GodClassReviewGuide: def __init__(self): self.review_checklist = { 'responsibility_analysis': [ "このクラスの主要な責任は何か?", "複数の責任を持っていないか?", "責任が明確に分離されているか?" ], 'size_analysis': [ "クラスのサイズは適切か?", "メソッド数は妥当か?", "複雑すぎるメソッドはないか?" ], 'design_analysis': [ "単一責任の原則に従っているか?", "開放閉鎖の原則に従っているか?", "依存関係は適切に管理されているか?" ], 'maintainability_analysis': [ "このクラスは理解しやすいか?", "変更時の影響範囲は限定的か?", "テストは書きやすいか?" ] } def generate_review_questions(self, class_definition): """クラスに特化したレビュー質問生成""" analysis = self.analyze_class_characteristics(class_definition) questions = [] if analysis['has_size_issues']: questions.extend([ "このクラスは分割できる部分はないか?", "関連の薄い機能が混在していないか?" ]) if analysis['has_multiple_responsibilities']: questions.extend([ "このクラスの変更理由はいくつあるか?", "異なる責任を別クラスに分離できないか?" ]) if analysis['has_high_coupling']: questions.extend([ "他のクラスへの依存を減らせないか?", "インターフェースを介した依存に変更できないか?" ]) return questions def suggest_refactoring_strategies(self, class_definition): """リファクタリング戦略の提案""" analysis = self.analyze_class_characteristics(class_definition) strategies = [] if analysis['responsibility_count'] > 3: strategies.append({ 'strategy': 'Extract Class', 'description': '責任ごとに別々のクラスに分離する', 'priority': 'high', 'estimated_effort': 'medium' }) if analysis['method_count'] > 20: strategies.append({ 'strategy': 'Extract Method', 'description': '大きなメソッドを小さなメソッドに分割する', 'priority': 'medium', 'estimated_effort': 'low' }) if analysis['coupling_score'] > 0.7: strategies.append({ 'strategy': 'Introduce Interface', 'description': 'インターフェースを導入して依存を抽象化する', 'priority': 'medium', 'estimated_effort': 'medium' }) return strategies
まとめ
神クラスの問題は、単一責任の原則を守り、適切な設計パターンを適用することで予防できます。
重要なポイント
- 早期発見: メトリクスや責任分析による継続的な監視
- 予防設計: SOLID原則に基づいた設計
- 段階的改善: 大規模な変更ではなく段階的なリファクタリング
- チーム意識: コードレビューでの神クラス防止意識の共有
- 継続的改善: 設計品質の継続的な向上
神クラスを避けることで、保守性が高く、理解しやすく、テストしやすいコードを書くことができます。
既存の神クラスがある場合も、適切なリファクタリング手法を適用することで段階的に改善できます。 まずは小さなクラスから設計を意識して、徐々に良い設計習慣を身につけていきましょう。