凪瀬 Blog
Programming SHOT BAR

目次

Blog 利用状況
  • 投稿数 - 260
  • 記事 - 0
  • コメント - 48909
  • トラックバック - 192
ニュース
広告
  • Java開発者募集中
  • 経歴不問
  • 腕に自信のある方
  • 富山市内
  • (株)凪瀬アーキテクツ
アクセサリ
  • あわせて読みたい
凪瀬悠輝(なぎせ ゆうき)
  • Java技術者
  • お茶好き。カクテル好き。
  • 所属は(株)凪瀬アーキテクツ
  • Twitter:@nagise

書庫

日記カテゴリ

 

2009年5月19日

Kanazawa.processではテスト駆動開発入門を読んで、テスト駆動開発を学びました。その本でのテスト駆動開発のサンプルとして挙がっていたテーマは複数通貨のMoneyを作るというものでした。

Dollarという米ドルを表現するオブジェクトを作り、Francというスイス・フランを表すオブジェクトを作り、そしてMoneyというオブジェクトに統合していくというリファクタリングの過程を経ています。

これをジェネリクスを使って設計したらどうなるでしょうか。

public abstract class Money<T extends Money<T>> {
  public abstract T add(Money<?> m);
}

Moneyオブジェクトに演算用のメソッドadd()を定義します。引き算や掛け算など要求に合わせて各種用意するといいでしょう。

Money<T extends Money<T>> という型変数の宣言方法は自己言及するジェネリクスの稿で 取り上げた手法です。add()の戻り型をTとしておくことで具象型自身の型を返させることができます。

これをDollarやFrancでオーバーライドして実装します。

public class Dollar extends Money<Dollar> {
  @Override
  public Dollar add(Money<?> m) {
    // ...
  }
}

こうすることで、Dollarのadd()はDollar型で返るわけです。 しかし、これだけだとDollarやFrancといった通貨単位ごとにadd()の実装を施さねばなりません。 protectedなファクトリーメソッドを用意して、オブジェクトの生成を具象型に委譲することで スーパークラスであるMoney型で統一的にadd()を記述することができます。

public abstract class Money<T extends Money<T>> {
  /** 為替レート */
  Map<Class<? extends Money<?>>, Map<Class<? extends Money<?>>, Double>> rateMap;
  /** 値 */
  protected double value;
  /** 加算メソッドの共通実装 */
  public T add(Money<?> m) {
    double rate = rateMap.get(this.getClass()).get(m.getClass());
    return getInstance(this.value + m.value * rate);
  }
  /** 具象型の生成 */
  protected abstract T getInstance(double value);
  /** コンストラクタ */
  protected Money(double value) {
    this.value = value;
  }
}

こうした手法で通貨単位ごとに型を作って何が嬉しいかと言えば、ドル建てである場所にスイス・フランの型が紛れ込んだ場合に コンパイルエラーにすることができるという点です。

設計にはいろんな手法がありますし、それぞれにメリット、デメリットがあるので要求に合わせて柔軟に選べるようにしたいものですね。

posted @ 0:54 | Feedback (0)
 

Kanazawa.processというテスト駆動開発の勉強会が開催されたので参加してきました。話題に上がったネタの技術的な部分をいくつか補足しておきます。

副作用

この勉強会ではテキストとしてテスト駆動開発入門を用いました。 第一章で出てくる「副作用」という表現。あまりにも一般用語然としていますが、プログラミングの専門用語として「副作用」と言う場合、 状態の変更によって得られる結果が変わることを言います。 wikipediaにも 副作用 (プログラム)という項がありますね。変数の破壊的代入というような言い方もします。

関数型言語では関数が副作用を持たない、つまり、同じオブジェクトを引数に渡したとしても、オブジェクトの状態が変わったことによって メソッドの呼び出し結果が異なる、といったことが起きません。そのために変数への代入は初期化のみが許されて、 再代入(破壊的代入)は許されないという特色があります。

その利点にあやかるべく、値を表すオブジェクト(バリューオブジェクト)には Immutableパターン というデザインパターンが適用されることが多くあります。 Javaの標準APIでは、Integerなどのプリミティブ型に対するラッパー型や、BigDecimal型などがこうした設計になっています。 これらは不変オブジェクトと表現されることもあります。

今のJavaには破壊的代入をコンパイラにチェックさせる機構がありませんが、JavaVM上で動作するScalaなど、 関数型の流れも汲み入れた新しい言語ではこれをサポートする機構が存在します。 Scalaの場合は変数宣言にvarとvalというキーワードで再代入可能な変数と再代入不可の変数を使い分けることができます。 IT Proの記事がわかりやすいでしょうか。

また、Java7での拡張に含まれる予定のJSR 305では バグを根絶するための各種アノテーションの提案がされています。 マイコミの記事が詳しいですね。 この候補として@Immutableというものも候補に挙がっており、Java7ではコンパイラでチェックできるようになるかもしれません。

不変オブジェクトとGC

Javaの理論と実践: パフォーマンスの都市伝説 でも述べられているように、不変オブジェクトを用いることによって目に見えるようなパフォーマンスの劣化が起こることはありません。 90%のオブジェクトは作られてすぐに廃棄されると言われており、近年のGC(ガーベッジコレクション)は世代管理をして こうしたライフサイクルの短いオブジェクトの回収を効率的に行えるように設計されています。

オブジェクトの再利用を行う機構を作ったとしても、不変オブジェクトの生成と破棄を行うスタイルと 目に見えるほどのパフォーマンスの改善を得ることはなかなかできません。 下手に頑張ったところで残るのは再利用や修正のしにくい醜い設計のプログラムだけです。

ソフトウェア品質の12の属性で取り上げたとおり、 パフォーマンスチューニングによる効率性の向上のためには、柔軟性、相互接続性、保守性、移植性、信頼性、堅牢性、試験性、使用性が犠牲になります。 そこまでの多大な犠牲と、パフォーマンスチューニングにかける時間、労力、そしてわずかばかりの効率性が釣り合うのかを冷静に判断してください。

大規模開発と静的解析

大規模特有の難しさと言うのは「人」が増えることによる開発の難しさなんですよね。 多くの人が関わって作業をすると言ったときに、メンバーの技術的ばらつきは大きくなる。 作業のやり方を統一することも難しいし、何より人間はヒューマンエラーを起こすのです。 これを漏れなく拾うことの難しさ、そしてフィードバックして個々人をレベルアップさせることの難しさがあります。

まぁ、ぶっちゃけて言えば手が回らない。なので、人海戦術はやめにして、機械化しましょう。 システム開発を機械化で高生産にするわけです。ビバ産業革命!

技術を持った人が注意深く作業する必要のある単純作業なんてのは、機械にやらせてしまいましょう。 そう、リファクタリング機能です。EclipseやNetBeansには高度で信頼できるリファクタリング機能が搭載されています。 静的な構文解析に裏打ちされた安定した機械的なリファクタリングを活用しましょう。

FindBugsプラグインなどの静的解析ツールによる機械的なコードレビューを活用しましょう。 命名規約やフォーマットのチェックなんてのはCheckStyleプラグインで機械的にチェックしてしまいましょう。 データ型の不一致をあぶり出す机上デバッグなんてやめて、ジェネリクスを使ってコンパイラでの静的型チェックをしましょう。

技術を持った人が注意深く、ヒューマンエラーと戦いながら退屈な単純作業をやらなきゃいけないなんて そんな低生産なやり方はガーベッジコレクションに回収させてしまいましょう。 もっとも、頭の固いエライヒトがいつまでもそのやり方を参照しているとリークしていつまでも回収できないのですけどね。

posted @ 0:19 | Feedback (3)