スタティックメソッドの使い所
注
筆者の雑感です。思いついたことを書いてます。いずれちゃんと調査、考察、推敲を行ってちゃんとした記事にしたいと思います。
はじめに
ある製品のコードを読んでいたらスタティックメソッドが結構、出てくる。メソッドをStaticにする基準を考えたり、調べたりしてみた。まだまとまっていないので同じことを別の言い方しているだけのところもある。
状態を意識しないメソッドはstaticメソッド
状態(フィールド)を意識せず引数のみによって結果が決まるのがstaticメソッド。一番、よく見る説明だが一番、ピンと来ない。字面は理解できるんだけど大雑把すぎて設計上の判断基準がわからん。
状態を意識しないということはインスタンス化する意義がないということ。つまり「クラス」から「オブジェクト」という個体に分化する意義がないということ。
個体に分化するもの
- 人クラスの鈴木さん
- 車クラスのプリウス
分化する意味がないもの
- 電卓(というか計算機能か。つまり機能か。)
static変数を操作するメソッドはstaticメソッド
static変数はインスタンスが作成される前にアクセスされる可能性があるからstaticメソッドで操作するのは理にかなっている。例えばシングルトンパターンのgetInstaneがそうだ。逆にstatiicメソッドを見つけたときにはstatic変数を操作している可能性を疑うと読みやすそうだ。
後記:マルチスレッド環境ならsynchronizedメソッドでやるかな。
自クラスのインスタンスを作成するメソッドはstaticメソッド
インスタンスが作成される前なのでstaticメソッドでしか実現できない。インスンタンスメソッドとstaticメソッドが混在するクラスにおいてstaticメソッドになるのは主に自クラスのインスタンスを作成するメソッドが多いようだ。
特定のデータに関連する処理をするメソッドはstaticメソッド
文字列を操作する場合は以下のようになるのが一般的。
"123".substring(1)
しかし「文字列に関連したメソッド」としてstaticメソッドにすることもできる。
String.valueOf(123)
「〜機能」はstaticメソッド
上と同じことだがこのように言い表すこともできる。
よく「電卓はstaticメソッドでいい」という例えを目にしたが「電卓」=「計算機能」と考えれば腑に落ちる。同じ事象のオブジェクトに注目するのか機能に注目するのかの違いだ。
いちいちインスタンスを作成するほどではないちょっとした操作はstaticメソッド
「〜に関連した操作」や「〜機能」をstaticメソッド化すると極論、どんなインスタンスメソッドでもstaticメソッドにできてしまう。これではstaticメソッドだけになってしまう。
staticメソッドにするときには「いちいちインスタンスを作りたくない」理由があるかが鍵だろう。インスタンス化するほどではないちょっとした処理だ。例えば上記のString#valueOfのケースで言うと、整数123の文字列を作成する前にいちいちIntegerクラスのインスタンスを作成する意味があるか?ということだ。
Integer num = 123; num.toString()
Integerクラスがそのドメインにおいてどの程度、重要な登場人物なのかで判断は変わるだろう。重要でない人物がコード中に現れると読み手は混乱する。本質的でない処理の記述に時間をかけるのは非効率だ。そのドメインにおいて本質的でない細かいデータ操作をstaticメソッドにすると開発作業が効率化して、コードがすっきりするだろう。ユーティリティクラスというのはそういう処理を集めたクラスだ。
メモリ空間に1つしかないものはstaticメソッド
ユーザがnewしてもシステムの制約上、増やせないリソース。DBや他システムへの接続など。
基本はインスタンスメソッド
このようにして見るとオブジェクト思考においてstaticメソッドというのは積極的に採用するものではなく、インスタンスメソッドの欠点を補うために限定的に使用する変則的な技法なのだと思う。
staticメソッドはオブジェクト指向ではない
オブジェクト指向においてはデータと操作は同じクラスにある。よってあるデータや操作を変更したとき、その影響範囲を特定のクラス内に閉じることができる。
staticメソッドでは操作とデータ(プリミティブの場合除く)が別のクラスにある。変更の影響が他クラスへ染み出してしまう。また機能に着目した設計になってしまい、オブジェクト(モノ)に着目した設計にならない。
メソッドのリファクタリングで発生するフィールド操作のないメソッドの扱い
リファクタリングの過程でインスタンスメソッドの一部処理を別メソッドとして切り出すことはよくある。可読性重視で切り出したメソッド内にフィールドに対する操作がないことも時折ある。フィールドに対する操作がないのであればそのメソッドはstaticメソッドにすることができる。このときどうするべきなのだろうか?
- staticメソッドにする?
- インスタンスメソッドのまま?
- フィールドへの操作が発生するように切り出す単位やフィールドの設計から見直す?
気にしたことがなかったが先日、PyCharmでPythonを書いていたら警告されて悩んだ。
staticメソッドは再利用性が低い
staticメソッドはクラス固有なのでサブクラスでオーバーライドできない。つまりスーパークラスでabstract staticメソッドを利用して枠組みを定義して、サブクラスでstaticメソッドを具象化するような使い方(テンプレートメソッドパターン)はできない。再利用を意識したコードによくある、実装部分をごっそり置き換えるような使い方ができない。