rpscala2011 0601
TRANSCRIPT
Scalaでプログラム作ってみたので
Scalaの作法を教えてくださ
い
自己紹介 (株 ) オープンストリーム 所属 twitter yangiYa COBOL → Java (→ Scalaになれたら。。 )
会社ではあまりプログラム作る機会がなくなってきた。欲求不満
Scalaは去年の秋ぐらいからはじめた Scalaの書籍で読んだもの
Scalaプログラミング入門 デイビッド・ポラック (日経 BP社 )
プログラミング Scala Dean Wamloer/Alex Payne (オライリー )
作ってみたものの説明
行列モデルを Scalaで実装
利用用途行列で表現できる概念に汎用的に利用
「物体」 (ゲームのキャラクタなど )の座標
「物体」の速度 「物体」の大きさ 将棋、囲碁、チェス、オセロ、などの駒の位置
要求仕様(モデルの値)1.座標は、x,y,z• x,y,z のデフォルトは Double• 平面上のオブジェクト表現の場合は、 x,yだけで管理したい
• x,y,z を整数で管理したい場合もある。(整数同士の計算結果が少数点以下の数字にならないようにしたい)
要求仕様(モデルのサービス)1.行列の加算( 2,1 ) + (3,1) = (5,1)
要求仕様(モデルのサービス)• 行列の k倍( 2,1 ) × 3 = (6,3 )
インタフェース設計trait Matrix {
/**[[scala.Int]]型の x 座標 */
def xByInt: Int
/**[[scala.Int]]型の y 座標 */
def yByInt: Int
/**[[scala.Int]]型の z 座標 */
def zByInt: Int
/**x 座標 */
def x: Double
/**y 座標 */
def y: Double
/**z 座標 */
def z: Double
}
インタフェース設計 /** この行列と引数の行列の和を返却する
*/
def add(matrix: Matrix): Matrix
def +(matrix: Matrix): Matrix = add(matrix)
インタフェース設計 /** この行列の実数倍 (引数 ) を返却する
*/
def product(scalarValue: Long): Matrix
/** この行列の実数倍 (引数 ) を返却する*/
def product(scalarValue: Int): Matrix
/** この行列の実数倍 (引数 ) を返却する*/
def product(scalarValue: Double): Matrix
インタフェース設計 /** この行列に実数倍 (引数 )した行列と、この
行列との和を返却する */ def productValueAndAdd(scalarValue: Long):
Matrix
/** この行列に実数倍 (引数 )した行列と、この 行列との和を返却する */
def productValueAndAdd(scalarValue: Int): Matrix
/** この行列に実数倍 (引数 )した行列と、この 行列との和を返却する */
def productValueAndAdd(scalarValue: Double): Matrix
要求仕様(非機能)• 低いオブジェクト生成コスト頻繁に利用されるバリューオブジェクトを想定
• スレッドセーフ複数のスレッドから利用されることを想定
3.利用者インタフェースのみに縛られる1. 利用者は具象クラスや内部実装に縛られたくない
2. インタフェースを守る範囲で内部実装は自由に変えたい
非機能要求からの方針1. オブジェクト生成コストは小さいほうがいい
– オブジェクトが使うメモリは小さく– オブジェクト生成時の処理ロジックは少なく
• イミュータブル– 複数スレッドからも安全に使用できる
• インターフェースを維持し、かつ、利用局面に応じた具象クラスを使えるようにする
• オブジェクト生成は、 javaの staticファクトリーメソッドのようなイメージで
• (Javaの例 ) java.util.Calendar#getInstance()==>Scala では、 object の apply メソッドがよさそう
モデル設計 (一部抜粋 )
MatrixWithDouble
x:Doubley:Doublez:Double
MatrixWithInt
xByInt:IntyByInt:IntzByInt:Int
MatrixWithInt2D
xByInt:IntyByInt:Int
zByInt:Int = 0
MatrixWithDouble2D
x:Doubley:Double
z:Double = 0.0
Matrix
x:Doubley:Doublez:DoublexByInt:IntyByInt:IntzByInt:Int
<<インタフェース >>
オブジェクト生成object Matrix { //・・・コンパニオンオブジェクトと呼ばれる?
def apply(x: Int, y: Int, z: Int): MatrixWithInt = MatrixWithInt(x, y, z)
def apply(x: Int, y: Int): MatrixWithInt2D = MatrixWithInt2D(x, y)
def apply(x:Double,y:Double,z:Double): MatrixWithDouble =
MatrixWithDouble(x, y, z)
def apply(x: Double, y: Double): MatrixWithDouble2D =
MatrixWithDouble2D(x, y) }
機能仕様(=テスト仕様)抜粋assert(Matrix(1, 2, 3) === MatrixWithInt(1, 2, 3))
val mtx10_20_30Double = MatrixWithDouble(1.0, 2.0, 3.0)
assert(Matrix(1.0, 2.0, 3.0) === mtx10_20_30Double)
assert(Matrix(1.0, 2, 3) === mtx10_20_30Double)
assert(Matrix(1, 2) === MatrixWithInt2D(1, 2))
assert(Matrix(1.1, 2.2) === MatrixWithDouble2D(1.1, 2.2))
機能仕様(=テスト仕様)抜粋val mtx21_32_0 = MatrixWithInt2D(21, 32)
assert((Matrix(1, 2) + Matrix(20, 30)) === mtx21_32_0)
assert(Matrix(20, 30) + (Matrix(1, 2)) === mtx21_32_0)
val mtx111_222_333 = MatrixWithDouble(11.1, 22.2, 33.3)
assert(Matrix(10, 20) + Matrix(1.1, 2.2, 33.3) ===
mtx111_222_333)
assert(Matrix(1.1, 2.2, 33.3).add(Matrix(10.0, 20.0)) ===
mtx111_222_333)
機能仕様(=テスト仕様)抜粋
var m1: Matrix = Matrix(1, 2)
var m2: Matrix = Matrix(10, 20)
assert(m1 + m2 === MatrixWithInt2D(11, 22))
実装:オーバーロードとオーバーライドsealed trait Matrix {
/** この行列と引数の行列の和を返却する */
def add(matrix: Matrix): Matrix = Matrix(x + matrix.x, y + matrix.y, z + matrix.z)
}
private[core] abstract trait MatrixWithIntBase extends Matrix {
def add(point: MatrixWithInt): MatrixWithInt = MatrixWithInt(xByInt + point.xByInt, yByInt + point.yByInt, zByInt + point.zByInt)
def add(point: MatrixWithIntBase): MatrixWithIntBase = MatrixWithInt(xByInt + point.xByInt, yByInt + point.yByInt, zByInt + point.zByInt)
}
実装:オーバーロードとオーバーライドfinal case class MatrixWithInt2D private[core](xByInt: Int, yByInt: Int)
extends MatrixWithIntBase with Matrix2D {
def add(matrix: MatrixWithInt2D): MatrixWithInt2D = MatrixWithInt2D(xByInt + matrix.xByInt, yByInt + matrix.yByInt)
def +(matrix: MatrixWithInt2D): MatrixWithInt2D = add(matrix)
override def add(matrix: MatrixWithIntBase): MatrixWithIntBase = matrix match {
case pointWithInt2D: MatrixWithInt2D => add(pointWithInt2D) case pointWithInt: MatrixWithInt => add(pointWithInt) case other => super.add(other) }}
機能と実装の説明おわり
もっとやるとしたら (今回やってません)
1.機能要求1.行列の回転
2.非機能要求– Flyweight パターン2.オブジェクトキャッシュ
感想と
知りたいこと
感想 - Scaladoc
classと objectが横並び
コンストラクタを privateにしたら表示されない
リンクはもっと柔軟にかけたらいいのに
Scaladoc リンクはもっと柔軟にかけたらいいのに
1.[[クラス名フル修飾 ]] でリンクになる–パッケージ名を書くの面倒。
importされているのは、シンプルなクラス名だったらいいのに
• メソッドへのリンクつくれないの?なんか方法ないですか。おしえてください
3.Scaladoc文法を書いてあったページがなくなっているような。。。(Scaladoc 2 Documentation for Authors)
Scaladoc コンストラクタを privateにしたら表示されない
2.private コンストラクタが表示されないことはうれしい
• 利用者に、どうやってインスタンス化するかは、ドキュメントに書くしかないか?–誰もが迷うことなくインスタンス化する方法を把握できればいいのに。いい方法ないでしょうか。
Scaladoc classと objectが横並び
1.コンパイルした結果の1."class Hoge"と "object Hoge"は型の関係はなにもないはず
2.コンパイル後のクラスファイル名ってホントはちがう
1.Hoge、 Hoge$ のようなかんじ。。。
–設計上、暗黙の守るべきルールがあるのかではなかろうか?教えてください
object と applyメソッド今回作ったものの中では。。。 コード再掲
sealed trait Matrix
object Matrix { def apply(x: Int, y: Int, z: Int): MatrixWithInt = MatrixWithInt(x, y, z) def apply(x: Int, y: Int): MatrixWithInt2D = MatrixWithInt2D(x, y) def apply(x: Double, y: Double, z: Double): MatrixWithDouble =
MatrixWithDouble(x, y, z) def apply(x: Double, y: Double): MatrixWithDouble2D =
MatrixWithDouble2D(x, y)}
ファクトリ役として使った
object と applyメソッド1. コンストラクタより、 applyメソッドのほうが、柔軟にインスタンス生成できる1. 生成する型を柔軟に切り替えられる2.シングルトンを返却したり、こっそりインスタンスをキャッシュすることもできる
3.利用者のコードを変更せず、内部実装を切り替えられる
1. こっそり、非パブリックなインスタンスを返却する用変更なんてことも可能
4.使用者が意図的に生成するインスタンスの型を決めたいなら、applyメソッドで作らず、 "createHoge"みたいなメソッドのほうがいい
5.コンストラクタより劣ること1.めんどくさい
object と applyメソッド1.コンパニオンオブジェクト
– 使命はなに?おしえてください
1.classと同名の objectをこうよぶのか?2.case class Foo() 、 object Foo
val foo :Foo = Foo後ろの Foo はオブジェクトなのかクラスなのかややこしい
• classと objectが関係あるよとの意思表示としてobject Foo extends Foo とかしたほうがいいとかあるのかな。。
おわり
ありがとうございました