目次
オブジェクト指向って何?
オブジェクト指向とは、プログラミング設計や実装の考え方の一つです。
現在はオブジェクト指向のプログラミング言語が主流になっており、プログラマであればさまざまな所でオブジェクト指向という言葉を聞いたことがあると思います。
「オブジェクト指向を説明してください」といわれ完璧に説明できる人はどのくらいいるのでしょうか。意外に少ないかもしれません。そのくらいオブジェクト指向という言葉は、曖昧な言葉なのです。
そのため、技術書やWebサイトで説明しているオブジェクト指向の説明は皆表現がバラバラだったりします。ただし基本的な考え方は同じです。
本記事では、分かったつもりでも意外に難しい「オブジェクト指向」について紹介しています。
スポンサーリンク
なぜ「オブジェクト指向」が必要なの?
オブジェクト指向の概念がなくてもプログラムを完成させることは可能です。
ただし、ソースコードの量が膨大になったり、保守性が悪くなります。
例えば、次のように3種類の車のプログラムを作成するとします。
車の種類 | 特徴 |
〇〇車 |
|
△△車 |
|
□□車 |
|
オブジェクト指向の概念を取り入れなくても、「○○車」専用のプログラム、「△△車」専用のプログラム、「□□車」専用のプログラムが出来上がり、問題なく動作します。
しかし、よく見ると「○○車」「△△車」「□□車」は基本的には似たような機能です。また「車の基本機能」に関しては全く同じです。
「○○車」専用のプログラム、「△△車」専用のプログラム、「□□車」専用のプログラムと別々に作成してしまうと、全く同じ内容のソースコードがあちこちに書かれてしまい、作業効率が非常に悪く、ソースコードの量も膨大になってしまいます。
また「車の基本機能」が変更になった場合、「○○車」「△△車」「□□車」のソースコードを修正する必要があり、保守性も悪くなります。
このような問題を解決する為に「オブジェクト指向」という考え方があり、機能ごとに部品化することで、作業効率と保守性の向上を目的としています。
スポンサーリンク
オブジェクト指向って具体的にどういうモノ?
オブジェクト指向の基本的な考え方は、機能を部品化し、部品化された機能を組み合わせることで製品を完成させる考え方です。
先ほど説明した車を例に考えると、以下のように部品化できます。
まずは「車の基本機能」である「エンジン」「ブレーキ」「燃費」「外観」「内装」を部品化、部品化することで「○○車」「△△車」「□□車」が自由に使えます。
「○○車」は基本機能以外に以下の特徴があるので、特徴がある部分だけを独自実装するのです。
- 高性能のエンジンを搭載
- 車の外観デザインは赤
- 燃費が良い
「△△車」「□□車」も「○○車」と同じように、基本機能はそのまま使い、独自部分だけを独自実装します。
このように機能を部品化することで、共通的な機能を使いまわし、作業効率が上がります。また同じソースコードをあちこちに書く必要がありません。
そして「車の基本機能」が変更になった場合は、共通になっている部品を直せば「○○車」「△△車」「□□車」すべてに反映されるので、保守性もアップします。
オブジェクト指向の具体的な考え方
継承
継承とは「親」となるクラスの機能を「子」となるクラスが引き継いで使うことをいいます。
例えば、車の例で考えると「車の基本機能」である「外観」は、同じ車種であれば基本的にほとんど同じです。車の外観には、以下のような機能があるとします。
外観の機能:「ガラス」「ライト」「ナンバー」「タイヤ」「色」
そして「外観の色」だけが違う、3種類の車の機能を作るとします。
- 「○○車」:外観は赤
- 「△△車」:外観は白
- 「□□車」:外観は黄色
この場合、「○○車」「△△車」「□□車」共に「色」だけを独自実装すればよいのです。
・親クラス
package car;
/**
* 車の外観基盤クラス
*/
public class BaseAppearance {
/**
* ガラスの処理開始
*/
protected void exexuteGlass() {
// ここにガラスの基本的な処理を書く(処理内容は省略)
}
/**
* ライトの処理開始
*/
protected void exexuteLight() {
// ここにライトの基本的な処理を書く(処理内容は省略)
}
/**
* ナンバーの処理開始
*/
protected void exexuteNumber() {
// ここにナンバーの基本的な処理を書く(処理内容は省略)
}
/**
* タイヤの処理開始
*/
protected void exexuteTire() {
// ここにタイヤの基本的な処理を書く(処理内容は省略)
}
/**
* 色の処理開始
*/
protected void exexuteColor() {
// ここに色の基本的な処理を書く(処理内容は省略)
}
}
・子クラス
package car;
/**
* ○○車の外観クラス
*/
public class SampleChildAppearance extends BaseAppearance {
/**
* 色の処理開始
*/
@Override
protected void exexuteColor() {
// ここに○○車独自の色の処理を書く(処理内容は省略)
}
}
上記例では親クラスである「BaseAppearance」クラスには、「ガラス」「ライト」「ナンバー」「タイヤ」「色」の処理が実装されています。
「SampleChildAppearance」クラスは「BaseAppearance」クラスをextends(継承)することで、親クラスである「BaseAppearance」の機能を、そのまま使えます。これが継承です。
また、「○○車」は「色」だけ独自実装すればよいので、「exexuteColor」メソッドをOverride(親クラスで定義しているメソッドを子クラスでも定義することで、処理を上書きする)し、子クラスである「SampleChildAppearance」の「exexuteColor」メソッドに独自処理を実装するだけです。
スポンサーリンク
ポリモーフィズム
オブジェクト指向の考え方で一番、理解するのが難しいのが「ポリモーフィズム」
日本語では多態性・多様性などと訳される言葉。難しく感じる「ポリモーフィズム」ですが、簡単にいうと「異なる動作を同じ処理で実現」するという考え方です。
・インタフェース
package animal;
/**
* 動物のインタフェース
*/
public interface IAnimal {
/**
* 鳴く
*/
void cry();
}
・犬クラス
package animal;
/**
* 犬クラス
*/
public class Dog implements IAnimal {
/**
* 鳴く
*/
@Override
public void cry() {
System.out.println("わん");
}
}
・猫クラス
package animal;
/**
* 猫クラス
*/
public class Cat implements IAnimal {
/**
* 鳴く
*/
@Override
public void cry() {
System.out.println("にゃー");
}
}
・メインクラス
package animal;
public class Start {
/**
* 処理実行
*/
public static void main(String[] args) {
IAnimal cat = new Cat();
IAnimal dog = new Dog();
cat.cry();
dog.cry();
}
}
上記の例の通り、「IAnimal」インタフェースをimplementsしている「Cat」クラスと「Dog」クラスの「cry」メソッドを呼び出すと、「Dog」クラスは「わん」、「Cat」クラスは「にゃー」となり、結果が異なります。
このように、同じ処理で異なる動作を実現する考え方を「ポリモーフィズム」といいます。
カプセル化
カプセル化とは、機能をまるでカプセルに入れているみたいに機能ごとに処理をまとめ、その内容を隠蔽することをいいます。
具体的には「アクセス修飾子」(Javaでいうとprivateやpublicなど)と呼ばれるモノで、その機能を公開するのか、非公開にするのかを明確に制御します。
カプセル化した機能をどのレベルまで公開するかを明確にすることで、外部から想定外な使い方をされる心配はなく、無駄な障害を防げます。