𝗔𝗯𝘀𝘁𝗿𝗮𝗰𝘁𝗶𝗼𝗻 𝗶𝗻 𝗢𝗢𝗣: 𝗛𝗶𝗱𝗶𝗻𝗴 𝗖𝗼𝗺𝗽𝗹𝗲𝘅𝗶𝘁𝘆
車の運転は、いくつかの単純な操作で行います。アクセルを踏む、ハンドルを切る、ブレーキをかける、といった具合です。
燃料がどのようにエンジンに送られるかを知る必要はありません。ピストンがどのように動き、ギアがどのように切り替わるかを理解する必要もありません。車はそれらの詳細をあなたから隠しています。あなたは単純なインターフェースを使って、複雑な機械を制御しているのです。
ソフトウェアも同じ仕組みで動いています。
メールの送信を例に考えてみましょう。あなたは一つのコマンドを呼び出すだけです:emailService.send()。
裏側では、多くのことが行われています:
- システムがアドレスを検証する。
- SMTP接続を作成する。
- プロバイダーとの認証を行う。
- メッセージを構築する。
- 初回の試行が失敗した場合、リトライ(再試行)を処理する。
これらのステップを見る必要はありません。あなたはただ、メールが送信されることだけを気にすればよいのです。
もし抽象化を使わなければ、コードは次のようになります:
- connect()
- authenticate()
- buildMessage()
- sendMessage()
- disconnect()
これを毎回行うと問題が発生します。コードのあらゆる部分が「知りすぎている」状態になるからです。もしメールプロバイダーを変更した場合、メールを送信しているすべてのコード行を更新しなければなりません。その結果、システムは変更が困難になります。
抽象化はこれを解決します。オブジェクトが「何をするか」を示し、「どのように動作するか」を隠します。
単純なインターフェースを公開することで、呼び出し側はタスクに集中できます。実装は隠されたままです。これにより、コードの結合度が低く(decoupled)、メンテナンスしやすい状態を保つことができます。
抽象化(Abstraction)とカプセル化(Encapsulation)を混同してしまうことがよくあります。
カプセル化は「誰がこのデータを変更できるか?」を問い、内部状態を保護します。 抽象化は「ユーザーは何の詳細を見る必要があるか?」を問い、複雑さを隠蔽します。
これらは組み合わさることで、より優れたシステムを構築します。
次は「継承(Inheritance)」について見ていきます。一つの疑問に答えていきましょう。もしオブジェクトが振る舞いを共有しているなら、同じコードを二度書く必要があるのでしょうか?