公開:2019.10.13 17:30:00 更新:2019.10.14 18:41:33 プログラミング総合

オブジェクト指向の簡単な説明 - 継承・カプセル化・多態性

今回はオブジェクト指向プログラミングの重要な概念である「カプセル化」「継承」「多態性」についてです。

カプセル化

だいたいどの仕事でもそうでしょうが、お客さんが知っていないといけない仕事と、お客さんが知らなくて良い仕事があります。
お客さんが知らなくて良い仕事の詳細を隠しておくことはお客さんを混乱させずお店をクリーンに整理された状態に見せるために重要です。
このことを「カプセル化」と言います。

ここに、Bシェルマートというコンビニエンスストアがあるとします。

図1 - Bシェルマート南大塚本店の機能 ※1

図1はBシェルマート南大塚本店のクラス図です。この図にはBシェルマート南大塚本店の業務が箇条書きにしてあります。
この場合、前回の記事で説明した

  • list: 一覧
  • getNewCart: 新しいカート
  • order: 注文
  • doRegister: 会計

はパブリックなメソッドで、お客さんとの対話の為に公開されています。
逆に

  • refresh: 棚の整理
  • report: 収支報告

はプライベートなメソッドで、お客さんには何をやっているのか分からない、知らなくて良いようになっています。
このようにパブリックとプライベートを分けるとお客さんにとって使いやすいサービスを提供できるようになります。

※1 - PlantText- The expert's design toolで作成しました
検索ワード:encapsulation、カプセル化、可視性、visibility、access level、など

継承

Bシェルマートに、本部が運営する直営店と、加盟者が運営するFC店がある場合を考えます。

図2 - Bシェルマート直営店と加盟店 (FC店) の関係図

図2は本部の直営店「Bシェルマート南大塚本店」と、FC店の「西池袋店」「北赤羽店」「東新宿店」の関係図です。
上の図で言いますと各FC店は直営店のサービスを全てこなしつつ、独自のサービスも行っています。

このようなクラス同士の関係を「継承」関係と言います。

関係図を見ますと、「Bシェルマート南大塚本店」は「基底クラス」であり、「西池袋店」「北赤羽店」「東新宿店」は「派生クラス」であることが分かります。
また、各FC店から見ると本店は「親クラス」となり、本店から見てFC店は「子クラス」となります。

検索ワード:inheritance、継承

多態性 (ポリモーフィズム)

オブジェクト指向プログラミングにおいて「多態性」は次のように分類されています。

静的多態性オーバーロード色んな支払い手段を一つの機器で対応
ジェネリクス処理手順の共通化
動的多態性ダックタイピング別ブランドのコンビニでもコンビニはコンビニとなる
オーバーライド (サブタイピング)支店ごとの細かなサービス内容の違い
オーバーローディング

最近は電子マネーがブームとなり、日々色んなカードやアプリが増えてきています。
これらは会社も違いますし、決済方法もチャージ式とか、引き落し式などがあり、別々に管理していたらけっこう大変です。
しかし、レジ前には謎の機械が置いてあり、そこにカードを当てるだけで、どんな種類の電子マネーでもワンタッチで決済ができてしまいます。
このような処理はオーバーロードによる多態性です。

図 - 色んな種類の支払い方法に一つの機器で対応

図は一つの機器、メソッドで別々のクラスの処理に対応していることを表わしています。

下のコードはreaderreadというメソッドで各種支払い方法に対応していることを表わしています。

CardReader reader = convini.getReader(); // コンビニのカード読取機
HogePayCard hogeCard = mySaihu.getHogePayCard(); // 自分の「ほげペイカード」
FugaPayCard fugaCard = mySaihu.getFugaPayCard(); // 自分の「ふがペイカード」
PiyoPayCard piyoCard = mySaihu.getPiyoPayCard(); // 自分の「ぴよペイカード」

// 読取機に「ほげペイカード」を読み取らせる
Coin coins1 = reader.read(hogeCard);

// 読取機に「ふがペイカード」を読み取らせる
Coin coins2 = reader.read(fugaCard);

// 読取機に「ぴよペイカード」を読み取らせる
Coin coins3 = reader.read(piyoCard);

違ったクラスのカードをひとつのメソッドで対応できていることが分かるかと思います。
readメソッドは名前はひとつですが、受け取るクラスによって完全に別のメソッドが実行されています。
しかしユーザにとっては型の違いを考えることなく処理を任せられるので便利です。
このような多態性を別名では「アドホック・ポリモーフィズム (ad hoc polymorphism)」とも言います。

検索ワード:オーバーロード、overloading、ad-hoc polymorphism、static polymorphism、など

ジェネリクス (テンプレート)

できたらやります。

検索ワード:template、generic programming、generics、ジェネリクス、総称型、compile time polymorphism、など

サブタイピング (オーバーライディング)

下の継承図をご覧ください。

図 - サブタイピングの例

(A)のBシェルマートは客にとって「どこかのBシェルマート」を意味しています。
そして全てのBシェルマートではcoffeeメソッドでコーヒーの販売をしなくてはなりません。

南大塚本店のcoffeeメソッドを、西池袋店と北赤羽店がオーバーライドしています。
南大塚本店ではコーヒーはコーヒー専用機による販売ですが、豊洲店ではレジでの直接販売、市ヶ谷店では自動販売機で販売しています。ただ客としては単に缶コーヒーより美味しいコーヒーという分類になっています。

次にお客さんがそれぞれの店に行ってコーヒーを注文したとします。

図 - サブタイピングの例

つまりお客さんは「どこかのBシェルマート」に行ったつもりが、実際にどこに行ったのかによって販売方法が違ったということになります。これがサブタイピングという多態性です。
上の図をコードで表わしてみます。

BShellMart shop; ← どこかのBシェルマート

shop = new BShellMartMinamiOtsuka(); ← 南大塚のBシェルマート
shop.coffee(); → コーヒー専用機による販売

shop = new BShellMartNishiIkebukuro(); ← 西池袋のBシェルマート
shop.coffee(); → レジでの販売

shop = new BShellMartKitaAkabane(); ← 北赤羽のBシェルマート
shop.coffee(); → コーヒー自販機による販売

「どこかのBシェルマート」を目指して歩いていき、入った店によってコーヒー販売のスタイルが違う、同じBシェルマートでも内容が違うということを表わしています。

またこのような多態性は実行してみるまで結果が確定できないので、動的多態性 (dynamic polymorphism)、実行時多態性 (runtime polymorphism) とも言われます。
C++では親クラスのメソッドにvirtual属性を付けることで、子クラスがオーバーライドができるようになり、動的多態になります。Javaではデフォルトで動的多態になっています。

検索ワード: dynamic polymorphism、runtime polymorphism、 subtyping、subtype polymorphism、inclusion polymorphism、動的多態性、実行時多態性、など

ダックタイピング

できたらやります。

検索ワード:duck typing、ダックタイピング

終わりに

いかがでしたか?
オブジェクト指向における重要な概念についてご理解が得られましたでしょうか?

まあ、はっきり言って研究職や、言語設計者にでもならない限り、これらのことはおおまかな分類が頭の中でできれば良いだけだと私は思っています。しかし使いこなせるようになると大きなパワーとなって返ってきますのでぜひマスターすることをおすすめします。