プログラムdeタマゴ

nodamushiの著作物は、文章、画像、プログラムにかかわらず全てUnlicenseです

オブジェクト指向初心者にありがちな後に後悔しない為の7の事inJAVA

私はプログラム歴2年と半、オブジェクト指向プログラミング(以下OOPと略記)歴1年とちょっとというかなり浅いプログラマーである。
私が一年前、OOP初心者時に書いたプログラム(1万行ぐらい)を現在誠意修正作業中なのだが、全く持って読めた物ではない。
この記事はOOP初心者にポリモー何とかだとか、再利何とかだとか、隠蔽何とかだとか、インターフェ何とかだとかそんなことを理解してプログラムしようと啓蒙する為の記事ではない。
何時かあなたが初心者の頃に書いたソースを修正するときに後悔と過去の自分に殺意を向けない為の記事である。


ポ何とかのあたりの事は、いかに述べていく事を守っていれば、1年後のあなたがEclipseをバンバン使いこなしてスムーズに勝手にやってくれるはずだ。今のあなたが気にする必要はない。


1:やたらオーバーロードしない

これはOOPではないが、Perlだとか、JavaScriptだとかCだとかから初めてJavaに触ると、同じ関数名を定義できるのが嬉しくてしょうがないのだ。ソースは私
オーバーロードは非常に便利で、静的型付けを強制するJavaの強みなのだが、これも使い方を間違えると非常に読みにくいコードになる。ついでに、全部の関数に処理が長々書いてあると修正が鬼のように面倒である。
オーバーロードの基本は一つのメインとなる関数を作成し、他の関数は、その関数を呼べる形に変数に変換を加えるだけにすると、後の保守が簡単だ。

public String getString(Object o){
	return o.toString();
}

public String getString(int i){
	Object o = new Integer(i);//getString(Object)で使える形に変換
	return getString(o);//この関数自体は処理をせずに結果を返す。
}

2:変数は全てprivateにする

OOP初心者の頃は、クラスとクラスの機能の分離を意識しながらプログラムするのは難しい。その結果できあがるのが、クラスとクラスが密にくっついたスパゲッティコードである。ソースは私。
特に変数はいったい何をpublicにし、何をprivateにするべきなのか、判断がつきにくい。


だから全部盲目的にprivateにしてしまおう。変数の取得変更は全てgetterとsettterから行う。たとえ、getter,setterがやっていることが渡された変数をそのまま設定し、getterがそのまま値を返すだけで、必要性が感じられないとしても変数をpublicにしてはいけない。getter,setterを通すソースでも同じように動くのだから、getter,setterを通しておこう。
こうしておくと、少なくとも変数の取得や変更の責任がgetter,setterにあることになり、管理や把握がずいぶんとしやすくなる。本当にpublicで良いかどうかの判断は後の時代のあなたがやってくれるだろう。


信用すべきは今の自分ではなく、何時か来るあなただと肝に銘じておこう。

3:コンストラクタからthisをもらさない。

OOP初心者の頃は、クラスとクラスの機能の分離を意識しながらプログラムするのは難しい。その結果できあがるのが、クラスとクラスが密にくっついたスパゲッティコードである。ソースは私。

とくに、コンストラクタから、別のクラスに「this」を渡してはいけない。
これは処理を追ったり後にクラスを分離するのを大変にするだけでなく、並行処理プログラムでは致命的バグになる可能性が高い。
コンストラクタを書いていて「関数名(this)」や、「new クラス(this)」なんて物が出たら、本当にそれが必要なのかどうか考えよう。
これが許可されるのは

  • 関数や新しくクラスを作る作業によって、そのインスタンスのメンバにアクセスされない事
  • 関数や新しくクラスを作る作業によって、別なクラスからそのインスタンスが見えるようにならないこと。

この2つを満たすときだけである。


4:名前のある内部クラスを作らない

OOP初心者の頃は、クラスとクラスの機能の分離を意識しながらプログラムするのは難しい。その結果できあがるのが、クラスとクラスが密にくっついたスパゲッティコードである。ソースは私。


インナークラスは非常に便利で記述力を大幅に向上させてくれる。
が、おおよその場合、そのクラスは別なファイルに移動することが出来る。別にコンパイルしたら生成されるクラスファイル数に差はないので多少変数にアクセするのが大変になろうと分離しておくのがよろしい。
分離した所為で後で後悔するということはまず無い。分離しないが為に後悔ならする。

ほんっっとうに分離することが、私たちを3次元空間の束縛から解き放ち2次元へ誘う事なみに難しい場合だけ内部クラスを作ろう。

もちろん、Runnableインターフェースを実装した無名内部クラスなんかは作ってもよろしい。


5:汚い実装をするぐらいなら標準APIから使えそうなのを探す

Javaの標準APIは非常に多く、いったい何があって何がないのか調べるのはかなり大変である。初心者の内はAPIを探しきれず、特になまじ今までCやらでプログラムしていたことがあれば自分で実装すればいいやと思う。
OOPというか、Javaであるが、再利用はOOPの特徴でもあるので入れておく。

おそらく、君が使いたい物が抽象的な機能であればあるほど、標準APIで代用が可能だ。もちろん、直接には無い場合も多いが、いくつかをちょろっと組み合わせれば簡単に作れる場合も多い。
そして、それは君が作るよりもずっとすばらしく、信用性、実行効率、拡張性、可読性の高い物だ。
自分で探しきることが出来ないなら、身近なJavaプログラマーに聞くもよし、はてなで聞くも良し、2chのJavaを扱うスレッドで質問するも良し、誰かが答えてくれるだろう。
もちろん、これこれこうな物を作ってくれ、というとほぼ誰も反応はしてくれないだろうが、これこれこうな物に使えそうなAPIはあるか?ならきっと誰かが答えてくれる。

6:条件分岐に整数値や文字列を使わないでenum型を使う

enumがJavaで実装されたのは実はつい最近なのだが、これはOOPの強みである。
あらかじめ決まった値だけを考えて条件分岐すると言うことは多い。その場合、enumを使う癖を付けておくと良い。
思わぬ値が来たりするというバグが未然に防げるし、enum型も結局はクラスなので将来の君がポリモーフィズムに変更しやすくなる。Cと違って列挙型が機能を持てる、ここがOOP(というかJavaの?)である強み。なんだけど、ここは今はどーでもいい
可読性も向上する場合が多い。
もちろん、enumを使う弊害というのもあるのだが、とりあえず初心者の段階でそれが出てくることはないだろう。ちなみに、enumの最大の欠点は、ファイルを修正することなしにenumで扱える値を拡張することが出来ないと言うことである。整数値とかだったら必要になったところで好きなように新たに値を定義できる。


7:一つのクラスにゴチャゴチャ機能を詰め込まない

これが最も重要であるのだが、初心者にはかなり難しい。いや、今の私でも難しい。きちんと自分が作りたい物を把握し、インターフェースをきっちり設計する作業をしないと中々機能を分割することは簡単じゃない。うっかり一つのクラスにいろいろな機能が入ってしまう。
細かく細かく分割すればいいのかというと、むしろ機能を分割しすぎた所為で扱いが難しくなることもある。

ここまでに挙げた内容はプログラムするときの習慣を改善すればすぐに実践できるが、これは言われたからといって出来るかと言ったらNoである。
まず初心者の内は、そのクラスが何を対象として扱うのか、を考えてみると良いだろう。


たとえば、容器というクラスを作ることを考えてみよう


容器は内容物を受け入れ、保持し、取り出す事が出来ればそれで十分だ。
そこに、お茶を抽出する機能や、パスタを電子レンジでゆでる機能や、温度を表示する機能を付けようと思えば付けられるが、これらが不必要だと言うことは簡単に分かるだろう。ついていれば便利だからといって実装してはいけない。これらを実装する必要があるなら、容器を拡張したクラスに実装すればよろしい。


けど、これは判断が難しい。
容器に入っている内容物を表示する機能が必要かどうか。
一瞬Yesと答えたくなりそうだが、答えはNoである。
いったい何を入れたのか、それはラベルが受け持てばいい。あくまで、容器は内容物の受け入れ、保持、取り出しであり、内容物は容器にとってはどうでも良い対象外の話なのだ。






さて、以上初心者の内に後々後悔しない為の7つの事の事を述べてみたが、

ぶっちゃけ全部一度失敗してみて身にしみるのが一番早いけどね!