2018-10-27子どもの頃,話が「抽象的すぎる」とよく言われていて,あまりよくない意味に捉えていたときもありました。よいプログラムを書く際に「抽象化」はとても大切なことの一つです。抽象度を上げたり,下げたり(具体化したり)を自在にできる考え方を身に付けることは,システム開発のみならず,様々な場面で有効です。今回はプログラミングにおける抽象化についてです。継承やインターフェイスなどのオブジェクト指向言語の機能をはじめて覚えたら,何かと使いたくなってしまうものです。練習用のプログラムならまだしも実践でやたらめったら使ってしまうと大変なことになりかねません。その機能があるのか理解して,適切に使いこなしましょう。適用可能範囲ある要求(シグネチャ)に対して,適用できるものと適用できないものを考えてみます。シンプルな関数を見てみましょう。文字列を渡すことはできません。整数でなければなりませんし,閾値もあります。1これは数値や文字列だけでなくクラスや構造体であっても同じです。持ち,2次元ベクトルを表現します。関数は,これを2つ受け取って2点間の距離を計算します。クラスのインスタンスしか適用することができません。引数の型によって受け取ることができるデータの条件を限定しているわけですね。Y(を持っているオブジェクト)」というだけです。同じ意味を持つのであれば,別のクラスのオブジェクトであってもよさそうなものです。2)を使うことで解決できます。インターフェイスとは,実装はさておき「属性(プロパティ)および振る舞い(メソッド)はこうなっていますよ」という定義だけを外部に宣言する仕組みです。そのインターフェイスを引数や戻り値に取るということは,「ある実装」ではなく「ある宣言」を受け取る,あるいは返却するという意味になります。インスタンスを渡したときの挙動は先ほどと同じになります。実装させることによって,このインスタンスを渡しても同じように結果を得ることができるようになります。アクセスすることはできません。Y(を持つオブジェクト)」なので,適用する側はそれさえ守れば何を渡してもよいですし,逆に適用される側はそれ以外の要素に言及してはいけない(アクセスしてはいけない)ということです。持っている同じ型のオブジェクト」に限定したい場合は次のようにします。組み合わせを渡すことはできなくなります。持つオブジェクトに対して処理を行いたい別の関数があったとします。クラスに実装させてみましょう。この場合,既に対象の要素は持っていたので実装は変えることなく,特定の性質についての定義だけを追加することができます。実装した別のクラスのインスタンスを適用することもできるようになります。実装しているクラスであれば何でも受け入れることができます。要求している関数にも適用することができます。インターフェイスを使うことで柔軟なプログラミングが可能になります。このように,同一のシグネチャ(呼び出し方)によって複数の型を受け取れる性質を「ポリモーフィズム(Polymorphism)」といいます。多態性,多相性などとも訳されます。これは,振る舞い(メソッド)だけでなく,属性(プロパティ)についても同じです。オブジェクト指向ではプロパティもカプセル化されるという点で実質的にメソッドと変わりがありません。おける抽象化とは“適用可能な範囲をコントロールする”ことです。言語による違いインターフェイスの機能は言語によって実装が異なります。ような静的型付け言語では,その型が何者であるのかがコンパイル時に明らかになっていなければなりません。どのような属性や振る舞いを持つのかという特性だけでなく,どのような継承関係なのかも必要です。インターフェイスの実装もその一部ですので,明示的に実装する必要があります。どこまで記述する必要があるかどうかは,言語の型推論能力などにも依ります。動的型付け言語では,実行時に型を判断します。インターフェイスという機能はありません。インターフェイスに限らず,受け入れる側が引数の型を強制する手段がありません。必要ではなく,そのオブジェクトの属性や振る舞いの有無だけが重要です。「ガーガー」鳴くなら,生物のアヒルでも機械のアヒルでもいいわけです。typing)」です。ある意味,全面的に多態性を受け入れている(それを表現するためのコーディングも不要)ともいえますが,型の不整合は実行時にならないと分からないため,型の安全性という面では懸念があります。継承とインターフェイスオブジェクト指向には「継承」という概念があります。多態性を表現するために,(クラスの)継承を用いることもできますが,これは適切ではありません。継承とインターフェイスでは動機が異なります。(クラスの)継承というのは,サブクラス(子クラス)がスーパークラス(親クラス)からその特性(属性や振る舞い)を受け継ぐことです。受け継いだ特性は継承元のクラスと同じように利用できます。継承は,主に**「汎化(一般化)」と「再利用」**を目的としています。複数のクラスで同じ意味のプロパティや同じ実装のメソッドがあった場合,それぞれに記述してしまうと冗長的コードになってしまいます。同じ意味のコードが散在していると,機能の修正や変更があったとき,すべての箇所に同じように適用しなければならず,手間なだけでなく,漏れる危険もあります。同じコードは重複して書かずに,1か所にまとめておきたいところです。ソースのコピペは厳禁です。1人が負うべき責務は1つであると同時に,1つの責務を担当する人は1人であるべきです。いうわけで,1か所にまとめるためにそれぞれのクラスから共通する特徴を抽出しようというのが**「汎化」**です。共通する部分をスーパークラスにまとめておき,継承することで各サブクラスで利用できるようにします。同じ共通機能を必要とする別のクラスを追加したい場合には,新たにサブクラスとして追加することでその機能を利用することができます。既存のコードを直接変更しないので元のクラスを参照しているコードへの影響がなく安全に機能追加ができます。このように,既存のコード(資産)を**「再利用」**して,変更や拡張を簡単にできるようにしているわけです。継承は特徴を引き継ぐため,スーパークラスを引数に取る関数に対してサブクラスを渡すことができます。インターフェイスと同じような使い方ができます。この場合は少し意味が違います。なく「実装」を共通利用するのが目的です。異なる実装に対応することです。このように,これらは基本的な動機が異なります。クラスのように複数のインターフェイスを実装させるということを継承で行うのは難しいという面もあります。多重継承を禁止しており,インターフェイスはその代替としての役割も存在理由のひとつです。多態性を表現するには,“継承ではなくインターフェイスを使う”ようにしましょう。処理の再利用そもそもクラスの継承には問題があるため,基本的に利用するべきではありません。継承を利用した場合,クラス同士の結合度が高まり,柔軟性が低下します。サブクラスは直接的にスーパークラスに依存してしまうため簡単には変更できません。汎化および再利用は継承を用いずとも可能です。単純にある機能を共通利用したいだけであれば「継承」ではなく「使用」にしましょう。