Javaへの愛が溢れています

Java のこととか、その他の言語のこととか、便利なことを色々書いていきたいです。

オブジェクト指向について超わかりやすく解説してみた

オブジェクト指向って何だろう


Javaといえばオブジェクト指向言語です。
ではオブジェクト指向って何なんでしょう?
クラスがあって、フィールドがあって、継承ができて・・・
ぼんやりとはわかっていても、詳しく説明を求められると、困りませんか?
今回は、Javaにおけるオブジェクト指向についてわかりやすく解説していきます。




そもそもオブジェクトって何?


オブジェクト指向とは、システムをオブジェクト同士の相互作用とみなす考え方のことです。
じゃあオブジェクトって何でしょう。


じゃあここで、まわりを見渡してみましょう。
あなたの前には、このブログを見ているパソコン携帯電話がありますね。
あなたがいる場所は部屋でしょうか。はたまたどこかの路上でしょうか。電車バスの中かもしれませんね。
あなたは今どんなを着ていますか? はどうでしょう?
そもそもあなたは誰ですか? 名前は? 歳は?


そういうあらゆるものオブジェクトです。
この世のあらゆるものは、オブジェクトとみなすことができます。
目に見えるものや肌で感じることができるものはもちろん、
概念(「数学」「宗教」etc...)や、感情(「嬉しさ」「悲しみ」etc...)なども、オブジェクトとすることができます。


Javaにおいては、オブジェクトはそれぞれ、
どういうものなのか(フィールド)」と、「何ができるのか(メソッド)」という2つの要素で構成されます。


■ フィールド


フィールドとは、それが「どういうものなのか」ということを表す、
つまり他の同種類のオブジェクトとの差別化を測るものです。


車を思い浮かべてください。
その車のは何色ですか? メーカーはどこですか?
タイヤの数はいくつありますか? 持ち主は誰ですか?
そういう、他の同種類のオブジェクト(この場合、他の車)との違いを表したものが、フィールドです。


他にもいくつか例を考えてみましょう。
人間なら、背の高さ、髪の色、性格や、着ている服や持っているものがフィールドです。
机なら、色・材質や付属している引き出しをはじめ、他にも上に乗っているものなどもフィールドとして扱うことができます。


プログラム中でフィールドは変数として表現されます。


■ メソッド


メソッドとは、「何ができるのか」ということを表す関数です。
これはわかりやすいですね。
例えば人間なら、歩いたり、走ったり、食べたり、色んなことができます。


では、机は何ができるでしょう?
メソッドには、「自発的に行うもの(privateメソッド)」と、「他のオブジェクトでもできるもの(publicメソッド)」があります。
机のメソッドとしては、publicメソッドとして、「机の上にものを置く」や、「引き出しの中にものを入れる」などが挙げられます。
これは机が自発的に行うことはありませんが、例えば人間などが机に対して行います。


プログラム中でメソッドは、関数として表現されます。
「何かをする時に必要なもの」(例えば「机の上にものを置く」場合は、「上におくもの」)は、関数の引数として与えられます。
もし「何かをした結果を知りたいこと」(例えば「机の上にものを置く」場合に、それが成功したか、もしくは既にものがある等の原因で失敗したかどうか)があれば、関数の返却値として取得できます。


クラスとインスタンス


実際にプログラムを書く時は、オブジェクトの型を「クラス」として書きます。
クラスの中には、オブジェクトがどのようなフィールドやメソッドを持っているかを記述します。

そして、実際にプログラム内でオブジェクトを使う時は、クラスを具体化させて使います。
例えば Person というクラスがあるなら、

Person hanako = new Person("Hanako");

のように具体化させます。
この、クラスを具体化させたもの(ここでは hanako)を、「インスタンス」と呼びます。


f:id:zozozozozo:20131126165730p:plain



オブジェクトの相互関係


はじめに、『オブジェクト指向とは、システムをオブジェクト同士の相互作用とみなす考え方のこと』と説明しましたね。
相互作用とは何でしょう。


ある2つのオブジェクトA,Bについて考えます。
これらの相互作用の例としては、以下のようなものが挙げられます。

  • AがBを作成する
  • AがBを持っている
  • AがBのメソッドを呼び出す
  • AがBを汎化する(他のクラスを extends する)
  • AがBを実現する(インターフェースを implements する)

これらは一例です。
また、汎化と実現に関しては少しややこしいので、またの機会があれば記事にしようと思います。


オブジェクト指向言語と継承


とある建築家が、Aさんの家を設計し、建築しました。
その家を見て気に入ったBさんがその建築家に、
「Aさんの家と全く同じ家を建てて欲しい。ただし、庭に池を追加して欲しい」
という風に依頼しました。
あなたが建築家ならどうしますか? 1から設計図を書きますか?
おそらく、Aさんの家の設計図をコピーし、そこに池を付け足すでしょう。


プログラムでは、これと同じようなことがよくあります。
このプログラムにこれこれの機能を追加したいと思うことが多々あります。
これを簡単にできるようにしたのが、オブジェクト指向における「継承」です。
Aさんの家を継承した家を作り、そこに池を追加すると、
Aさんの家と同じものを全て扱えて、かつ池という追加要素も扱えるBさんの家を簡単に作ることができます。

このとき、親となったAさんの家を「スーパークラス」、Bさんの家を「サブクラス」と呼びます。


f:id:zozozozozo:20131127131312p:plain


Java では、Aさんの家クラス HouseOfAを継承する
Bさんの家クラスHouseOfBを作りたい場合、以下のように書くだけで継承できます。

class HouseOfB extends HouseOfA{
   // 中略
}

簡単ですね!


■ 継承のうまみ


中にはこう思う人がいるかもしれません。
「そんなややこしいことしなくても、コンピューターにはコピー&ペーストという素敵な機能があるじゃないか!」


わざわざ継承という機能を使うメリットは多くありますが、
一番大きいのが管理が楽になるということでしょう。


例えばAさんの窓でドアが開かない、雨漏りがするなどの不具合が起こったとします。
コピペで済ませていた場合、Aさんの家を直してもBさんの家は直りません
Bさんの家でも、Aさんの家でバグが起こった場所と同じ場所を修正する必要があります。
もしコピペ先が複数あるなら、修正も面倒ですし、
そもそも直さなければいけない箇所の管理が困難でしょう。

継承を使っていた場合、Aさんの家を直しさえすれば、自動的にBさんの家も直ります
同じようにAさんの家クラスを継承しているものがいくつあっても、
一箇所直すだけで良いので、かなり修正も管理も楽になります。


オブジェクト指向言語カプセル化


カプセル化とは、オブジェクト指向プログラミングの特徴のひとつであり、
「他のクラスに見せたくない」もしくは「他のクラスから見えなくても良い」フィールドやメソッドを、private に設定すると見えなくできるということです。

説明が難しいので長くなってしまいますが、なるべくわかりやすいように説明してみます。


カプセル化のうまみは、「分業」にあります。

例えば、あるメーカーAの製品に関して、サポートデスクがあるとします。
Aでは、先日サポートデスクの担当者が代わり、
それに伴ってサポートデスクの電話番号が変わってしまいました。
なので、お客様に新しい電話番号を伝えなくてはなりません。
これはとても大変な作業ですね。

プログラムでも、同様のことが起こりえます。
例えばフィールド型を変えたりした場合です(int型→double型など)
何の対策もしていないなら、そのフィールドを参照していたプログラム全てを書き換えなくてはなりません
プログラムが広く公開されていた場合、他の人にまで影響が及ぶ可能性があります。


ここで先ほどのカプセル化の説明を思い出してみてください。
カプセル化では、「他のクラスに見せたくない」もしくは「他のクラスから見えなくても良い」フィールドやメソッドを、private に設定すると見えなくできます。

先ほどの例で言えば、「サポートデスクの担当者」なんて、顧客にはどうでもいい情報です。
ましてサポートデスクの担当者が変わっただなんて、「でっていう」って感じです。
質がよくなることより、今までと同じように使える(=同じ電話番号でサポートデスクにつなぐことができる)ことが大事なのです。

プログラムでも一緒です。
内部のことなんてどうでもいいのです。
多くの場合、ちょこっと便利になるより、不便なままでも良いのでプログラムの書き換えをしない方が良いのです。

そこで、プログラムのカプセル化を行い、
内部がどう変わろうと、外から見れば一緒」というプログラムを作成するようにします。


具体的には、外からアクセスする可能性のない部分は private にして隠します。
外からは public に指定されたフィールド・メソッドのみにアクセスできます。
(protected というのもありますが、ここでは置いておきます)
一度公開したプログラムを改変する時は、public の部分の外から見た挙動(引数・返却値)は絶対に変えないようにします。
そうすることで、プログラムの汎用性を高めます。

メーカーの例では、外から見えるサポートデスクの電話番号という情報は絶対に変えないようにし、
内部で、古い電話番号にかかってきた電話を自動的に新しい電話番号に転送する仕組みを作ると良いですね。



カプセル化の利点は他にもあります。
private に設定したフィールドは絶対に外部から書き換えられることがないという点です。
勝手にいじられるとまずいパラメータなどは、private に設定し、
その値を取得できる getter関数 を作っておくと良いですね。
また、例えば正の数しか代入してほしくない場合などは、
setter関数を作り、その中で正しい値かの判定をすると、
不正な値の代入を防ぐこともできます。

いずれにせよ先ほどにも言ったとおり、
フィールドの型が変わる可能性等もありますので、
特に理由がなければ、フィールドは private にしておき、
外部からは getter関数setter関数を用いてアクセスする仕様にすると良いでしょう。


まとめ


ここでは、オブジェクト指向についての基本を中心に
オブジェクト指向って何なのか? どんなメリットがあるのか?
をわかりやすく解説しました。
もちろん、オブジェクト指向には他にも特徴・メリットがたくさんありますが、割愛させていただきました。


なお、わかりやすく噛み砕いて書くことを目標にしたため、逆に「詳しくわからない」部分があるかと思います。
そういう時は、Googleの力を活用してください(他力本願ですが)