クラスを使うタイミング

クラスとは

以下のように考えれば良い。

  • データのまとまり。レコード
  • プログラムを実行するためのインターフェース (API)
  • 計算に必要なデータをまとめるためのコンテクスト
  • 機能のまとまり

Classの定義

例えば遺伝子のクラスは以下のように定義できる。

// 遺伝子クラスの定義
class Gene(val name:String, val chr:String, val start:Int, val end:Int, val strand:Strand)

valをパラメータ名に付けると、

val g = new Gene("gene1", "chr1", 10000, 20000, Strand.Forward)
g.name 

とクラスの外部からパラメータにアクセスできるようになる。何もつけないとクラスの内部でしかパラメータにアクセスできない。Javaではパラメータにアクセスするためのgetter/setterを用意する、あるいはパラメータにpublic finalと付けるのが推奨されていたが、Scalaではパラメータにはvalを使ってimmutableにして副作用を避けるのが基本で、そうしたプログラミングが苦にならないよう配慮されている。

遺伝子を定義したらExon, Intronなども同じように定義できる。

// Exon
class Exon(val chr:String, val start:Int, val end:Int, val strand:Strand)

遺伝子もexonもゲノム座標中の区間として考えると同類なので、共通部分をGIntevalとして抽出してみる。

// genome中の区間[start, end)を表すクラス
class GInterval(val chr:String, val start:Int, val end:Int, val strand:Strand)

GIntevalクラスを継承するようにしてGene, Exonを書き換える。

  • このような大きな変更の前にはgit commitしておくとよい。commit前に変更を始めてしまった場合は、git stash -> git stash branch (new branch name)のコンボ。

Gene, ExonをGIntervalから継承させる。

class Gene(name:String, chr:String, start:Int, end:Int, strand:Strand) 
	extends GInteval(chr, start, end, strand)

class Exon(chr:String, start:Int, end:Int, strand:Strand) 
	extends GInteval(chr, start, end, strand)

Gene, Exonのコンストラクタの引数からvalが消えている。こうすると、親クラスと同名のパラメータが二重定義されないように、Scalaのコンパイラが頑張ってくれる。(変数を上書きしたい場合は、overrideを付ける)

入力量はさほど減っていないが、これでGene, Exonに共通する操作はGIntevalで定義すればよくなった。たとえば、区間の交差を判定するメソッドをGIntervalに追加すると、Gene, Exonで共通に使えるようになる。

class GInterval(val chr:String, val start:Int, val end:Int, val strand:Strand) {

	def intersectWith(other:GInterval) = {
		chr == other.chr && start < other.end && other.start <= end
	}
}

交差する区間をsweepする例で定義したクラスも、GIntervalから派生したクラス全般に適用できる。

comments powered by Disqus