デザインパターンについて1 (Bridgeパターン)

今回オブジェクト指向の勉強をするために題材としてデザインパターンを選択した。

デザインパターンとは、よく出会う問題とそれにうまく対処するための設計のパターンのことで、オブジェクト指向における再利用性についてよく考えられた構造を持っています。なのでデザインパターンを勉強すればオブジェクト指向が身につくはず!

進め方は、以下のサイトに書いてあるデザインパターンのサンプルコードを実装、自分でコードを追加、その構造を理解、どのような点で便利であるかを調べることにした。

http://www.ceres.dti.ne.jp/~kaga/frame.html

まずは、Bridgeパターンのサンプルコード、クラス図、出力を見てみることにした。

サンプルコード

クラス図

出力


これらを見てみると、機能構造であるDisplayクラスがあり、表示の仕方を具体化しているDisplayImplクラスに委譲している。そして、Displayクラスを継承しているCountDisplayクラスでは指定回数だけ表示する機能を追加したクラス、DisplayImplクラスを継承しているStringDisplayImplクラスでは文字列を使って表示するというクラスになっています。つまり、機能のクラスと表示のさせ方のクラスに分かれていることがわかる。

使い方としては、Main以下のようにCountDisplayクラスの初期化時にStringDisplayImplクラスのインスタンスを引数として使う。すると、CountDisplayクラス内でStringDisplayImplクラスのメソッドを扱うことができる(コードの中では、openメソッドで使われているrawOpenメソッドなど)ようになる。そして、Main以下では、StringDisplayクラスの振る舞いは気にしなくていいので、CountDisplayクラスのインスタンスを扱うだけで、CountDisplayクラスの機能をStringDisplayクラスの表示のさせ方で使うことができるようになっている。

また、CountDisplayクラスであるd2とd3はmultiDisplayメソッドを使うことができるが、d1は使うことができない。(エラーになる)

なぜこのような構造になっているか理解を深めるために、以下のようにクラスを追加して実装してみた。

コード

クラス図

出力

Main以下を見るとCountDisplayクラスとEmptyDisplayクラスを初期化するときに引数としてStringDisplayImplクラスやCharDisplayImplクラスを使っている。この組み合わせを変えることで、以下のような機能と表示のさせ方が異なる4種類のクラス(に近いもの)を作成できる。

4種類のクラス(に近いもの)のインスタンス

  • d1は線で囲まれた表示のさせ方でmultiDisplayメソッドを使える。
  • d2は()で囲まれた表示のさせ方でdeleteDisplaメソッドを使える。
  • d3は()で囲まれた表示のさせ方でmultiDisplayメソッドを使える。
  • d4は線で囲まれた表示のさせ方でdeleteDisplayメソッドを使える。

このように機能と表示のさせ方のクラスを分けることのメリットは、

  • 機能と表示のさせ方の組み合わせの数だけ異なる種類のクラス(に近いもの)を扱うことができる。
  • 機能を追加するだけで、全ての表示のさせ方に対応できる。(逆も対応できる)
  • 複数の継承関係がある場合でも片方ずつ考えることができる。

ということがわかった。

今回は説明の仕方が難しかったなぁ。もう少し書き直すかもしれない。。。
次回は、Builderパターンについて調べてみます。

Rackについて1

RubyとかLinuxに触り始めて約3ヶ月。次のステップアップとして、有名なgemのソースコードリーディングをしていく。最初に読む題材は読みやすいという理由からRackにしました。

とりあえずRackを以下のサイトで簡単に調べてみた。

http://gihyo.jp/dev/serial/01/ruby/0023

Rackとはrubyにおけるサーバとアプリケーション/フレームワーク間のインターフェイスの役割を果たすライブラリと書いていた。。。つまりどういうことだ?
ということで、今回はこの意味が理解できるまでを書いていきたいと思います。

同じサイトに書いてあったシンプルなアプリケーションsimple_app.rbを実装していろいろ見てみた。

以下のruckupファイルであるconfig.ruも必要になるので実装。

ここで、rackupコマンドを打つとconfig.ruを実行します。以下が出力されたので、WEBrickというWebサーバが立ち上がったことがわかる。

$ rackup
> INFO WEBrick 1.3.1
> INFO ruby 1.9.3 (2011-10-30) [x86_64-linux]
> INFO WEBrick::HTTPServer#start: pid=7997 port=9292

これでブラウザからhttp://localhost:9292/にアクセスできるようになった。また、-sのオプションを付けることでアプリケーション側のコードを書きかえることなくサーバを切り替えることができる。これは便利だ。

$ rackup -s サーバ(thinなど)
> Thin web server (v1.2.11 codename Bat-Shit Crazy)
> Maximum connections set to 1024
> Listening on 0.0.0.0:9292, CTRL+C to stop

なんとなく流れが見えてきたので、ここからはアプリのコードに注目してみる。
まずはcallの引数になっているenvの出力を確認してみる。

 {"GATEWAY_INTERFACE"=>"CGI/1.1", "PATH_INFO"=>"/favicon.ico", "QUERY_STRING"=>"", ・・・・}

他にもSERVER_NAMEとかREQUEST_METHODなどのWebサーバーの情報がハッシュの形で格納されていることがわかった。何か情報が欲しいときはkeyを指定するというインターフェイスだ。そして、戻り値は「ステータス」「ヘッダ」「ボディ」の3つである。コードの中で、ボディは'何見てんだよ'にあたるので、この部分がブラウザに表示される。

また、コード中にあるenv['REQUEST_METHOD']を出力すると、"GET"か"POST"が出力される。一般的には表示させるときには"GET"で、プログラムに送るときは"POST"となっていてる。実際にはhttp://localhost:9292/にアクセスすると、env[REQUEST_METHOD]が"GET"になりブラウザにボタンが表示されます。そのボタンをクリックすると、env[REQUEST_METHOD]が"POST"になるので、条件分岐によりブラウザに表示させる内容が切り替わります。このようにenvにアクセスすることでサーバの情報をアプリ側で簡単に扱うことができます。

まとめると、サーバとアプリのインターフェースであるRackとは、

  • アプリの書き替えなしでサーバを切り替えることができる。
  • サーバの情報をアプリ側で簡単に扱うことができる。

ということでした。

今回はまだコードを読むところまではいけなかったが、Rackが便利だというのは理解した。まだまだハッシュのenvにkeyを指定して情報を取得するなど、不便に思うところも少しだけあったので、次回はより扱いやすくするためのライブラリであるRequestやResponseについて書きたいと思います。

TDDBC横浜に参加してきました

TDDBC横浜に参加してきました。

プログラミング経験も浅く、勉強会的なものも初参加で緊張していたのですが、ペアの方やスタッフの方々に恵まれ楽しく参加することができました。ありがとうございました。

ブログを書くのが個人の責務ということでブログにも挑戦してみました。今回は、和田さんの基調講演のまとめ、ペアプロを通して学んだこと、KPTの3つのことを簡単に書いてみました。

基調講演

  • ソフトウェア開発の三本柱
    • バージョン管理     →セーブポイントのようなもので一番大事。
    • テスティング      →今回のTDDBC横浜で学びにきたもの。
    • 自動化         →さらに一歩踏み込んだ形として、おかしいときだけ人間に教えるとうのもある。
  • TDDのこころ
    • 一つずつ、少しずつ   →小さいタスクに割るというのは技術で、練習がいる。
    • ひとりずつ仕留める   →複数の敵を一つずつ仕留めていく。
    • すばやくまわす     
    • 自分が最初のユーザ   →まず利用者の視点でテストを書く。どういうのが欲しいのかを考える。(WHAT,WHY)
    • 道具にこだわる     →開発者として大事なこと。
    • 不安をテストに     →不安を克服できるように書く。
    • 祈るのではダメ     →テストがないとやってみないとわからないという状態になってしまう。
    • テストが命綱      →義務感ではなく、不安のない状態を維持するためにテストする。
    • TDDの真の目的は不安の克服と健康の維持
  • TDDBC導入の効果
    • 実際の事例から実装時間は二割増え、バグが半分減る。
    • 実装時間は増えるがデバッグ工数は減るので、結果として開発工数は減る。
  • サイクル
    • テストを書く    → 実行させ失敗させる(Red)
    • 目的のコードを書く → テストを成功させる(Green)
    • リファクタリング(Refactoring)


ペアプロ演習

ペアプログラミングの課題は野球の打率計算に関するものでした。ペアの方と話し合い、まずは動かしてから、Refactoringをするということに決めたので、3つの引数をただ代入し計算していくというメソッドを作りました。最初の流れを簡単に書いていきます。

  • テストを作ったらまずRedになることを確認。(メソッドがないので)

  it "打席数、打数、安打数を受け取り打率を計算できること" do
      BaseBall.batting_average(515, 455, 135).should  == 0.297
  end

  • 戻り値のみのメソッドを作成しGreenになることを確認。(テストのテスト、仮実装)

  def self.batting_average(box, bat, hit)
      0.297
  end

  • 実装を始める。Greenの状態を維持しながらRefactoring。RedのときはRefactoringしてはいけない。

  def self.batting_average(box, bat, hit)
      (hit.to_f / bat.to_f).round(3)
  end

その後、ある程度進めていくと複数の選手の打率が必要になり、メソッド自体を変更しようということになりました。しかし、メソッドやクラスの変更をすると、テスト自体の実装を変更しなくては全てRedになってしまいます。ここで、パラレルチェンジというものに挑戦しました。パラレルチェンジとは古いテスト、コードを残したまま新しいテスト、コードを作成し、両方Greenになってから古い方を消すというものです。パラレルチェンジの流れを簡単に書いていきます。

  • 最初に古いメソッドの名前を変更。

  it "打席数、打数、安打数を受け取り打率を計算できること" do
      BaseBall.old_batting_average(515, 455, 135).should == 0.297
  end

  def self.old_batting_average(box, bat, hit)
      (hit.to_f / bat.to_f).round(3)
  end

  • 新しいテストを作成。

  before do
    @aoki = BaseBall.new(643, 583, 170)
  end

  it "打席数、打数、安打数を受け取り打率を計算できること" do
      @aoki.batting_average.should == 0.297
  end

  • 新しいメソッドを作成。Greenになることを確認してから実装しRefactoring。

  def initialize(box, bat, hit)
      @box = box
      @bat = bat
      @hit = hit
  end

  def batting_average
      (@hit.to_f / @bat.to_f).round(3)
  end

  • この段階で古いテストと新しいテストがGreenになっていることを確認してから古いテストとコードを消します。このようにパラレルチェンジをすると安全に変更することができます。

演習を通して大きな修正を含めて一連の流れを体験することができました。これから何回も繰り返すことで自分のモノにしていきたいと思います。あと課題は演習中に全て終わらなかったので、空いている時間でチャレンジしたいと思います。

KPT

TDDBC横浜を通して感じたKPTを書いていきます。

  • Keep
    • 勉強会に参加すること。
    • 参加した内容をブログに書くこと。
  • Problem
    • 最初の机に座っていた方たちとペアの方、スタッフの方としか話せなかった。
  • Try
    • 懇親会に参加する。
    • 開発環境にこだわる。
    • もっと本を読む。

最後に

最後にスタッフの皆様、参加者の皆様、ありがとうございました。たくさんの学び、気づきがありとても有意義な一日でした。今後も、勉強会など積極的に参加していこうと思います。