見出し画像

よくわかるSOLID原則3: L(リスコフの置換原則)

ソフトウェアエンジニアが知っているべきSOLID原則についての記事です。SOLID原則は、5つの原則の頭文字を並べた言葉で、S・O・L・I・Dそれぞれの原則について、5回に分けて説明します。

1) Single Responsibility Principle:単一責任の原則
2) Open/closed principle:オープン/クロースドの原則
3) Liskov substitution principle:リスコフの置換原則
4) Interface segregation principle:インターフェース分離の原則
5) Dependency inversion principle:依存性逆転の原則

今回は3番目のリスコフの置換原則です。

なぜソフトウェアエンジニアがSOLID原則について知っていなければいけないかは最初の記事をご覧ください。

リスコフの置換原則

リスコフの置換原則は、難しくいうなら

S が T の派生型であれば、プログラム内で T 型のオブジェクトが使われている箇所は全て S 型のオブジェクトで置換可能

という原則です。この時Tはオブジェクトやクラスなどとは限らず、現代であればインターフェースなどで表現されることが多いでしょう。Sは継承やダックタイピングやインターフェースの実装など、あらゆる手段で、Tを拡張した何かです。

・ Tは仕様書
・ Sはそれの実装

と読み替えてもいいでしょう。Tの使用を満たすSなら、それは他のものと置換してもいいということです。たとえば、S1・S2・S3・S4など、いくつかの実装があったとして、Tという文脈であれば、S1でもS2でもS3でもS4でも、全く問題なく動作しなければいけないということです。


契約プログラミングの観点

Wikipediaでの説明では契約プログラミング的観点で

事前条件を派生型で強めることはできない。つまり、上位の型よりも強い事前条件を持つ派生型を作ることはできない。

となっています。これはたとえばインターフェースTの実装であるS1、S2などは、呼び出し方は、Tの呼び出し方そのものでなければいけません。そのため、S1固有の呼び出し方、S2固有の呼び出し方などは許されません。もしどうしてもそれがしたければ、Tでそれを何かしら定義する必要があります。

もう少し平易に言うとすれば、Tとして宣言されてる場所ではそれは必ずTとして扱いましょうということです。実体がSだろうがS1だろうがS2だろうがなんだろうが関係ありません。Tとして振る舞うって言ってる限りはT以外の振る舞いをしてはいないということです。

事後条件を派生型で弱めることはできない。つまり、上位の型よりも弱い事後条件を持つ派生型を作ることはできない。

戻り値や副作用などもTで決まってる範囲は、必ずS1、S2、S3などどれもが実装していなければいけません。本来文字列が帰ってくるはずなのに、S1の場合だけnumberを返したり、nullを返したりしてはいけません。

これもTの振る舞いするって言ってるんだからT以外の振る舞いをしてはいけないのです。

知識を限定する

知識というのは、たとえばソースコードの名前や、メソッドの実装、マジックナンバーなどあらゆる情報を知識と呼びます。リスコフの置換原則はTを経由してS1、S2、S3にアクセスするならば、S1、S2、S3の知識を使ってはいけないと言い換えることができます。

S1は実際にどういう実装をしているか?に併せてエラー処理を追加することは許されません。必ず、T自体がそれを規定していなければいけないのです。

逆の言い方をすると、Tさえ見ればS1、S2、S3を見なくても、Tを使ったプログラミングができるということです。誰もわざわざS1やS2やS3固有の情報なんて見たくもありません。

じつは、次に紹介するISP(インターフェース分離の原則)でも、知識を遮断するという考え方がとても重要になります。

不要な知識の漏洩は密結合の主原因となります。知識を遮断できるなら疎結合にできる、つまり複雑さを下げられるということです。

まとめ

型の派生、インターフェースの実装、ダックタイピングなどを行うなら、派生するベースにだけ依存して、それを拡張する側の知識は使うなというのがLSPです。

次回は、Interface segregation principle:インターフェース分離の原則です。

この記事が気に入ったらサポートをしてみませんか?