
初心者が何の指針も無しにプログラミングすることは、目隠しをした状態で吊橋を渡るようなものです。今回はそんな事にならないように、プログラミング初心者向けに、オブジェクト指向によるプログラミングの基本原則「SOLID Principles」を紹介します。
見出し
はじめに
プログラミングを始めたばかりというのはだいたい目の前しか見えていません。目の前の小さな問題解決で一杯一杯なので、そのソースコードが将来誰に読まれたり、どのように運用されたり、どのように拡張されたりなんてことは全く見えていないでしょう。そのまま進めていくと、後で気づいたら悲惨な技術的負債の山が出来上がっているかもしれません。
では、どうすればよいのでしょうか?簡単です。先人の知恵に従えばよいのです。いわゆるベストプラクティスを学ぶ事です。今回はオブジェクト指向でプログラミングをしたことがある経験者なら誰でも知っている基本原則「SOLID Principles」を噛み砕いて紹介します。
基本原則「SOLID Principles」
Single Responsibility Principle
最も有名な原則で、日本語では「単一責任の原則」や「単一責務の原則」などと表現されます。
これは「モジュールやクラスは単一の機能に対して責任を持たせるべき。」という原則です。別の言い方をすると、一つのクラスに関係の無い機能を沢山持たせずに、そのクラスが果たすべき責務に応じた機能だけを持たせなさい、と言うことになります。
メリットは、関係の無い機能間で影響が少なくなるので(いわゆる疎結合)、ロバスト性(他の機能を変更した時に関係の無い機能まで意図せずに変更しなければならない状況を避けること)を高めることができることです。さらに、この原則で書かれたソースコードはシンプルなクラスになるので読みやすいというメリットもあります。
Open / Closed Principle
これは「ソフトウェアのエンティティは拡張のためにオープンされ、変更のためにクローズドされるべき。」という原則です。別の言い方とすると、仕様変更が起きても修正する必要のないソースコードを書き(クローズド)、機能追加のために拡張できるソースコードを書くべき(オープン)と言うことになります。
それをどうやって実現するのかと言うと、オブジェクト指向では当たり前の「クラス継承」や「インターフェース実装」を利用したポリモルフィズムを実現することです。
このメリットは、やはりロバスト性が高まることと、既存機能への仕様変更の影響を少なくすることです。つまり、仕様変更や機能追加が簡単になるということです。
例えば、ある銀行の残高照会のAPIを使って会計簿を作るアプリを作るとします。その時、銀行を表すインターフェースを作った上でそれを実装した特定の銀行を表すクラスを作ったとします。すると、その銀行がAPIの仕様を変更したとしても、インターフェース経由でその機能を利用している他のクラスは変更する必要がありません(クローズド)。さらに、対応する銀行を増やす機能追加をする場合も、同じインターフェースを実装して作れば良いので拡張は容易です(オープン)。
Liskov Substitution Principle
これは「もしT型クラスのサブクラスとしてS型を定義した時、T型のオブジェクトはS型のオブジェクトと交換可能にすべき。」という原則です。別の言い方をすると、この原則を維持するには、親クラスを継承した子クラスがあり、子クラスが親クラスのメソッドをオーバーライドしている場合、オーバーライドしたメソッドに関して、引数となる型にはより抽象度の高い型を使うことができ、戻り値となる型には継承された型を使うことができます。
メリットは、ソースコード間の疎結合を促し、再利用性やメンテナス性を高めることです。
言葉で考えるとややこしいのですが、実はこれはJavaやC#などの主要なオブジェクト指向言語では自動的に満たされるため、クラス継承やインターフェース実装を行っていれば、意識せずに実現できます。C++などのかなり昔にできた言語はこの原則を満たすように意識してプログラミングする必要がありました。こういった原則の元となる文献はC++をベースに考えられていることが多く、その当時の状況を反映している原則だと言えます。
Interface Segregation Principle
これは「使用しないメソッド郡に依存するようなクライアントがあってはならない。インターフェースが多すぎるメソッド郡を持っている場合、そのメソッド郡を小さく具体的に分割することで、クライアントが関心のあるメソッドのみを持つようにすべき。」という原則です。別の言い方をすると、親クラスに沢山のメソッドを持たせると、それを継承した子クラスが不要なメソッドを持つ事になってしまうので、親クラスは共通的なメソッドのみを持つようにし、子クラスが必要なメソッドだけを持つようにすべきと言うことです。
メリットは、疎結合を促し、変更を容易にし、メンテナンス性を高めることができます。
デメリットとしては、複雑なアプリケーションの場合に、小さいメソッドが多すぎると、管理が難しくなるという問題があります。適切なさじ加減が腕の見せどころです。
Dependency Inversion Principle
これは「高レベルのモジュールは低レベルのモジュールに依存すべきでなく、間に抽象を挟むべき。抽象は詳細に依存すべきではなく、詳細が抽象に依存すべき。」という原則です。別の言い方をすると、ある具体的な処理をするクラス(低レベルのモジュール)があり、それを利用するクラス(高レベルのモジュール)がある場合、低レベルのクラスを抽象化したクラスもしくはインターフェースを用意し、高レベルのクラスはその抽象化したクラスもしくはインターフェースを経由して、低レベルのクラスを利用するように実装すべきと言うことです。
メリットは、高レベルのモジュールと低レベルのモジュールの間に抽象化するインターフェースを挟んでいるので、それらのモジュールは疎結合になり、メンテナンス性は向上します。
デメリットして、不必要に抽象化を行うと、無駄なインターフェースを量産することにつながり、ソースコードを複雑化してメンテナンス性を落とす結果になります。この抽象化が必要かどうかを見極めて適切に適用することが腕の見せどころです。
最後に
いかがでしたか?基本原則と言うのは意識せずに実行できるレベルになっている事が望ましいです。私の場合はこれらの基本原則はある程度プログラミングができるようになってから知ったのですが、全て当たり前だなと思ったのを覚えています。オブジェクト指向プログラミングをマスターしている人は何も考えずに行っていることだからです。これからオブジェクト指向プログラミングを身につけたいと思っている人は、この基本原則を意識して体に染み込ませましょう。では。


コメントを残す