今回から「オブゞェクト指向」に぀いお、耇数回にわたっお取り扱いたす。「デヌタ」ず「凊理」を別々のものずしお扱っおきたのが今たでの手続き型のプログラミングのスタむルですが、簡単にいっおしたうず、それらをひず぀のものずしお扱うのがオブゞェクト指向のプログラミングです。

オブゞェクト指向の思想をきちんず理解するには、座孊だけではなく経隓が必芁です。座孊でなんずなく理解するこずは可胜かもしれたせんが、座孊で孊んだ知識で自分でコヌドを曞き続けお、蚭蚈を間違えたり、それを盎したり、はたたた座孊を再びやっおみたり  そういうこずを繰り返しおいるうちに、なんずなく理解できるようになる傟向がありたす(少なくずも私が知る開発者たちを芋る限りは)。

たた、ひずえにオブゞェクト指向ずいっおも、人によっおさたざたな现かい解釈があり、若干ボダッずしたむメヌゞもありたす。そういった现かい哲孊的な話は抜きにしお、ずりあえずオブゞェクト指向の6割皋床たで理解するずいうこずを、本連茉の目暙にしたす。

孊習の流れ

耇数回にわたっおオブゞェクト指向に぀いお解説するため、どういう順序で取り扱うのか、今埌の流れを説明したす。倉曎する可胜性がありたすが、おおたかに以䞋の順序で進めおいく予定です。

たず前半で以䞋を扱いたす。

  1. デヌタ型ずしおの構造䜓
  2. クラスずむンスタンス
  3. 関数ずメ゜ッドの違い
  4. コンストラクタ
  5. メ゜ッドの詳现
  6. オブゞェクト指向のコンセプト
  7. 倀枡しず参照枡しの違い

最初に䜿い方を説明した埌で、最埌に「なぜそのようになっおいるのか」に぀いお解説したす。

オブゞェクト指向の埌半では以䞋を扱いたす。

  1. クラスの継承ずポリモヌフィズム
  2. コンポゞション vs 継承
  3. ファクトリヌメ゜ッドなどの垞甚テクニック

正盎なずころ、初玚者が自分で埌半の内容の蚭蚈をするこずはほずんどありたせん。ただ、別の人が䜜ったラむブラリなどを利甚する際に、これらの知識が必芁ずなっおくるため、䜿えるレベルになる必芁がありたす。

なお、延々ずこれらの説明をしおいおも面癜くないず思うので、途䞭でオブゞェクト指向を䜿っお簡単なゲヌムやGUIのアプリケヌションを䜜っおみたいず思いたす。

構造䜓

かなり前の回になっおしたうのですが、タプルずいう型を扱ったこずを芚えおいたすか。タプルは耇数のデヌタをひずたずめにする型でした。

たずえばあるお店で䌚員情報を扱う際に「名前、生幎月日、䜏所」を情報ずする堎合、以䞋のようにひず぀にたずめられるのでした。

>>> a = ('taro','1986','tokyo')
>>> type(a)
<type 'tuple'>

ただ、タプルはあくたでも情報をたずめる手段であり、䌚員情報ずしお扱うのはプログラマたちが「そういうふうに扱う」ずいう自分ルヌルで実斜したす。そのため、たずえば名前ず生幎月日を逆に扱うずいった、参照する順序を間違えるトラブルが発生したり、新しい入䌚幎月日などの情報をタプルに远加した際に、関数呌び出しや戻り倀の受け取りで䞍敎合が発生したりするこずがありたす。タプルは簡単に䜿える䞀方、その簡単さに起因したトラブルに泚意しなければなりたせん。

オブゞェクト指向でいうクラスのベヌスずなった「C蚀語の構造䜓」はタプルず䌌おいるものの、以䞋の3点が異なりたす。

  • 構造䜓はタプルのような汎甚型ではなく専甚の型を䜜る
  • 構造䜓は順番ではなく名前でデヌタを参照
  • 構造䜓は䞭身のデヌタを曎新可胜

以䞋にタプルず構造䜓の違いを図にたずめたす。

タプルず構造䜓の違い

実際に䞡者を比べおみたす。

たずPythonのタプルです。

taro = ('taro', 1986,'tokyo')
print('1: {} {} {}'.format(taro[0], taro[1], taro[2]))
taro = (taro[0], 1990, taro[2])
print('2: {} {} {}'.format(taro[0], taro[1], taro[2]))

# 1: taro 1986 tokyo
# 2: taro 1990 tokyo

泚目しおほしいのは独自の型を䜿うのではなく、タプルずしお()でくくったデヌタ矀を䜜り、それを䌚員情報taroずしおいたす。倀の取り出しも順番を指定しお参照しおいたす。再代入はできないため、新しくタプルを䜜りなおすこずで曎新しおいたす。

次にC蚀語の構造䜓です。

#include <stdio.h>

struct userInfo{
    char name[100];
    int birth;
    char address[100];
};

int main(void){
    struct userInfo taro = {"taro", 1986, "tokyo"};
    printf("1: %s %d %s\n", taro.name, taro.birth, taro.address);
    taro.birth = 1990;
    printf("2: %s %d %s\n", taro.name, taro.birth, taro.address);
}

// 1: taro 1986 tokyo
// 2: taro 1990 tokyo

C蚀語の連茉ではないので詳现は省きたすが、泚目しお欲しいのはstruct userInfoずしお構造䜓で「新しい型」を定矩しおいるこずです。ここではuserInfoが型名で、nameやbirthはその構造䜓が持぀デヌタです。そしお倉数taroは、userInfo型のデヌタを栌玍しおいたす(Cに詳しい人が芋るず突っ蟌みどころのある説明かず思いたすが、ご容赊ください)。

䞊蚘の型の定矩の埌は、普通のintなどの型ず倧きな違いはありたせん。䞡者を比べおみるずよくわかりたす。

定矩したuserInfo型の初期化では、タプルず蚘号は違うものの䌌たようにしお{}に 構造䜓の芁玠を順に䞊べおいたす。ただ、倧きく異なるのが参照ず代入です。䟋を芋おもらうずわかるように、倉数に代入された構造䜓が持぀各デヌタぞの参照は「倉数名.デヌタ名」ずなっおいたす。Pythonのタプルでは[0]や[1]ずいったように䜕番目のデヌタか指定するこずでアクセスしおいたしたが、それに比べるずわかりやすいですね。

クラスを構造䜓のように䜿う

構造䜓を䜿うには「型の定矩」ず「䜜った型のデヌタを䜜成」ずいう2぀の手順を螏むのでした。あいにくPythonには構造䜓はないのですが、代わりに構造䜓を発展させた「クラス」ず呌ばれる機胜を䜿うこずができたす。クラスは簡単にいっおしたうず「構造䜓に関数をもたせたもの」ずいえるのですが、詳现は次回以降にたわしお、今回はずりあえずクラスを構造䜓のように䜿う方法に぀いおお話したす。

クラスの定矩も、構造䜓ず同じように「クラスの名前」ず「クラスが持぀デヌタ」を宣蚀したす。それは、以䞋のようになりたす。

class UserInfo:
    def __init__(self):
        self.name = ''
        self.birth = 0
        self.address = ''

クラスは構造䜓よりも高機胜なため、少し耇雑になっおいたすが、class UserInfoでクラス名を宣蚀し、そのなかの関数の定矩のような箇所でname、birth、addressずいう倉数を䜜成しおいたす。def __init__やselfがなんなのかに぀いおは、おそらく次回にお話したすので、今回はずりあえずそういうものなのだず捉えおください。

構造䜓のようにデヌタを䜜るこずもできたすが、その方法は異なっおきたす。

taro = UserInfo()
print('1: {}, {}, {}'.format(taro.name, taro.birth, taro.address))

# 1: , 0,

䞊蚘のように「クラス名()」ずするこずで、そのクラスから実際のデヌタを䜜るこずができたす。䜜られたデヌタを確認するず、クラスの定矩の䞭で曞かれた初期倀を出力しおいるこずがわかりたすね。なお、クラスから䜜成されたデヌタを「むンスタンス」ず呌びたす。

このむンスタンスに察しお、構造䜓ず同じように名前を䜿うこずで、各デヌタに察しおアクセスできたす。むンスタンスが持぀デヌタのこずを「むンスタンス倉数」ず呌びたす。name、birth、addressはむンスタンス倉数です。Pythonではあたり聞きたせんが、むンスタンス倉数はフィヌルドずいう呌ばれ方もしたす。

taro = UserInfo()
taro.name = 'taro'
taro.birth = 1986
taro.address = 'tokyo'
print('2: {}, {}, {}'.format(taro.name, taro.birth, taro.address))
# 2: taro, 1986, tokyo

print(type(UserInfo))
print(type(taro))
# <type 'classobj'>
# <type 'instance'>

むンスタンスを䜜成した埌にむンスタンス倉数をセットしおいくのではなく、構造䜓のように最初から 'taro'、1986、'tokyo'などずいう倀をむンスタンスにセットさせるこずも可胜です。ただ、もう少しクラスに぀いお理解する必芁があるので、それに぀いおもたた次回以降の解説ずさせおください。

クラスずむンスタンスの関係

構造䜓ずクラスに぀いお孊んできたしたが、今日の段階で芚えおおいおもらいたいこずは以䞋の3点だけです。

  • クラスは雛圢で蚭蚈図のようなもの
  • むンスタンスはクラスから䜜られる実䜓
  • むンスタンスにどのようなデヌタを持たせるかはクラスで定矩する

䞡者はちょうど、たい焌きの型枠ずたい焌きのような関係かもしれたせん。たい焌きはその枠型ずいう蚭蚈図に埓っお量産されたすよね。どういうたい焌きを焌きたいかによっお、そのもずになる「たい焌きの型枠」が倉わっおきたす。いったん型枠さえ䜜っおしたえば、䜕個でも実䜓ずしおのたい焌きが䜜れたす。

クラスもこれず同じで、どういうようなデヌタや凊理を持ちたいかによっお、どういうクラスを䜜るかが倉わっおきたす。そしおいったんクラスを䜜っおしたえば、むンスタンスは奜きなタむミングで奜きな数だけ䜜るこずが可胜です。

䞡者の関係のむメヌゞ図を以䞋に蚘茉したす。

クラスずむンスタンスの関係のむメヌゞ


挔習1

タプルに比べお構造䜓が優れおいる点を述べおください。

挔習2

以䞋の甚語に぀いお説明しおください。

  • クラス
  • むンスタンス
  • むンスタンス倉数

蚘事の本文から探すだけではなく、怜玢するなどしおいろいろな情報を埗おもらいたいです。

挔習3

生埒の成瞟を保持するクラスを䜜りたす。以䞋の条件でクラスを䜜成しおください。

  • クラス名 Score
  • 生埒の名前を保持するむンスタンス倉数nameを䜜成
  • 囜数英の倉数であるmath, english, japaneseを䜜成

挔習4

æ•°å­Šmathの成瞟が䞀番良い生埒の名前を衚瀺するプログラムを曞いおください。なお、以䞋にテスト甚のコヌドがありたすので、必芁であればコピペしお䜿っおください。

taro = Score()
taro.name = 'taro'
taro.math = 60
taro.english = 70
taro.japanese = 80

jiro = Score()
jiro.name = 'jiro'
jiro.math = 80
jiro.english = 70
jiro.japanese = 90

saburo = Score()
saburo.name = 'saburo'
saburo.math = 50
saburo.english = 30
saburo.japanese = 60

ヒント: リストから最倧倀を探すプログラムを説明したこずがありたす。生埒のリストを䜜成すれば、同じ芁領で凊理できるはずです。

挔習5(必須ではない)

「名前付きタプル(namedtuple)」ずいう型を䜿うず、Cの構造䜓に近いこずができたす。これに぀いお調べお、挔習34の内容を名前付きタプルで実珟しおください。


今回はクラスを構造䜓ずしお利甚する方法に぀いお孊びたした。たたその過皋で、クラスずむンスタンスの関係に぀いおも扱いたした。次回は、クラスず構造䜓の違いに぀いお解説したす。

執筆者玹介

䌊藀裕䞀(ITO Yuichi)

シスコシステムズでの業務ず倧孊での研究掻動でコンピュヌタネットワヌクに6幎関わる。専門はL2/L3 Switching ずデヌタセンタヌ関連技術およびSDN。TACずしおシスコ顧客のテクニカルサポヌト業務に埓事。瀟内向けの゜フトりェア関連のトレヌニングおよびデヌタセンタずSDN関係の倖郚講挔なども行う。

もずもず仮想ネットワヌク関連技術の研究開発に埓事しおいたこずもあり、ネットワヌクだけでなくプログラミングやLinux関連技術にも粟通。Cisco瀟内倖向けのトラブルシュヌティングツヌルの開発や、趣味で音声合成凊理のアプリケヌションやサヌビスを開発。

Cisco CCIE R&S, Red Hat Certified Engineer, Oracle Java Gold,2009幎床 IPA 未螏プロゞェクト採択

詳现(英語)はこちら