セマンティックドメインにはIS-A関係があるように見えますが、サブタイプ/継承を使用してこれをモデル化することには少し注意が必要です(特にランタイムタイプの反映のため)。しかし、あなたは間違ったことを怖がっていると思います。サブタイプには確かに危険が伴いますが、実行時にオブジェクトを照会しているという事実は問題ではありません。意味がわかります。
オブジェクト指向プログラミングはIS-A関係の概念に非常に強く依存しており、おそらく2つの有名な重要な概念につながっていると思われます。
しかし、IS-Aの関係を見るための、より機能的なプログラミングベースの別の方法があり、おそらくこれらの困難はないと思います。まず、プログラムで馬とユニコーンをモデル化するため、a HorseとUnicorntypeを作成します。これらのタイプの値は何ですか?まあ、私はこれを言うだろう:
- これらのタイプの値は、それぞれ馬とユニコーンの表現または説明です。
- これらは、スキーマ化された表現または説明です。自由形式ではなく、非常に厳しいルールに従って構築されています。
それは当たり前のように聞こえるかもしれませんが、人々が円楕円問題のような問題に取り組む方法の1つは、これらの点を十分に気にしないことです。すべての円は楕円ですが、それは、円のすべての図式化された説明が、異なるスキーマに従って楕円の図式化された説明になることを意味しません。つまり、円が楕円だからといって、a Circleがであるという意味ではありませんEllipse。しかし、それは次のことを意味します:
- すべての(スキーム化された円の説明)を、同じ円を説明する(異なるタイプの説明)に変換する合計関数があります。
CircleEllipse
- を取り、円を記述する場合、対応するを返す部分関数があり
EllipseますCircle。
したがって、関数型プログラミングの用語では、Unicorn型はまったくサブタイプである必要はなくHorse、次のような操作が必要です。
-- Convert any unicorn-description of into a horse-description that
-- describes the same unicorns.
toHorse :: Unicorn -> Horse
-- If the horse described by the given horse-description is a unicorn,
-- then return a unicorn-description of that unicorn, otherwise return
-- nothing.
toUnicorn :: Horse -> Maybe Unicorn
そして、以下のtoUnicorn正反対である必要がありますtoHorse。
toUnicorn (toHorse x) = Just x
HaskellのMaybeタイプは、他の言語で「オプション」タイプと呼ばれるものです。たとえば、Java 8 Optional<Unicorn>タイプは、an Unicornまたはnoneです。2つの選択肢(例外をスローするか、「デフォルト値またはマジック値」を返す)は、オプションタイプに非常に似ていることに注意してください。
基本的に、ここでやったことは、サブタイプや継承を使用せずに、タイプと機能の観点からIS-A関係の概念を再構築することです。私がこれから取り上げるのは:
- モデルには型が必要
Horseです。
Horse型は、任意の値がユニコーンを記述しているかどうかを明確に決定するのに十分な情報を符号化する必要があります。
Horseタイプの一部の操作では、その情報を公開して、タイプのクライアントが特定Horseのユニコーンであるかどうかを確認できるようにする必要があります。
- この
Horseタイプのクライアントは、実行時にこれらの後者の操作を使用して、ユニコーンと馬を区別する必要があります。
したがって、これは基本的に「Horseユニコーンかどうかを尋ねる」モデルです。あなたはそのモデルに警戒していますが、私は間違っていると思います。Horsesのリストを提供する場合、タイプが保証するのは、リスト内のアイテムが記述するものが馬であるということだけです。したがって、必然的に、それらのどれがユニコーンであるかを判断するために実行時に何かをする必要があります。したがって、それを回避することはできません。それを実現する操作を実装する必要があると思います。
オブジェクト指向プログラミングでは、これを行うためのよく知られた方法は次のとおりです。
Horseタイプを持っている;
- 持っている
UnicornのサブタイプとしてHorse、
- ランタイムタイプリフレクションを、特定の
Horseがであるかどうかを識別するクライアントアクセス可能な操作として使用しますUnicorn。
上記に示した「ものと説明」の角度から見ると、これには大きな弱点があります。
- あなたは何を持っている場合は
Horseユニコーンを記述していないが、インスタンスUnicornのインスタンスを?
初めに戻ると、これは、このIS-A関係のモデリングにサブタイピングとダウンキャストを使用することについて本当に恐ろしいことだと思います。ランタイムチェックを行う必要はありません。タイポグラフィを少し乱用し、HorseそれがUnicornインスタンスであるHorseかどうかを尋ねることは、それがユニコーンであるかどうかを尋ねることと同義ではありません(ユニコーンHorseでもある馬の説明であるかどうか)。HorsesクライアントがHorseユニコーンを記述するを構築しようとするたびにUnicornクラスがインスタンス化されるように、構築するコードをカプセル化するためにプログラムが非常に長い時間をかけていない限り、そうではありません。私の経験では、プログラマがこれを慎重に行うことはめったにありません。
したがって、HorsesをUnicornsに変換するダウンキャスト以外の明示的な操作があるアプローチを採用します。これは、次のHorseタイプのメソッドのいずれかです。
interface Horse {
// ...
Optional<Unicorn> toUnicorn();
}
...または、外部オブジェクト(「馬がユニコーンであるかどうかを通知する馬上の別個のオブジェクト」)の場合もあります。
class HorseToUnicornCoercion {
Optional<Unicorn> convert(Horse horse) {
// ...
}
}
これらのどちらを選択するかは、プログラムの編成方法に依存します。どちらの場合も、Horse -> Maybe Unicorn上記の操作と同等であり、異なる方法でパッケージ化するだけです(Horseタイプに必要な操作に波及効果があることは明らかです)クライアントに公開します)。