読者です 読者をやめる 読者になる 読者になる

きどたかのブログ

いつか誰かがこのブログからトラブルを解決しますように。

staticメソッド/変数について思うこと

しばしばこの思いを忘れてしまうのだが、
staticメソッドはあまり使いたくないな、と思うのが本音。


よく共通的な役割を持つメソッドを、superクラスに持たせるか、Utilityのstaticメソッドにするか悩む。
無論、クラスの役割として意味のあるものであればsuperクラスにするのだが、
それは本当にクラスの役割か?と思う。
多くの場合、それは実装をsuperクラスに持たせてコードを減らしているに過ぎず、
本当の意味でのオブジェクト指向とはほど遠い。


では、Utilityのstaticメソッドにしてしまうと、テストが難しくなる。
だったら、privateなクラス変数/インスタンス変数にUtilityのインスタンスを持つのが良いと思う。


では、その際にUtilityはSingletonパターンを使うべきか?
よくあるgetInstance()のstaticメソッドは、2つ以上の役割を持つことになり良くないとする人がある。
単一責任の原則か。
まあ、その理論でいくとそうだなぁ。
では、1枚Factoryを挟んで、Singleton管理させることにする。
さて、Factoryが返す型は、インタフェース?クラス?って話があるのだが、
わざわざクラスにする必要もないのでインタフェースにでもしておけばいい。
これでMockは作り易い。


さて、クラス変数/インスタンス変数のどちらにUtilityのインスタンスを持たせるか?
正直、どちらでも良いのだが、finalは付けないことだ。
static finalはリフレクションで変更が出来ず、例外となる。
finalもリフレクションで例外は出ないものの変更は出来ない。
どちらかというとクラス変数に持つのかな。


Utilityのメソッドは、ほとんどの場合、関数的になってしまう。
だからどうしたって思う面もある。
オブジェクト指向言語と言えども、メモリのことを考えると至るところが関数的になってしまうし、
最終的にデータを処理する部分ってのはどうしても関数だと言えるんじゃないかな。


JavaBeanをメソッド引数で引きまわすようなタイプのものは、ほとんど関数的な書かれ方になる。
自分がやってる仕事は関数だなぁって昔から思ってる。
スレッドセーフかつシングルトン前提になったりとすると、それも関数になりがちだ。
なんかここらへんの言葉遣いに自信がないんだけど、
別の言葉で言い表すと、OOAじゃない箇所はどうしても出てくるというのが正しいだろうか。
つまり、データに直接触るオブジェクトが、多くなってしまいがちで、
それではOOAというか、DOAだな。
POAとまでは言わない。
COBOLなんかみたいに同じデータに対してプログラム個々にレコードフォーマットを作ったりすることはほとんどない?
いやいや、JavaでもCOBOLみたいなのは起こる。
同じデータ構造に対して別のJavaBeanをじゃかじゃか作るんだったら、結局同じことになるんじゃないのか。
XMLをオブジェクト化するとして、JAXBならDTDとかからクラスを作るだろうけど、いろんな人が作ると、
パッケージ名だけ違う同じようなクラスの出来上がりってことがあるだろう。
スキーマを意識して、エレメントのレベルでJavaBeanを管理出来てないとダメなんだろう。
ファイルやDBもそうだ。
そのへんをやらないと、オブジェクト指向言語でもPOAっぽい状態になる。
そうならないためには、データの構造を共通化?出来るようにDOAから始めるんだろうな。
DOAから始めた後に、そのデータに対して直接触れるオブジェクトを極力減らすように考える感じ。
出来るだけそのデータ構造を触るオブジェクトを減らすようにしないと、
DOAの抱えていた「データ構造が変更された時に修正が多岐にわたる」という問題に直面する。
DTOを使っていると、データの移し替えがけっこう面倒なことになる。
移し替えてるのが各ロジックになってるんだろうから。
自分の言うDTOは、PofEAAとは違っていて、どちらかというとドメインオブジェクトよりだ。
いや、そもそもトランザクションスクリプトだからドメインオブジェクトと言うのもおこがましい気もする。
とりあえず分散環境におけるDTOということではないと言っておこう。


ようやくいいのが見つかった。
http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html
この図(DAOパターン)のTransferObjectにsetする部分が、
いろんなBusinessObjectに散在しちゃうことがあると言いたいだけだ。


なんか、すっげー脱線したな。。。
あんまり小難しい話を書くのは好きじゃない。
言語仕様やVM仕様は強いけど、設計に強いわけじゃないから。


さて、みみっちい話の続き。
public static final String HOGE = "HOGE"のように、
むやみにpublicでstatic final宣言するのは嫌いである。
クラスAのpublic static final String HOGEを、
クラスBのstatic final Stringで、A.HOGEを参照していると、コンパイル時に実値が埋め込まれてしまう。
別にBの変数上に定義するしないは関係なくて、メソッド内でA.HOGEを使っていても埋め込まれる。
また、protected static finalも同様で、サブクラスがスーパークラスのそれを返すメソッドを持っていた場合、
コンパイル時に値が埋め込まれている。単純な継承のみではこの問題は出ない。
よほど変更する気のない値でもない限りpublic static finalにはしたくない。
異なるjar間で、そういうのがあった場合、jarの差し替え時にその値が変わっていると、
すでに埋め込まれてた古い値で動く輩がいるという状況になる。


定数クラスなり、インタフェースクラス上での定数宣言なりも同じ話だ。
こういうのを回避するのには、定数クラスであっても定数を返すstatic getterを設けるか、
変更される危険性を残しつつfinalを外してしまうかだ。
この方法はインタフェースクラス上の変数には使えない。
もしくは、Enumにしてしまった方がいい。
static final にEnumオブジェクトもしくはEnumのname()を突っ込むならば、
それはコンパイル時には解決されず、クラス初期化時に代入されることになる。


何が言いたいのか?
(定数クラスを前提として)「定数はpublic static final」と言ってる人の多くは、
上記のことを知らないことがあるということを言いたい。
CI全盛の時代にそこまで気にすることもないかも知れないけど。