SOLID は、開発者がより保守しやすく、理解しやすく、柔軟なソフトウェアを作成するのに役立つ 5 つの設計原則を表す頭字語です。関連性のある例を使用して、それぞれを見てみましょう。
定義: クラスが変更する理由は 1 つだけである必要があります。つまり、クラスの仕事または責任は 1 つだけである必要があります。
説明: 電子メールの送信と支払い処理など、2 つの異なるタスクを組み合わせたツールがあると想像してください。両方のタスクが 1 つのクラスで処理される場合、電子メール機能の変更により支払い機能が壊れる可能性があります。これらの責任を分離しておくことで、ある部分の変更が別の部分に影響を与えるリスクを最小限に抑えることができます。
例:
class EmailSender: def send_email(self, recipient, subject, body): # Code to send an email print(f"Sending email to {recipient} with subject '{subject}'") class PaymentProcessor: def process_payment(self, amount): # Code to process payment print(f"Processing payment of amount {amount}") # Usage email_sender = EmailSender() email_sender.send_email("user@example.com", "Hello!", "Welcome to our service!") payment_processor = PaymentProcessor() payment_processor.process_payment(100)
この例では、EmailSender は電子メールの送信のみを担当し、PaymentProcessor は支払いの処理のみを担当します。それぞれに単一の責任があるため、コードの保守と拡張が容易になります。
定義: ソフトウェア エンティティ (クラス、モジュール、関数など) は拡張に対してオープンである必要がありますが、変更に対してはクローズされている必要があります。
説明: これは、既存のコードを変更せずに、クラスに新しい機能や動作を追加できる必要があることを意味します。支払い処理システムがあり、新しい支払い方法を追加したいと考えているとします。既存のコードを変更せずに、この新しいメソッドを追加できるはずです。
例:
from abc import ABC, abstractmethod class PaymentProcessor(ABC): @abstractmethod def process_payment(self, amount): pass class CreditCardPayment(PaymentProcessor): def process_payment(self, amount): print(f"Processing credit card payment of {amount}") class PayPalPayment(PaymentProcessor): def process_payment(self, amount): print(f"Processing PayPal payment of {amount}") # Usage payments = [CreditCardPayment(), PayPalPayment()] for payment in payments: payment.process_payment(100)
この例では、PaymentProcessor は支払いを処理するためのコントラクトを定義する抽象クラスです。 CreditCardPayment と PayPalPayment は、このクラスを拡張した実装です。新しい支払い方法を追加する場合は、既存のクラスを変更せずに、PaymentProcessor を拡張する新しいクラスを作成できます。
定義: サブタイプは、プログラムの正確さを変えることなく、その基本タイプと置き換え可能でなければなりません。
説明: これは、プログラムの機能に影響を与えることなく、スーパークラスのオブジェクトをサブクラスのオブジェクトに置き換えることができる必要があることを意味します。たとえば、Vehicle クラスで動作する関数がある場合、それは Car や Bike などのサブクラスでも動作する必要があります。
例:
class Vehicle: def start_engine(self): pass class Car(Vehicle): def start_engine(self): print("Starting car engine...") class Bike(Vehicle): def start_engine(self): print("Starting bike engine...") # Usage def start_vehicle_engine(vehicle: Vehicle): vehicle.start_engine() car = Car() bike = Bike() start_vehicle_engine(car) # Should work fine start_vehicle_engine(bike) # Should work fine
この例では、Car と Bike は Vehicle のサブクラスです。 start_vehicle_engine 関数は、サブクラスの詳細を知らなくても、Vehicle の任意のサブクラスを操作できます。これは、リスコフ置換原則に準拠しています。
定義: クライアントは、使用しないインターフェースの実装を強制されるべきではありません。 1 つのファット インターフェイスの代わりに、メソッドのグループに基づいて、それぞれが 1 つのサブモジュールを提供する多数の小さなインターフェイスが優先されます。
説明: この原則は、1 つの汎用インターフェイスではなく、クライアントのタイプごとに特定のインターフェイスを作成する必要があることを示唆しています。印刷、スキャン、ファックスができるマシンがあると想像してください。印刷またはスキャンのみが可能な別のマシンがある場合は、使用しない機能の実装を強制すべきではありません。
例:
from abc import ABC, abstractmethod class Printer(ABC): @abstractmethod def print(self, document): pass class Scanner(ABC): @abstractmethod def scan(self, document): pass class MultiFunctionDevice(Printer, Scanner): def print(self, document): print(f"Printing: {document}") def scan(self, document): print(f"Scanning: {document}") # Usage mfd = MultiFunctionDevice() mfd.print("Document 1") mfd.scan("Document 2")
ここでは、プリンターとスキャナーは別のインターフェイスです。 MultiFunctionDevice は両方を実装しますが、印刷のみまたはスキャンのみを行うデバイスがある場合は、インターフェイス分離原則に従って、使用しないメソッドを実装する必要はありません。
定義: 高レベルのモジュールは低レベルのモジュールに依存すべきではありません。どちらも抽象化 (インターフェイスなど) に依存する必要があります。抽象化は詳細に依存すべきではありません。詳細は抽象化に依存する必要があります。
説明: 高レベルのクラスが低レベルのクラスに直接依存するのではなく、両方ともインターフェイスまたは抽象クラスに依存する必要があります。これにより、柔軟性が向上し、メンテナンスが容易になります。
例:
from abc import ABC, abstractmethod class NotificationService(ABC): @abstractmethod def send(self, message): pass class EmailNotificationService(NotificationService): def send(self, message): print(f"Sending email: {message}") class SMSNotificationService(NotificationService): def send(self, message): print(f"Sending SMS: {message}") class NotificationSender: def __init__(self, service: NotificationService): self.service = service def notify(self, message): self.service.send(message) # Usage email_service = EmailNotificationService() sms_service = SMSNotificationService() notifier = NotificationSender(email_service) notifier.notify("Hello via Email") notifier = NotificationSender(sms_service) notifier.notify("Hello via SMS")
この例では、NotificationSender は、EmailNotificationService や SMSNotificationService のような具体的なクラスではなく、NotificationService 抽象化に依存します。こうすることで、NotificationSender クラスを変更せずに通知サービスを切り替えることができます。
単一責任原則 (SRP): クラスは 1 つのことを実行し、それを適切に実行する必要があります。
オープン/クローズ原則 (OCP): クラスは拡張のためにオープンですが、変更のためにクローズされる必要があります。
リスコフ置換原則 (LSP): サブクラスは基本クラスの置換可能である必要があります。
インターフェース分離原則 (ISP): どのクライアントも、使用しないメソッドに依存することを強制されるべきではありません。
依存性反転原則 (DIP): 具体的な実装ではなく、抽象化に依存します。
これらの SOLID 原則に従うことで、理解しやすく、保守し、拡張しやすいソフトウェアを作成できます。
以上がSOLID 原則 - Python の実例を使用して説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。