前ページでオブジェクト指向プログラミングのメリットについて紹介しました。
引き続き、メリットについて見ていきたいと思いますが、そのために、まずずっと昔に行われていたプログラムの作り方を振り返り、今のオブジェクト指向プログラミングまで見ていくことにしましょう。
グローバル変数
プログラムを動かす時には、大量のデータを管理する必要があります。
ファイルやデータベースとやりとりするデータ、画面から入力したデータ、処理途中のデータ、サーバー内部にずっととっておくデータ、各種設定、ログ等、様々なデータを内部で管理します。
そういった大量のデータをどう管理するか、がプログラムそのものを管理することに継ながります。
でも、最初はダメダメでした。
「グローバル変数」という、どこからでも自由にアクセスできる変数にデータをつっこんで使うという方法を、最初は採っていました。
Javaには正確な意味でのグローバル変数はありませんが、publicでstaticなフィールドがかなり似ているので、これを使ってどんな感じか見てみましょう。
/**
* 「グローバル変数」を持つクラス。
*/
class GlobalVariable
{
/**
* グローバル変数x3。
*/
public static int data1;
public static int data2;
public static int data3;
}
/**
* 実行用クラス。このクラスを実行してください。
*/
class GlobalVariableRunner
{
public static void main( String[] args )
{
// グローバル変数data1~data3に入れます。
GlobalVariable.data1 = 35;
GlobalVariable.data2 = 45;
GlobalVariable.data3 = 55;
// 平均を計算して出力します。
printAverage();
}
public static void printAverage()
{
// 平均を計算して出力します。
int sum = 0;
sum += GlobalVariable.data1;
sum += GlobalVariable.data2;
sum += GlobalVariable.data3;
// 平均を計算します。
int average = sum / 3;
System.out.println( average );
// 出力結果:
// 45
}
}
// GlobalVariableRunner.java /** * 「グローバル変数」を持つクラス。 */ class GlobalVariable { /** * グローバル変数x3。 */ public static int data1; public static int data2; public static int data3; } /** * 実行用クラス。このクラスを実行してください。 */ class GlobalVariableRunner { public static void main( String[] args ) { // グローバル変数data1~data3に入れます。 GlobalVariable.data1 = 35; GlobalVariable.data2 = 45; GlobalVariable.data3 = 55; // 平均を計算して出力します。 printAverage(); } public static void printAverage() { // 平均を計算して出力します。 int sum = 0; sum += GlobalVariable.data1; sum += GlobalVariable.data2; sum += GlobalVariable.data3; // 平均を計算します。 int average = sum / 3; System.out.println( average ); // 出力結果: // 45 } }
GlobalVariableクラスがdata1~data3フィールドを持っています。
このフィールドがグローバル変数です。
このフィールドはpublicでstaticなフィールドなので、どこからでも「GlobalVariable.data1」という形で使用できてしまいます。
publicでstaticなフィールドはプログラムのどこからでもアクセスできます。
ということは、大事なデータを入れておいても、間違えて書き換えてしまうかもしれないということです。
data1フィールドに誤った値を上書きするプログラムを、勘違いして書いてしまったり、問題ないと思って書いてしまったり、もしくは複数人数で作っている時には他の誰かがやっちゃうかもしれません。そういったミスを防ぐ方法がありません。
特に、プログラムの規模が大きくなればなるほどそういった問題が発生しやすく、グローバル変数を使用すると問題が多発しました。
なので、このような「グローバル変数」は今では絶対に作ってはならないとされています。
「構造体」に入れる
グローバル変数はまずい、ということで次に採られたのが「構造体」というものにデータをまとめて入れるという方法です。
構造体は、「publicなフィールド」だけを持つクラスのことです。
つまりデータの貯蔵専用クラスですね。
/**
* 構造体っぽいクラス。
*/
class Struct
{
/**
* 変数x3。
*/
public int data1;
public int data2;
public int data3;
}
/**
* 実行用クラス。このクラスを実行してください。
*/
class StructRunner
{
public static void main( String[] args )
{
// Structクラスのインスタンスを作ります。
Struct ref = new Struct();
// data1~data3フィールドに入れます。
ref.data1 = 35;
ref.data2 = 45;
ref.data3 = 55;
// 平均を計算して出力します。
printAverage( ref );
}
/**
* 平均を計算します。
*/
public static void printAverage( Struct st )
{
// 平均を計算して出力します。
int sum = 0;
sum += st.data1;
sum += st.data2;
sum += st.data3;
// 平均を計算します。
int average = sum / 3;
System.out.println( average );
// 出力結果:
// 45
}
}
// StructRunner.java /** * 構造体っぽいクラス。 */ class Struct { /** * 変数x3。 */ public int data1; public int data2; public int data3; } /** * 実行用クラス。このクラスを実行してください。 */ class StructRunner { public static void main( String[] args ) { // Structクラスのインスタンスを作ります。 Struct ref = new Struct(); // data1~data3フィールドに入れます。 ref.data1 = 35; ref.data2 = 45; ref.data3 = 55; // 平均を計算して出力します。 printAverage( ref ); } /** * 平均を計算します。 */ public static void printAverage( Struct st ) { // 平均を計算して出力します。 int sum = 0; sum += st.data1; sum += st.data2; sum += st.data3; // 平均を計算します。 int average = sum / 3; System.out.println( average ); // 出力結果: // 45 } }
Structクラスが構造体です。
使う時は、普通にインスタンスを作り、フィールドに値を入出力します。
構造体はインスタンスを作って参照を通して使うことになるので、「参照がないと使えない」という点でアクセスを制限することができます。そのため、グローバル変数よりは安全です。
でもフィールドには自由にアクセスできます。data1~data3フィールドはpublicフィールドなので、自由に読み書きできてしまいます。
この例では「平均点」を計算しています。data1~data3は何らかの点数で、その平均を出している場合、data1~data3フィールドには「0~100」の値が望ましいでしょう。
でも、publicフィールドだと自由にアクセスできてしまうため、何かでついうっかり書き換えてしまう可能性があります。
つまり、グローバル変数の時のように、なんらかのマチガイからdata1~data3の値が不正に書き換えられてしまう可能性があるということです。そして同じように、規模が大きくなればなるほどその可能性は高くなります。
手続き型プログラミング
このページで紹介したようなプログラミングの方法を「手続き型プログラミング」と言います。
手続き型プログラミングでは、処理とデータ、つまりメソッドと変数は別々になっていて、全部ごちゃまぜに管理する形になります。
しかしそうなると、全部ごちゃごちゃに絡み合ったプログラムができあがってしまいます。
そのようなプログラムは、単体テストが難しく、バグがあってもどこに原因があるか分かりづらく、修正をした場合に影響範囲が大きく、個々が絡み合っているためプログラムの使い回しが難しくなります。つまりダメダメです。
このような「古い方法でプログラミングする」場合の問題点を、オブジェクト指向プログラミングすることでだいぶ解決できます。
次ページではその方法を見てみましょう。