オブジェクトって何? クラス?? 本読んでも、意味が全然分からないっす・・
こんにちは、Zero-Cheeseです。
私は学生の頃プログラムが好きだった事を、社会人になってから思い出し、独学で勉強した者です。
そのプログラムの学習の中で、一番最初につまずいたのが、このオブジェクト指向でした。
セミナー行っても、文法の紹介ばかりで、いまいち分かりませんでした・・
結構、本気でつまずいてしまい、簡単な概念を理解するだけでも、相当時間がかかりました。
皆様は私みたいなヘマをせずに、サクッと乗り越えて頂けたらと思い、記事にしてみました。
本記事の目標は、以下の2点にしました。
- オブジェクト指向が、イメージできるようになる
- オブジェクト指向のコード(Python)を入門レベルで理解する
従来型プログラムの特徴と問題点
従来型プログラムの代表である、「手続き型プログラム」を見ていきます。
「手続き型」プログラムの特徴
このプログラミングの構造は、何も知らない人がプログラムを作って、と言ったらそうなる構造になっています。
この「手続き型プログラム」は、
- 作りたいモノを処理単位に分解して、実装していく
といった、特徴を持っています。
下記イメージです。
これだけでは、ピンとこないので、具体例を考えてみます。
例えば、スクランブルエッグ を作る例を考えてみます。
その場合、ものすごく簡略化して処理手順を書くと、下記のようなイメージなるかと思います。
この処理をプログラムで書く場合、プログラムはあいまいな事が書けないため、厳密に書く事になります。
- プライパンの温度を、○℃にする
- かきまぜるのは、△回/秒で、箸を使って卵をかきまぜる。
等々の、具体的なコードとなります。
「手続き型」プログラムの問題点
このプログラム構造には、以下の問題点があります。
- プログラムの変更が大変
- プログラムを作る上で、気にする事が多すぎて、頭がパンクする。
- せっかく作ったプログラムが、他ソフトで再利用しにくい。
順番に具体例を見てみます。
① プログラムの変更が大変
さきほどのスクランブルエッグを例に見てみます。
普通は、ニワトリの卵ですが、たとえば、ダチョウの卵に変更になったとします。
地球のドラゴンボールから、ナメック星のドラゴンボール位、大きさが変わってるじゃん!!
その場合、プログラムのコードの変更点は、以下の通りになります。
ここで問題点は、1つの変化点(ニワトリの卵→ダチョウの卵)だけで、全てのプログラムを見直して、必要箇所を修正しているという点です。
少ないコードの場合は問題ないかもしれませんが、数万行はコードはザラにあります。
全てのコードを精査するのは、大変厳しい作業です。
②プログラムを作る上で、気にする事が多すぎて、頭がパンクする。
先ほどのスクランブルエッグを作る例を見てみます。
従来型の「手続き型」で作成する場合、最初から最後までのコードを、様々な前提を全て気にした上で、作る必要がありました。
例えば、「フライパン」を使用する前提だから、予熱方法をこう処理しよう(コンロで火をつけて予熱しよう)とか、卵を焼き固めるのはこうしようとか、コード全体が、色々な前提を元にして書かれていきます。
スクランブルエッグ位ならしれてますが、前提が何十個、何百個とかになってくると、とても大変な状態になります。
③ せっかく作ったプログラムが、他プログラムに再利用できない。
これも、スクランブルエッグを作る例を見てみます。
従来型の「手続き型プログラム」では、フライパンを使う前提があるから、こういう処理をする、という書き方をしていました。
よって、この前提(例えば、フライパンを使う)が崩れてしまうと、その他のコードが使えないものになってしまいます。
こういう様々な前提の上で成立するコードは、他ソフトに転用できにくい(再利用しにくい)コードになっていました。
オブジェクト指向とは
以下の順番で説明していきます。
- オブジェクト指向をイメージで理解する
- メリット・デメリット(従来型プログラムの問題点がなぜ解決できるのか?)
- クラスとオブジェクトの関係をイメージで理解する
- 具体的なコードのご紹介
コードの紹介には、Pythonを使用します。
オブジェクト指向をイメージで理解する
まずは簡単にイメージできる、「タクシーの例」を紹介した後に、先程の「スクランブルエッグ」の例でご紹介します。
タクシーの例で、オブジェクト指向をイメージする
まずは簡単な例ということで、タクシーに乗って、東京駅まで行く例で考えてみます。
(先程取り上げた、スクランブルエッグの例は、後ほど出てきます。)
この場合、以下の3人(個)の登場人物(物も含む)が関係して来ます。
この登場人物(物も含む)こそが、プラグラムの世界では、オブジェクトに該当します。
そして、このオブジェクト同士のメッセージのやり取りで、処理を進めていくやり方がオブジェクト指向になります。
東京駅まで行く場合、下記のようなメッセージのやりとりがあると考えられます。
やり取りしたメッセージ
- 客から、タクシードライバーに、「東京駅までお願い」とメッセージを送る。
- 客から、タクシーに、「運転」というメッセージを送る。
タクシードライバーから、タクシーにメッセージって、人から物(タクシー)にメッセージを送る?? と疑問を持った方もいるかと思います。
もちろん現実世界では、ただ運転をしている訳ですが、これは車への指示と見ることもできます。
つまり物に、指示を送る(メッセージを送っている)と捉える事もできます。
つまるところ、オブジェクト指向って何?
つまりオブジェクト指向というのは、プログラムをオブジェクトという単位に分けて記述していく方法です。
そのオブジェクト達がメッセージをやり取りする事で、処理を表記していくプログラム手法です。
「スクランブルエッグ」の例で、オブジェクト指向でイメージする。
一番最初に取り上げた、「スクランブルエッグ」の事例を見てみます。
登場人物(物を含む)は、以下の3人(個を含む)です。
これのオブジェクト間のメッセージを見てみます。
処理手順は以下の通りでした。(振り返りです。)
その際のメッセージのやり取りは、以下の図のようになります
(細かいメッセージのやりとりは省略し、分かり易さ優先で表記しています。)
各オブジェクトは、役責を持っている事が特徴です。
- 料理人 → 料理全体の指揮、調理に関わる処理
- フライパン → フライパンに関わる処理
- ダチョウの卵 → ダチョウの卵に関わる処理、ダチョウの卵に関する知識全部
オブジェクト指向のメリットとデメリット
オブジェクト指向のメリット
- プログラムの変更が容易になる。
- 分業化できる
- コード品質の保証が容易になる。
- 他のソフトから再利用できるようになる。
順番に見ていきます。
①プログラムの変更が容易になる。
冒頭に例にあげた、スクランブルエッグで、卵をニワトリから、ダチョウに変えた例で見てみます。
既に表示した下記フローですが、卵の知識は全て、一番右側の「卵」オブジェクトが保有しています。
(ここでいう、卵の知識というのは、卵の割り方、焼き固める温度 を表しています。)
よって、「卵」オブジェクトを「ダチョウの卵」オブジェクトに変更するだけで、他は変更無しで、プログラム全体の処理を変更する事が可能になります。
また変更する箇所もオブジェクト単位なので、従来型プログラムのように、コード全体を見直す必要が無いのが特徴です。
②分業化、③コード品質の保証 容易化、④再利用
オブジェクト単位でプログラムを書くため、どこまで一塊がはっきりする傾向にあります。
よって分業化しやすい特徴を持っています。
また、コードテストもどこまでが一塊がはっきりしているため、実施しやすい傾向にあります。
加えて、あるオブジェクトのみを、他のソフトウェアで再利用する事も容易となってきます。
オブジェクト指向のデメリット
オブジェクト指向にてコードを書く場合、作りたいモノ全体からオブジェクトに分解するという、作業が必要になってきます。
オブジェクト間の関係性を考えた上で、どの単位のオブジェクトに分解するか、設計能力が問われます。
作者も、どの単位でオブジェクトに分解するか、いつも悩んでいます。
クラスとオブジェクトの関係
「そもそも実際のコードでオブジェクトってどうやって作るの?」
一般的に、クラスを作って、クラスからオブジェクトを作ります。
クラス?? また訳の分からない用語がでてきたよー
こちらも、イメージで分かり易く、解説していきます。
人間を例に見てみます。
人間の属性として(人間が持っている特徴は)、
- 名前
- 性別
- 身長
- 体重
の4つしかない世界を考えてみます。
この人間の例を使って、クラスからオブジェクトを作るイメージを、下記絵にてご紹介します。
クラスというものに、「人間の情報(名前とか)」を定義しておき、そこに具体的な値(名前:A)を入れる事により、オブジェクトを作るというイメージになっています。
まとめると、
- クラスには、ある「モノ(この例では、人間)」がもっている属性やメソッド(説明しませんでしたが、走るとか叫ぶとか 行為(プロセス)を伴うもの)を記述しておきます。
- そのクラスの属性に、具体的な値を入れる事で、オブジェクトを作る。
と言ったものになります。
クラスからオブジェクトをコードで作ってみる
先程の人間を例にして、コードを書いてみます。
クラスのコード
# 人間クラス
class Person(object):
def __init__(self, name, gender, height, weight):
self.name = name
self.gender = gender
self.height = height
self.weight = weight
わけが分かんないよー
これもまた、1つずつ分かり易く解説していきます。
クラスコードの最初部分
下図のように、クラスの定義は、「class 〇〇(object): 」(〇〇は任意文字)から開始します。
(記述の中にある、「object」は、無視して頂いて大丈夫です。)
冒頭4文字あける事で、Pythonでは、どのコードが一塊がを示しています。
クラスコードの次の部分
「def __init__」から始まって、インデントされた塊部分は、コンストラクタと呼ばれています。
このコンストラクタは、クラスからオブジェクトを作る際に、実行される部分です。
次節で、オブジェクトを作るコードを見ていくので、そちらで詳細を見ていきます。
クラスからオブジェクトを作るコード
a = Person('A', 'man', 170, 70)
b_ko = Person('B子', 'woman', 168, 50)
c_ko = Person('C子', 'woman', 155, 40)
上記コードは、Aさん、B子さん、C子さんの順番にオブジェクトにしています。
また、コード中の()の中は、先程のクラスのコンストラクタで宣言した、「名前、性別、身長、体重」の順番になっています。
下図は、クラスからオブジェクト作成した時の「名前、性別、身長、体重」の値が、どう代入されていくかを表示したものです。
このように最終的に、クラスで書いた、self.〇〇部分に代入されます。
このselfですが、「オブジェクト自身」を指します。
つまりself.name というのは、オブジェクト自身のname という変数に、オブジェクトを作る際に設定した値(’A’ とか、’B子’ 等)を代入するという行為になります。
オブジェクト指向をコードで作ってみる
途中で取り上げた、タクシーで東京駅に行く例で見てみます。(下図の例です。)
まずは、クラスの定義ですが、タクシードライバーだけ、一例として書いてみます。
このコードでは、行為(メソッド)も定義しています。
(ここでは、def drive(self, to_destination) がメソッド部分に該当します。)
class TaxiDriver(object):
def __init__(self, my_taxi):
self.my_taxi = my_taxi
def drive(self, to_destion):
# 目的地までの運転手順を書く
# 下記は一例です。
# タクシーを走らせたり
self.my_taxi.start()
# 右折したり
self.my_taxi.turn_right()
# 左折したり
self.my_taxi.turn_left()
# 停止したり
self.my_taxi.stop()
それらを元に、下記コードに、オブジェクト間のやりとりを記載してみます。
下記の名前で、既にオブジェクトを作ったと仮定した上で、コード記載しました。
- 客 → 「customer」という名前のオブジェクト
- タクシードライバー → 「taxi_dirver」という名前のオブジェクト
- タクシー → 「taxi」という名前のオブジェクト
コード例がこちらです。
# 客がタクシードライバーに東京駅まで頼む
# askというメソッドを、クラスで定義したとする
costomer.ask(taxi_driver, tokyo_station)
# タクシードライバーが東京駅まで運転する
taxi_driver.drive(tokyo_station)
まとめと本記事の注意点
本記事においては、
- 従来型の問題点
- オブジェクト指向をイメージで理解する
- オブジェクト指向のメリット・デメリット
- コードの作り方
をご紹介しました。
本記事では、情報量が増えるため省略しましたが、実際のプログラミングでは、
- 何をオブジェクトとするのか
- オブジェクト間の関係をどうするのか?
等、じゃっかん異なってきます。
また同じ理由で、継承、インターフェース等の概念は、触れないようにしました。
このあたりも含めて、更に詳細を知りたい方は、下記本がお薦めです。(あるセミナーの講師に、良書という事で、紹介頂きました。)
主に下記の内容が解説されております。
- どうやってオブジェクトに分解すればいいか
- オブジェクト間の関係はどうすればいいか
- デザインパターンと呼ばれている、「オブジェクト間の関係性」で定石パターンを深く理解する事ができる
(内容が結構難しので、一度、中身を見てから、購入されるかどうかを検討するのがお勧めです。)
本ブログでも、ご要望があれば、更に深いオブジェクト指向を紹介していこうと思います。
雑談
「この記事、よく分かんない部分があるー」という方は、Twitterやお問い合わせから質問して頂けると、近日中に回答するように致します。
セミナーの講師から聴いた話ですが、オブジェクト指向が日本で流行りだしたのは、JRが自動改札を採用した時代と伺いました。
この時代、自動化の波があったので、大量のプログラマーが必要になった時代との事です。
ただし、手続き型のプログラムというのは、数十万行クラスのコードとなると、いわゆる一部の天才しか作れない物になってしまうので、私も含めた凡人には厳しい世界でした。
そこで着目したのが、オブジェクト指向という手法で、一気に広まったという話を聞きました。
その講師自身、そう聞いた という感じでしゃべっていたので、合っているかどうか怪しいですが・・
話は変わって、更に余談です。
オブジェクト指向は、手続き型プログラムの問題を全て解決できる魔法の方法としてもてはなされましたが、実際は全然異なっていました。
オブジェクト指向の特性を発揮しようとすると、使いこなすのが、とてつもなく大変だという事が分かって来ました。
Javaで書いたからオブジェクト指向なんでしょ! と言っている方もいますが、プログラム言語を何を使ったかで、オブジェクト指向になる訳ではありません。
オブジェクト指向の考え方で全体を設計し、コード化する必要があります。
そして、そのオブジェクト指向のあるべき姿とは?という事で、20〜30年位かけて、様々な方が色々なものを考案されてきました。
ただそのあたりの問題も今日では、ほとんど解決され(学習するのは大変ですが)、一定の方向性は決まったかな?と個人的に思っています。
今後、より深いオブジェクト指向の記事も、折をみて書いていきたいと思います。
最後まで読んでいただき、ありがとうございました。