クラス |
Mizuho’s Web Site Coyright 2004 All rights reserved
持っているほうが 望ましい知識/スキル |
プログラマが必ず持っているべき知識/スキル |
オブジェクト指向に関する知識/スキル ・オブジェクト指向全般に関する知識 ・オブジェクト指向言語に関する知識 ・問題対象からオブジェクトを抽出できるモデリング能力 |
図1 ●オブジェクト指向は、どちらかというと「プログラマが持っているほうが望ましい知識/スキルに属する。現在ではフレームワーク等が注目されているので必須になりつつある。 |
![]() |
![]() |
・OSに関する知識 ・データ構造/アルゴリズムに関する知識 ・バグが発生しやすそうな個所を見つけ出すカン ・コーディングの作法に関する知識 |
・プログラミング言語に関する知識 ・開発ツールに関する知識 ・問題対策から プログラム仕様を作成できる抽象化能力 |
・C++ ・Java ・Smalltalk ・Object Pascal(Delphi) ・Eiffel ・C# |
OOP |
オブジェクト指向プログラミング |
オブジェクト指向技術を使って プログラム・コードを生成する |
・ActiveXコントロール ・サーバーサイド・コンポーネント (EJBコンポーネントなど) ・アプリケーション・フレームワーク |
コンポーネント/クラスライブラリ |
再利用可能なソフト部品またはクラスの集まり |
UML:United Modeling Language EJB:Enterprise
JavaBeans CORBA:Common Object Request Broker Architecture ORDB:Object Relation Database |
![]() |
![]() |
・ピュアODB ・ORDB |
ODB |
オブジェクト指向データベース |
プログラム中で利用するオブジェクトを保存しておく(永続化する) |
・EJB ・CORBA |
オブジェクト指向ミドルウェア |
必要なオブジェクト(クラス)およびそれらの関係を抽出し、それぞれに持たせる処理を決める |
OOA/OOD |
・UML ・デザイン・パターン |
オブジェクト指向分析/設計 |
必要なオブジェクト(クラス)およびそれらの関係を抽出し、それぞれに持たせる処理を決める |
![]() |
![]() |
図2 ●オブジェクト指向プログラミングを取り巻く主な関連技術。このように整理すれば、用語に飲まれる心配はないだろう |
メンバー変数 |
派生 |
メソッド |
インスタンス |
ポリモ-フィズム |
カプセル化 |
クラス |
図3 ●オブジェクト指向関連の用語がユーザーを悩ませる |
継承 |
クラスE |
関数E |
データE |
クラスD |
関数D |
データD |
クラスC |
関数C |
データC |
クラスB |
関数B |
データB |
クラスA |
関数A |
データA |
データE |
データB |
データD |
データC |
関数A |
関数B |
関数C |
関数D |
データA |
関数E |
プログラム |
図4 ●クラスを利用すると、データと関数がまとまっていてわかりやすい。本来はモジュールのようなファイルでの管理も意識する必要があるが、ここでは無視している |
オブジェクト指向プログラミング |
従来の手続き型プログラミング |
プログラム |
リスト2 ●フィールドの頭の「public」を「private」に変更している |
public class Sankaku { private double teihen; private double takasa; public void setInfo(double a, double b) { teihen = a; tasaka = b; } public double getMenseki(){ return teihen * takasa / 2; } } |
リスト1 ●Sankakuクラスは二つのフィールドと、一つのメソッドを持っている |
public class Sankaku { --------------------------(1) public double teihen; -------(3) ↑ public double takasa; --------(3) public Sankaku() { --------(4) } ------------------------(4) public double getMenseki(){ ----(5) return teihen * takasa / 2; ----(5) } --------------------------(5) ↓ } -------------------------------------------(1) public class RunTest{ --------------------------(2) ↑ public static void main(string[ ] args){ Sankaku a = new Sankaku(); --------(6) a.takasa = 10; a.teihen = 20; System.out.println(a.getMenseki()); } ↓ } -------------------------------------------(2) |
![]() ![]() |
![]() ![]() |
メソッドC |
フィールドC |
サブクラス |
クラスC1 |
メソッドB |
フィールドB |
クラスB1 |
サブクラス |
メソッドA |
フィールドA |
スーパークラス |
クラスA |
メソッドC |
フィールドC |
メソッドA |
フィールドA |
クラスC0 |
メソッドB |
フィールドB |
メソッドA |
フィールドA |
クラスB0 |
![]() |
図6 ●クラスをスーパークラスとサブクラスに切り分ける |
直接アクセスできる |
クラス |
![]() |
クラスの 利用者 |
メソッド |
データ |
悪い例 |
クラス |
○ |
× |
![]() |
![]() |
クラスの 利用者 |
![]() |
メソッド |
データ |
アクセスできない |
よい例 |
図5 ●データを直接アクセスさせるような仕様はなるべく避け、常にメソッドを通してアクセスできるようにするべきだ。 |
◆サーバーサイド・コンポーネントとはサーバー側で利用するためのソフト製品。
◆EJBはEnterprise Java Beansの略で、Javaのコンポーネント仕様 Java Beansをサーバー側で使えるように拡張したもののこと。EJB仕様に基づくソフト製品は、サーバーサイド・コンポーネントの代表例の一つと言えます。
◆COBRAは Common Object Request Broker Architecture の略で、分散オブジェクト環境の標準仕様です。
◆UMLは United Modeling Language の略で、オブジェクト指向分析/設計における標準モデリング言語のことです。
◆パターン:オブジェクト指向分析/設計における”定石”のことを指します。デザイン・パターンやアナリシス・パターンに加えて、「やってはいけないパターン」のアンチパターンなどがあります。
*1 例えばSmalltalkというオブジェクト指向言語では、クラスのことをオブジェクトとも呼んでいます。多だし他の言語/ツールでは、インスタンスのことをオブジェクトと呼ぶこともあります。このようにオブジェクト/クラス/インスタンスの使い分けが統一されていない点も、ユーザーの混乱を招く一因となっています。
*2 Javaの「new」はVBでオブジェクトを作るときに使う「New」とほとんど同じ役割です。
*3 VBでは「set=NewCTest」と書きます。
*4 すべての関数を外部から使えるようにするとは限りません。データと同様に関数を「非公開」にすることもできます。
*5 カプセル化は、クラス内のフィールド(データ)や、それに関連のあるメソッド(関数)をひとまとめにするという意味でも使われます。
*6 C++ではメンバー関数ともいいます。
*7 protectedを指定したデータ(フィールド)またはメソッドは、それらを持つクラスと、そのクラスを継承したサブクラスから参照可能です。publicとprivateはあとで説明します。このほか、クラスに対してもアクセス指定子を使用することができます。Javaでは抽象クラスを表すabstract、C++では指定クラスから参照可能なfriendなどがその例です。
*8 C++ではメンバー変数といいます。VBでも同様です。
さて、とりあえずコードを「クラス」という単位で区切ることができました。でも、単に区切っただけでは不十分です。次に、その区切り(クラス)の中でデータや関数をどのような形にするべきかを考えなければなりません。
コードをすっきりさせるためには、それぞれのクラスができるだけ「独立」の関係になるようにすることが大切です。要は「クラスの中でできることは、すべて中でやるようにする」わけです。
そのためには「クラスの中のデータを扱えるのは、基本的にそのクラスの関数だけ」とするのが有効なはずです。外部からそのクラスのデータにアクセスしたい場合は、必ずそのクラスの関数を通じてアクセスしてもらうようにするのです*4(図5)。丁度会社にいるあなたを訪ねてきた人に対して、まず受付を通してもらうようにお願いするようなものです。
このようにクラスの中の情報に対して外部から直接アクセスできないようにし、アクセス方法を(そのクラスが持つ関数に)一本化することを「カプセル化」または「情報隠ぺい」と呼びます*5。カプセル化は、クラスと並んでオブジェクト指向プログラミングで欠かせないキーワードですので、ぜひ押さえておいてください。
クラスが持っている関数のことを「メソッド」と呼びます*6。クラスを利用したい場合は、このメソッドを呼び出す形で利用することになります。ここで、先ほど例として挙げたCDentakuクラスがSetDataというメソッドを持っているとします。SetDataメソッドは、演算の対象となる二つの値をセットするものです。このSetDataメソッドを呼び出す場合は、例えば以下のように記述します。
a.SetData(10,20)
「a」というのは、先ほど生成したインスタンスaのことです。このようにインスタンスを生成すると、元のクラスが備えている機能が利用できるようになります。当然、SetDataメソッドも利用できるわけです。Javaでは、このようにインスタンス名+ドット演算子(.)+メソッドという形で記述します。
さて、いま言ったこととちょっと矛盾しますが、すべてのクラスで「外部にはデータを見せない」と杓子定規に決めてしまうのはちょっと不自然です。すべてのクラスから共通に使いたいデータや関数だって当然存在します。そのような場合には「これはどこからでも使えるよ」という仕組みもあったほうが便利です。コードをすっきりさせるためには、「整理する」だけでなく、「人間にとって自然な形をとる」ようにすることも大切なのです。情報隠ぺいといっても、すべての情報を隠すというわけではないことに注意してください。
そこで多くのオブジェクト指向言語は「アクセス指定子」というものを用意しています。これを使って、クラスがもつ情報やメソッドを「どれだけ外に見せるか」を決めることができます。Javaではアクセス指定子として、public、private、protectedなどを用意しています*7。
ここまでわかったところで、実際にコードを見ていきましょう。まずはリスト1をご覧ください。三角形を表すSankaku(1)、そしてRunTest(2)という二つのクラスを定義しています。RunTestクラスはSankakuクラスからインスタンスを作って実行するためだけに作ったものです。C言語でいうところのmain関数に相当するものです。VBを使っている人は、フォームのLoadイベントの中でクラスからインスタンスを作っていると思ってください。Sankakuクラスは、底辺(teihen)、高さ(takasa)という二つのデータを持ちます(3)。もっと正確に言えば、底辺と高さのデータをいれるための”入れ物”を持っています。これらを「フィールド」といいます*8。これらに加えて、面積を求めるgetMensekiメソッドも備えています(5)。(4)はあとで説明することにして、(2)の中の処理に注目してください。まず最初に、Sankakuクラスのインスタンスaを作っています(6)。これでインスタンス「a」を操作することで、Sankakuクラスのメソッドにアクセスできるようになるわけです。
ところが、このサンプルは大きな問題点を抱えています。それは、(3)で宣言したフィールドのアクセス指定子として「public」を指定していることです。publicという単語から想像が付くと思いますが、こう指定するとアクセスにまったく制限がなくなり、すべてのクラスから自由にアクセスできるようになります。先ほど、すべての情報をクラスの外部からアクセスできないようにするのもどうかと言いましたが、この情報に関しては別に自由にアクセスできるようにする必要ありません。クラスはできるだけ「独立」にするという原則を安直に崩すのは慎むべきです。しかもすべてのクラスから参照可能にすると、ほかのクラスから不用意にアクセスされる可能性があるので、バグを生む要因にもなりかねません。
そこで、ここでは二つのフィールドを「private」にするするほうがよいでしょう。こうすると、そのフィールドを持つクラスの中からのみ参照可能になります。ただこのサンプルの場合、このままではフィールドを別途用意する必要があります。これらのことを踏まえてリスト1のコードを変更すると、リスト2のようになります。teihenとtakasaの値はSetInfoメソッドを使って設定できます。
*9 C++ではスーパークラスを基底クラス、サブクラスを派生クラスと呼びます。
*10 リスト3のSankakuクラスはリスト2のteihenを「x」に、takasaを「y」に変更しているだけです。
*11 C++では、サブクラス名のあとに「コロン(:)+スーパークラス名」と記述します。VBの場合、JavaやC++のような継承はできません。詳しくはPart2のカコミ記事(80〜81ページ)を参照してください。
*12 このほか、オーバーロード(overload)もポリモ-フィズムの例として挙げる人もいます。オーバーロードは、一つのクラス内に名称は同じ内容が異なるメソッドを定義することを指します。同じクラスで、三角形の面積を求めるメソッド「Menseki(int hankei)」を定義する、といった具合です。このように引数の型や個数は異なる必要があります。Javaではメソッドのオーバーロードのみ可能ですが、C++では演算子のオーバーロードもできます。
図7 ●スーパークラスのメンバーをprivateで宣言すると、サブクラスからはアクセスできない |
Sankakuクラス(サブクラス |
○ |
○ |
× |
![]() |
![]() |
![]() |
protected |
public |
Sankakuクラス(スーパークラス |
private |
リスト3 ●継承を利用した例。SankakuクラスはSankakusuiクラス継承している |
public class Sankaku { --------------------------(1) private double teihen; ↑ private double takasa; public Sankaku() { x = a; y = b; } public double getMenseki(){ return x * y / 2; } ↓ } -------------------------------------------(1) public class Sankakusui extends Sankaku{ ----------(2) private double z; -----------(3) ↑ public void SetInfo2(double c) { --------(4) z=c; -------------------------------(4) } -----------------------------------(4) public double GetTaiseki() { -----------(5) return GetMenseki()*z/3; -----------(5) } -----------------------------------(5) ↓ } ---------------------------------------------(2) public class RunTest{ public static void main(string[ ] args){ Sankakusui Mys = new Sankakusui(); Mys.SetInfo (10,20); Mys.SetInfo (30); System.out.println(Mys.getMenseki()); } } |
public class SaintBernard extends Dog { public SaintBernard () { type = ”セントバーナード”; } } public class RunTest { public static void main(String[ ] args) { SaintBernard taro = new SaintBernard(); taro.setName(”タロ”); SaintBernard jiro = new SaintBernard(); taro.setName(”ジロ”); SaintBernard hana = new SaintBernard(); taro.setName(”ハナ”); Dog.dogs[ ] = {taro,jiro,hana }; for (int i = 0 ; i <= 2 ; i++) { System.out.println(dogs[ i ].getType()); System.out.println("("+dogs[ i ].getType()+")"); System.out.println("「"+dogs[ i ].bark()+"」"); } } } |
リスト4 ●オーバーライドの例。barkメソッドの鳴き声に注目 |
public class Dog { Strig name; String type; public void setName(String n) { name = n; } public String getName(){ return name; } public String bark(){ return type; } } public class Chihuahua extends Dog { public Chihuahua () { type = ”チワワ”; } public String bark() { return ”キャンキャン!”; } } |