この記事は何
僕はクラス内でインスタンス変数を使うとき、保守性の高いコードを書くうえでいくつか意識をしていることがあります。
意識していることの紹介と、なぜそれを意識しているのかを紹介します。
基本方針
まず初めに、意識していることを紹介する前に、クラスをどのような考えで作っているかを紹介します。
僕はクラスを以下のような考えで実装しています。
もちろんどれも例外はあるので、「できるだけそのようになる」ということを意識しているというつもりで読んでいただけると嬉しいです。
- イミュータブルである
- メソッドの返り値はコンストラクタで受け取った値だけで決まる
- クラス内部の実装が変更に対して閉じている
Open-Closedの原則と、参照透過性が高く、安全に使いやすいクラスを作ることを意識しているため、上記のような部分を意識しています。
今回は、上記のような実装を実現するため、特にインスタンス変数を使うときに意識していることを紹介ます。
1. インスタンス変数は初期化を行なった後には値を変更しない
インスタンス変数は基本的に一度値を入れたら変更をしないようにしています。
インスタンス変数の値を変える場合は、何かしらの状態変化が起こっていることが大半だと思います。
そして状態が変わっている場合、そのインスタンス変数を参照しているメソッドの返り値も変わる可能性が高いです。
そのようなクラスになっていると、クラスを参照する側からは現在どのような状態になっているかを常に意識をして実装を行う必要があり、実装ミスを誘発しやすくなると考えています。
2. setterメソッドを外部に公開しない
1の考え方に従っていれば結果的にそうなるかもしれませんが、基本的にsetterメソッドは外部に公開しないようにしています。
setterメソッドはインスタンス内部の状態を外部から直接変えることができてしまう処理です。
また、setterで値を変える場合は、インスタンスを違う目的の処理に使いまわそうとしている場合が大半だと思います。
その場合はsetterメソッドでインスタンス変数を上書きするのではなく、インスタンスを生成し直す実装で代替するようにしています。
3. クラス内部ではgetterメソッドを経由して値の参照を行う
メソッドの実装など、クラス内部でインスタンス変数を参照する場合は、直接インスタンス変数を参照するのではなく、getterメソッドを介して参照するようにしています。
このようにすることで、インスタンス変数で受け取る値のインターフェースなどが変わった場合も、Getterメソッドの修正をするだけで内部の処理の修正を行うことができ、内部処理の変更をできるだけ小さく抑えることができると考えています。
サンプルコード
あげた3つの要素を含めたクラスのイメージをRubyで作ってみました。
もちろん全てのクラスをこのように実装することはできないと思いますが、できるだけ紹介した内容を意識した実装を行うようにしています。
class Sample
private attr_reader :foo # 内部での参照用にgetterメソッドを定義する
def initialize(foo)
@foo = foo # @fooはコンストラクタでの初期化以外では代入しない
end
def bar
@bar ||= foo.baz # 変数を直接参照しない。@barはこのメソッド内で一度だけ値が代入される
end
end
最後に
最後までお読みいただきありがとうございました。
Devトークも公開しているので、もし直接話してみたい、と感じていただけた方はぜひDevトークの方も「話したい」を押していただけると嬉しいです!