このページではオブジェクト指向の書き方についてまとめています。オブジェクト指向プログラミングについてクラスの作り方といった基本的な知識があるとより理解しやすいと思います。
勝手にオブジェクト指向プログラミングになるルールが紹介された資料を見つけて (このページ、このページの参考文献(本)がこれらしい。)
オブジェクト指向プログラミングができるようになったので、 そのルールを自分なりにアレンジしたものとそのお役立ち度や効果を紹介します。
また、オブジェクト指向プログラミングが何を理想としているのかを示すためにオブジェクト指向の考え方を紹介します。
オブジェクト指向とは
まず、言葉の意味について記載します。オブジェクト指向についても説明してしまいます。
- プログラム:コンピュータにやってほしい仕事(命令)を書いた文書
- プログラミング言語:コンピュータにやってほしい仕事(命令)をコンピュータに伝えるための言語。ここで言う言語は日本語、英語などの言語をイメージしている。つまり、コンピュータとのコミュニケーションツールである。
- プログラミング:コンピュータにやってほしい仕事(命令)をプログラミング言語で書くこと(文書にすること)
- オブジェクト指向:コンピュータにやってほしい仕事(命令)をなるべく小さく簡単な仕事、またはその組み合わせによって書くという方法
- オブジェクト指向プログラム言語:オブジェクト指向によってプログラミングしやすいように作られたプログラミング言語。例えば、C++、Java、Pythonなど。
- オブジェクト指向プログラミング:オブジェクト指向によってコンピュータにやってほしい仕事(命令)をプログラミング言語で書くこと(文書にすること)。 このとき、必ずしもオブジェクト指向プログラム言語を使う必要はない。(使った方が楽に書けるとは思う。)オブジェクト指向プログラミング言語を使えば、必ずオブジェクト指向で書けるという訳でもない。
オブジェクト指向とは上に書いたようにただの書き方のお作法の1つということになります。
ただし、正しい書き方というのは一通りではありません。
では、どのように正しい書き方ができているか判断するかというと、後で記載しているオブジェクト指向によって得られるメリットが十分得られていると感じられれば上手くオブジェクト指向プログラミングができていると判断します。
つまり、正しい書き方とはプログラムを書いている人が決めるものであり、絶対的な正解はなく、完全に主観です。
ここでオブジェクト指向プログラム言語に出てくるクラスという概念についても簡単に触れておきます。
クラスとはコンピュータにやってほしい仕事をなるべく小さく簡単な仕事に分けたときにその小さな仕事(関数)とそれに関わる変数を1つにまとめたものです。
例えばゲームでのHP関連の仕事をクラスにまとめることを考えると、変数としてHPと回復する(HPを増やす)関数とダメージを受ける(HPを減らす)関数と死亡判定する(HPが0以下になったかどうか判定する)関数ぐらいをひとまとめにします。
クラスは「モノ」だと説明されて犬クラスを作成する例がよく紹介されます。犬には名前があって、吠えるという仕事(関数)を定義したり、名前を出力する仕事を定義したりします。しかし、もっと抽象的で実際にないものを、例えばゲームでの敵との接触判定をクラスにしてもよいです。
犬の例はイメージしやすくなるという良い点もあるのですが、「モノ」のイメージに捉われてしまい私は好きではありません。
オブジェクト指向によって得られるメリット
オブジェクト指向プログラミングによって得られるメリットは以下の通りです。
- バグが出にくい:コンピュータにやってほしい仕事が小さく簡単であるため、明快であり、バグが出にくくなります。
- バグが修正しやすい:コンピュータにやってほしい仕事が小さく簡単なので、期待した仕事をしていない箇所がすぐに見つかります。
- 変更しやすい:バグが修正しやすいため、変更に対して抵抗が小さくなります。
- 同じプログラムを書かなくてよい:コンピュータにやってほしい仕事を小さく簡単な仕事の組み合わせで書くので、使いまわす部分がたくさん出てきます。プログラミングをする量が減ります。また、プログラムにバグが見つかり修正した場合、使いまわしている部分もすべて修正したことになります。
オブジェクト指向プログラミングをするときに意識したいルール
オブジェクト指向プログラミングをするときに意識したほうが良いルールを以下で紹介します。
勝手にオブジェクト指向プログラミングになるルールを守り、オブジェクト指向プログラミングを体験することによって、オブジェクト指向プログラミングを理解することができます。
慣れればどのくらいなら変更してもオブジェクト指向プログラミングのメリットを享受できるかわかるようになります。
そこまでくれば、ルールを守ることで増えるプログラミングにかかる時間とメリットのバランスを見てルールを守るか破るか決められるようになります。
- 1クラス80行以下:ぜひやったほうが良い
クラスを短く書くことで自動的にほぼ小さく簡単な仕事かその組み合わせでしか書けなくなります。そのため、バグが出にくい、修正しやすい、変更しやすいというメリットが享受できます。 - クラスを単純に書く:ぜひやったほうが良い
複雑なクラスを書くということは複雑な仕事をしている可能性が高いです。単純であれば、バグが出にくい、修正しやすい、変更しやすいというメリットが享受できます。上のルールかこのルールを守れば、かなりわかりやすいプログラムになるのではないかと思います。そうすれば、劇的に変更しやすくなるので、他のルールは徐々に取り入れていくということも容易にできます。そのようにして学んでいってもいいかもしれません。クラスを単純に書くために意識すべきことは以下の通りです。- インスタンス変数は2つまで
class HP{
private int hp;(左のこれです。) - インデントはできれば1つ、最大でも2つまで
- インスタンス変数は2つまで
- Setterを使わない:やったほうが良い
class HP{
private int hp;
public int set(int setHp)//これがSetter
{
hp=setHp;
}
これを使うと予期せぬ箇所で、または、予期せぬ値に変数を変更してしまう可能性が高くなります。Setterを使わないことで予期せぬ変更を防ぐことができ、バグが出にくくなります。 それでは、どのように値を変えるのかと思うかもしれません。 初期化はコンストラクタで行います。 その他、例えば回復の処理は
public void heal(int healVal)
{
hp=hp+healVal
}
ダメージの処理は
public void damage(int damageVal)
{
hp=hp-damageVal
}
のように、変更したい状況に応じた関数で変更するようにします。 - 求めるな、命じよ:できればやる方が良い
何をいっているかというとクラスの変数の値を外部に教える(Getter)のではなく、そのクラス内で完結させようということです。よくあるパターンの例としてHPの死亡判定を例にすると、
class HP{
private int hp;
public int get()//ダメな関数
{
return hp;
}
public boolean deadJudge()//いい関数
{
if(hp<=0)
{
return true;
}
return false;
}
}
--------------------------------------------
if(HP.get()<=0)
{
死亡時の処理
}
ではなく、
if(HP.deadJudge()==true)
{
死亡時の処理
}
とするのが良いということです。get()だとその後どう使われるかわかりませんが、deadJudge()は死亡判定にしか使えません。deadJudge()が死亡判定以外の場所で見つかればバグだと簡単に判断できます。また、deadJudge()がhp>0でtrueになってもバグだと簡単に判断できます。 - 基本型、文字列型はクラスにする:できればやる方が良い
int hp;としてhpを単独で存在させるのではなく、
class HP{
private int hp;
public boolean deadJudge()
{
if(hp<=0)
{
return true;
}
return false;
}
}
のように仕事をセットにするのが目的です。何がどこに書かれているか、何をどこに書くべきか明確になり、変更しやすくなります。 - ドットは1つまで:できればやる方が良い
class First{
public Second Second;
}
class Second{
public void test()
{
}
}
First.Second.test()ではなく、
class First{
private Second Second;
public void test()
{
Second.test();
}
}
class Second{
public void test()
{
}
}
としてFirst.test()とするということです。Second.test()の変更の影響がFirst.test()内で収まるようにします。修正が修正を呼ぶようになることを防げます。 - 名前は長くても内容がわかるようにする:できればやる方が良い
実はこれは上で紹介した勝手にオブジェクト指向プログラミングになるルールが紹介された資料とは真逆のことを言っています。資料ではクラスや関数の名前が長くなるということはクラスや関数が複雑なことをしているのだと考えています。しかし、上に挙げたルールで小さく簡単な仕事に分けられることは十分保証できますので、名前を長くしても内容が分かるようにしたほうが不適切な箇所で呼び出されることがなくなり、バグが出にくくなると思います。
多すぎて覚えられないという方は最初の「1クラス80行以下」、あるいは「クラスを単純に書く」だけでも意識してみることをオススメします。
最初から理想的なオブジェクト指向になったコードを書くのは難しいので、まずは望みの仕事が実行されるコードをとにかく書いて、その後にきれいに直していく(これをリファクタリングといいます。)のがいいと思います。
仕事の組み合わせ方
最初に書いたようにオブジェクト指向では小さな仕事を組み合わせることで仕事内容をコンピュータに指示します。
意識したほうがいいルール「Setterは使わない」、「求めるな、命じよ」に書いたようにSetter、Getterを使わないほうがいいと書きました。
これでどうやって仕事を組み合わせるのかというと、ある関数の引数にクラスを指定することによって実現します。
例えば、ゲームオーバーの処理を例に具体的に示します。
class HP{
private int hp;
public boolean deadJudge()
{
if(hp<=0)
{
return true;
}
return false;
}
}
--------------------------------------
class GameOver{
public void execute(HP PlayerHP)
{
if(PlayerHP.deadJudge()==false)
{
return;
}
(GameOverの処理)
}
少しおかしいかもしれませんが、雰囲気は伝わるのではないかと思います。