「6さいからのプログラミング」第12話は、プログラムが大規模になったときや多人数開発のときに役に立つ「オブジェクト指向」について解説します。1オブジェクト指向プログラムが数万行を超えてくると、全体像を把握するのが困難になるため、後で内容を変更したくなったときや他の人と一緒に開発するときに大変になります。そのような場合、多くのプログラミング言語で採用されている「オブジェクト指向」を使うと便利です。「オブジェクト指向しこう」とは、プログラムを「オブジェクト」というまとまりで扱う手法で、全体像を把握したり、後で内容を変更したり、プログラムを別の箇所で再利用したり、他の人と開発するのを容易にします(図1-1)。オブジェクト指向これまでのプログラムは全体がひとまとめになっていたため、どの部分がどの部分に影響しているかが解りにくく、プログラムを変更するときにはすべてを把握する必要がありましたが、オブジェクト単位でプログラムを切り分けると、変更したいオブジェクトとオブジェクト間の関係を把握するだけでプログラムが変更できるようになります。2オブジェクトの機能さて、切り分けた各オブジェクトが互いにやりとりできるようにするために、オブジェクトは「3つの仕組み」と「3つの要素」という機能を持つことが決められています(図2-1)。3つの仕組みオブジェクトは、データを持つ。オブジェクトは、他のオブジェクトにメッセージを送る。オブジェクトは、送られたメッセージに応じて処理を行う。オブジェクトの3つの仕組みと3つの要素それぞれがどのようなものかを順番に説明します。プログラムを切り分けるだけでなく、これらの機能を持たせることで、各オブジェクトは扱いやすい部品になります。2.13つの仕組みまずは「3つの仕組み」から見ていきましょう(図2-2)。オブジェクトの3つの仕組みこれは先ほどの「データを持つ」「他のオブジェクトにメッセージを送る」「送られたメッセージに応じて処理を行う」という3つの仕組みを図示したものです。オブジェクトはデータを保持することができ、これを「データメンバ」といいます。データメンバは言語によって呼び方が異なり、C++では「メンバ変数へんすう」、C#では「プロパティ」と呼ばれます。オブジェクトは他のオブジェクトを操作するために、「メッセージ」を送ることができます。メッセージとは、いくつかの情報をまとめたものです。送られたメッセージに応じてオブジェクトは処理を行うことができ、これを「メソッド」といいます。C#でも「メソッド」ですが、C++では「メンバ関数かんすう」と呼ばれます。「データメンバ」と「メソッド」は、合わせて「メンバ」といいます。次に3つの要素、「継承」「カプセル化」「ポリモーフィズム」を見ていきましょう。1つ目の「継承けいしょう」とは、オブジェクトのメンバを引き継いで別のオブジェクトを作成できる機能です。図2-3のように、引き継ぐときに新しいメンバを追加したり、引き継いだメンバを上書きすることもできます。継承2つ目の「カプセル化か」とは、オブジェクトのメンバの一部を隠すことができる機能です。図2-4のように、外部に公開する部分を必要最小限にすることで、オブジェクトに対する想定外の操作を防ぐことができます。カプセル化3つ目の「ポリモーフィズム」とは、継承によって複製されたオブジェクトたちを、全て継承元と同じとみなして扱える機能です。図2-5のように、多くの種類のオブジェクトが生まれたとしても、その違いを意識することなくまとめて扱うことができます。ポリモーフィズムまとめますと「オブジェクト指向」とは、プログラムを「オブジェクト」という単位で扱い、そのオブジェクトに「3つの仕組み」と「3つの要素」を持たせることで、プログラムの開発を容易にする手法です。3クラスとインスタンスそれでは、プログラム上でオブジェクト指向がどう表現されるかを、具体的に見ていきましょう。多くの言語はC++に影響を受けていますのでC++を例に解説します。C++は基本的にはC言語にいくつかの文法と機能を足しただけの言語ですので、今までに説明したC言語の知識が流用できます。C++のオブジェクトは、C言語における構造体を発展させて実現しました。この発展した構造体を「クラス」と呼びます。クラスとC言語の構造体との違いは、クラスには中に変数だけでなく、関数も持たせられることです(図3-1)。struct_and_class.cppこのプログラムでは「構造体S」と「クラスC」を定義し、メイン関数の20~21行目で、Sの変数sと、Cの変数cを作成しています。このとき、sのメンバ変数aが「s.a」と表せることは以前説明しましたが、cのメンバ変数aも同様に「c.a」と書いて扱えます。cのメンバ関数fも同様に「c.f()」と書いて呼び出せます。この変数cのような、クラス型の変数のことを「インスタンス」といいます。C++におけるオブジェクトは、ひな形であるクラスと、それを実体化したインスタンスによって作られます。メンバ関数の内部では、そのクラスが持つメンバ変数を読み書きできます。このプログラムでは「c.f()」を呼び出すと、14行目の「this->a=5;」でc.aに5を代入することができます。メンバ関数内で自身のインスタンスを表すときは「this」と記述し、ポインタのように扱います。ちなみに「this->」は省略可能となっており、「this->a=5;」は単に「a=5;」と書かれることが多いです。3.13つの仕組みここでオブジェクトの「3つの仕組み」とC++のクラスとを比べると、表3-1のようになっていることが解ります。3つの仕組みとC++のクラス3つの仕組みC++のクラスオブジェクトは、データを持つ。メンバ変数オブジェクトは、他のオブジェクトにメッセージを送る。他のオブジェクトのメンバ関数の呼び出しオブジェクトは、送られてきたメッセージに応じて処理を行う。メンバ関数内の処理C++における「メッセージ」のやりとりは、このようにメンバ関数の呼び出しで代用されています。ここからはC++における「3つの仕組み」を見ていきましょう。3.2継承1つ目は「継承」です。C++での継承はクラス名の右側に「:継承元」と書くことで行えます(図3-2)。inheritance.cppこの例では、7行目でクラス「Parent」を継承して新たなクラス「Child」を作成しています。継承元のメンバが継承先に引き継がれますので、Childは変数zだけでなく、xやyも持ちます。3.3カプセル化2つ目は「カプセル化」です。C++でカプセル化を行うには、「private:」「protected:」「public:」という「アクセス修飾子しゅうしょくし」を用います。「private:」と書くと、メンバは外部や継承先クラスから参照できないようになります。「protected:」は継承先クラスからは参照できるようになり、「public:」は外部からも参照できるようになります(図3-3)。encapsulation.cppこの例では、メンバSetにはpublic:が設定されているため、16行目のn.Setのように参照することができます。メンバvalueにはprivate:が設定されているため、n.valueのように外部から値を読み書きすることはできません。9行目のようにクラス内部からはthis->valueとして参照可能です。private:などのアクセス修飾子は、一度設定すると再設定されるまで設定が持続します(図3-4)。access_modifiers.cppこの例の場合、メンバb、c、dは全てpublic:になります。3行目のメンバaのようにアクセス修飾子が設定されていないメンバはprivate:となります。継承関係にもアクセス修飾子を設定することができます(図3-5)。継承元」と書くと、継承元のメンバでpublic:だったものは全てprotected:になって引き継がれます。継承元」と書くと、継承元のメンバは全てprivate:になって引き継がれます。継承元」は継承元のアクセス修飾子の設定がそのまま引き継がれます。このように継承元メンバの公開範囲は制限することができます。継承元」と解釈されます。3.4ポリモーフィズム3つ目はポリモーフィズムです。C++では継承先クラスを継承元クラスと同じように扱うことができます。メンバ関数の先頭に「virtual」と書くと、継承先クラスで上書きすることができます(図3-6)。polymorphism.cpp7~17行目では、クラスPersonを継承してクラスKuinaとクラスAliceを作成し、それぞれでメンバ関数GetAgeを上書きしています。メンバ関数を上書きすることを「オーバーライド」といいます。main関数の中では、それぞれのクラスのインスタンスを作成し、24行目でPerson*型の配列「people」にそれぞれのアドレスを代入しています。KuinaやAliceは、Personとは型が異なりますが、このように継承元クラスであるPersonと同じ型として扱えます。28行目ではPersonクラスのメンバ関数GetAgeを呼んでいますが、インスタンスはそれぞれPerson、Kuina、Aliceのものですので、それぞれで上書きしたGetAgeが呼ばれます。よって画面には「0」「6」「7」が表示されます。4オブジェクトの設計ここまでオブジェクト指向の考え方とC++での実装方法について解説しました。ここからは、プログラムのどこの部分をオブジェクトにすればいいのかについて、オブジェクトの設計方法を説明します。基本的には、「開発が効率的になるように」オブジェクトを作成すると大丈夫です。言い換えると、それ以外の観点でオブジェクトを作成すると本末転倒になりますので注意が必要です。アクションゲームのプログラムで、主人公のクラス「Hero」と敵のクラス「Enemy」を作成し、どちらもキャラクターでなので、さらにクラス「Character」を作って継承関係にさせるべきかどうかを検討したとしましょう。このとき、衝突判定や移動などの処理が再利用できそうならば、クラスCharacterを作って継承関係にすることは有意義です。細かな処理が異なるために再利用できる部分が少なければ、無理に継承関係にしないほうが有益になることもあります。ある失敗として、プログラムのあらゆる概念をオブジェクトにして大きなピラミッド構造を作りたくなることがあります。プログラムの最終目的は期待通り動くことであり、秩序正しく美しい階層を構築することではありません。何をオブジェクトにしてどのような継承関係にするかは、開発が効率的になるかどうかに基づいて判断すると良いでしょう。5その他の言語のオブジェクト指向さて、C++ではクラスを使ってオブジェクト指向を実現しましたが、他の言語ではこれとは異なる方法でオブジェクト指向を実現していることがあります。JavaScriptには、クラスというものが存在しません。JavaScriptでは図5-1のように、空の状態のインスタンスにメンバを追加していく流れでオブジェクトを構築していきます。JavaScriptのオブジェクトまた、Java(JavaScriptではない)におけるオブジェクトは、C++の仕組みに近いですが、よりオブジェクト指向を前提とした言語設計になっています。クラスのメンバでない変数や関数を作成することができず、あらゆるデータや処理は基本的にオブジェクトで管理され、メイン関数も何らかのクラスに含める必要があります(図5-2)。HelloWorld.java今回は、プログラムが大規模になったときに役立つ仕組み「オブジェクト指向」について説明しました。次回はより速く計算させるために、同時に複数のプログラムを実行する方法について説明します。前へ目次ホームへ次へ©Kuina-chan1579924001jaf。