BitBucket で Git のプライベートリポジトリを 無料で作る方法
Rails で簡単なアプリを作ろうと思っているが、ソースの管理に困る。
ローカルPCでソース管理するのも嫌だし、 サーバを借りているわけでもない。
GitHub でソース管理することも考えたが、 プライベートリポジトリが欲しい...
検索していると下記サイトを発見。
www.find-job.net
比較を見ていて BitBucket が気になった。
bitbucket.org
Atlassian を利用してアジャイル開発するプロジェクトに携わっていたことがあるのだが、
アジャイル開発のタスク管理がしやすい JIRA、
情報共有に特化した高機能ウィキ Confluence、
コードレビューが捗る Crucible、
などなど使い勝手が非常に良かった。
ということで、BitBucket でプライベートリポジトリを作ってみる。
サインアップ
①https://bitbucket.org へアクセス
②フォームへ入力
③メールが届くので認証しておく
⑥git clone して、git の設定をしておく
$ git clone https://jetglass@bitbucket.org/jetglass/tutorial.git $ git config user.name "jetglass"
⑦ファイルを編集して、コミット&プッシュ
$ cd tutorial $ vim sample.html
下記、行を追加。
<h2>Hello World!</h2>
$ git add sample.html $ git commit -m "テスト" $ git push origin master
⑧https://bitbucket.org を開いてコミットを確認すると、追加分が確認できる。
まとめ
手順の通り簡単にプライベートリポジトリを作れる。
しかも、作るリポジトリの数に制限がないのはありがたい。
チーム 5人までなら無料で使えるので、スタートアップで利用するのにいいと思う。
Capistrano からの bundle install で rmagick のインストールが失敗するときの対処方法
概要
cap コマンドからの bundle install が失敗する件。
前回記事で Capistrano 2 系で踏み台経由でデプロイって記事を書いたのだけど、
cap コマンド内で実行される bundle install が失敗してしまうので、
今回それを解決する方法を記事にする。
↓前回記事
jetglass.hatenablog.jp
これも関連記事。
jetglass.hatenablog.jp
環境は ローカルPC -> 踏み台 -> テストサーバ。
ちなみにテストサーバに ImageMagick はインストール済み。
エラーの内容と原因
$ bundle exec cap deploy
上記を実行で下記エラー。
** [out :: test_server] Gem::Installer::ExtensionBuildError: ERROR: Failed to build gem native extension. ** [out :: test_server] ** [out :: test_server] /usr/local/bin/ruby extconf.rb ** [out :: test_server] checking for Ruby version >= 1.8.5... yes ** [out :: test_server] checking for gcc... yes ** [out :: test_server] checking for Magick-config... yes ** [out :: test_server] ** [out :: test_server] Warning: Found a partial ImageMagick installation. Your operating system likely has some built-in ImageMagick libraries but not all of ImageMagick. This will most likely cause problems at both compile and runtime. ** [out :: test_server] Found partial installation at: /usr/local ** [out :: test_server] checking for ImageMagick version >= 6.4.9... yes ** [out :: test_server] Package MagickCore was not found in the pkg-config search path. ** [out :: test_server] Perhaps you should add the directory containing `MagickCore.pc' ** [out :: test_server] to the PKG_CONFIG_PATH environment variable ** [out :: test_server] No package 'MagickCore' found ** [out :: test_server] Package MagickCore was not found in the pkg-config search path. ** [out :: test_server] Perhaps you should add the directory containing `MagickCore.pc' ** [out :: test_server] to the PKG_CONFIG_PATH environment variable ** [out :: test_server] No package 'MagickCore' found ** [out :: test_server] Package MagickCore was not found in the pkg-config search path. ** [out :: test_server] Perhaps you should add the directory containing `MagickCore.pc' ** [out :: test_server] to the PKG_CONFIG_PATH environment variable ** [out :: test_server] No package 'MagickCore' found ** [out :: test_server] Package MagickCore was not found in the pkg-config search path. ** [out :: test_server] Perhaps you should add the directory containing `MagickCore.pc' ** [out :: test_server] to the PKG_CONFIG_PATH environment variable ** [out :: test_server] No package 'MagickCore' found ** [out :: test_server] checking for stdint.h... yes ** [out :: test_server] checking for sys/types.h... yes ** [out :: test_server] checking for wand/MagickWand.h... no ** [out :: test_server] ** [out :: test_server] Can't install RMagick 0.0.0. Can't find MagickWand.h. ** [out :: test_server] *** extconf.rb failed *** ** [out :: test_server] Could not create Makefile due to some reason, probably lack of ** [out :: test_server] necessary libraries and/or headers. Check the mkmf.log file for more ** [out :: test_server] details. You may need configuration options. ** [out :: test_server] ** [out :: test_server] Provided configuration options: ** [out :: test_server] --with-opt-dir ** [out :: test_server] --without-opt-dir ** [out :: test_server] --with-opt-include ** [out :: test_server] --without-opt-include=${opt-dir}/include ** [out :: test_server] --with-opt-lib ** [out :: test_server] --without-opt-lib=${opt-dir}/lib ** [out :: test_server] --with-make-prog ** [out :: test_server] --without-make-prog ** [out :: test_server] --srcdir=. ** [out :: test_server] --curdir ** [out :: test_server] --ruby=/usr/local/bin/ruby ** [out :: test_server] ** [out :: test_server] ** [out :: test_server] Gem files will remain installed in /home/deployer/qupio_web_admin/shared/bundle/ru ** [out :: test_server] by/1.9.1/gems/rmagick-2.13.3 for inspection. ** [out :: test_server] Results logged to /home/deployer/qupio_web_admin/shared/bundle/ruby/1.9.1/gems/rmagick-2.13.3/ext/RMagick/gem_make.out ** [out :: test_server] An error occurred while installing rmagick (2.13.3), and Bundler cannot ** [out :: test_server] continue. ** [out :: test_server] Make sure that `gem install rmagick -v '2.13.3'` succeeds before bundling.
同じように苦しんだ人の記事を発見。
エラーログに出ている通り、PKG_CONFIG_PATH の環境変数の設定がおかしいようだ。
前回設定した deployer ユーザで ssh して調べる。
$ ssh test_server $ env | grep PKG_CONFIG_PATH PKG_CONFIG_PATH=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig
普通に設定されてる。
となると cap コマンド経由で環境変数が設定されていないのが問題か。
cap コマンド実行時の環境変数の調べ方、
Capistrano: Can I set an environment variable for the whole cap session? - Stack Overflow
↑を参考にコマンド実行。
$ bundle exec cap COMMAND=printenv invoke
PKG_CONFIG_PATH は設定されてない。
解決方法:環境変数を追加
deploy.rb に下記を追加。
set :default_environment, { 'PKG_CONFIG_PATH' => '/usr/lib/pkgconfig:/usr/local/lib/pkgconfig' }
もう一度、環境変数を確認。
$ bundle exec cap COMMAND=printenv invoke
PKG_CONFIG_PATH が追加されていることを確認できた。
cap コマンド実行。
$ bundle exec cap deploy
今度こそ成功!
まとめ
環境変数が問題で cap に失敗する場合があるかもしれない。
cap 実行時の環境変数は下記で確認
$ bundle exec cap COMMAND=printenv invoke
cap 実行時の環境変数を追加したい場合は deploy.rb に下記を追加
set :default_environment, { 'HOGE_PATH' => '/hoge_path' }
Capistrano 2 系で踏み台サーバ経由でデプロイする方法
Capistrano 2 系の記事を今さら書くのも微妙だが、作業メモ。
3 系でも同じような感じで使えるはず。
概要
Capistrano 2 系で踏み台サーバを経由してデプロイする方法。
状況は下記。
ローカルマシン -> 踏み台サーバ -> テストサーバ
前に書いた↑これ(~/.ssh/config)を利用すれば簡単に実現できた。
実現方法
~/.ssh/config の設定
まずは ~/.ssh/config の設定をする。
なぜ設定するかというと、
cap コマンドを叩くユーザの ~/.ssh/config を有効にできるため。
# Mac(ローカルマシン)の ~/.ssh/config Host bastion # ホスト名(任意) User tsuyacchi # ユーザ名 HostName xxx.xxx.xxx.xxx # IP or ホスト名 Port 2222 # ポート番号 IdentityFile ~/.ssh/tsuyacchi_id_rsa # 秘密鍵ファイルパス Host test_server # ホスト名(任意) User deployer # ユーザ名 HostName zzz.zzz.zzz.zzz # IP or ホスト名 IdentityFile ~/.ssh/deployer_id_rsa # 秘密鍵ファイルパス ProxyCommand ssh bastion -W %h:%p # SSHトンネル(bastion 経由)
前に書いた記事の通りだが、$ ssh test_server
でログインできるようにしておくこと。
deploy.rb の設定
次に Capistrano 2系での設定。
... set :user, "deployer" ssh_options[:forward_agent] = true # ~/.ssh/config の設定を利用してくれるようになる ... role :web, "test_server" # ~/.ssh/config に設定した通り、踏み台経由でアクセスしてくれる ...
cap コマンド実行でエラー
$ bundle exec cap deploy ... connection failed for: test_server (Net::SSH::AuthenticationFailed: Authentication failed for user deployer@zzz.zzz.zzz.zzz)
この環境だとこんなエラーが出た。
踏み台サーバ -> テストサーバ への接続でパスフレーズを聞かれるのが原因。
CapistranoでSSHのパスワード入力プロンプトが出ずにNet::SSH::AuthenticationFailedエラーとなる問題の解決法あれこれ - Qiita
↑解決方法が2つ記載してあったけど、状況に合わないので別の方法を検討。
ssh-agent & ssh-add による秘密鍵登録
↑を参考に問題解決できそう、やってみる。
$ eval `ssh-agent` $ ssh-add ~/.ssh/deployer_id_rsa
秘密鍵を登録して、
$ bundle exec cap deploy
成功。
ただし、cap コマンド越しの bundle install 失敗、
これはまた別の記事にしよう。
補足:パスフレーズ聞かれる問題
公開鍵 + 秘密鍵の設定なのに、なぜか test_server へのアクセスは毎回パスフレーズを要求される。
色々調べたら下記記事を発見。
公開鍵認証のssh設定のはずが突然パスワードを聞かれるようになった | hello-world.jp.net
そして、ドンピシャで /home/deployer のパーミッションが 0755 になっていた。
/home/hoge や /home/hoge/.ssh 配下のパーミッションが適切でないと、 パスフレーズを毎回聞かれるらしいので注意。
補足:deploy.rb のみで踏み台サーバ経由でデプロイ
~/.ssh/config を利用しないで踏み台サーバを経由する方法もある。
deploy.rbに下記を書く。
set :gateway, 'bastion'
https://groups.google.com/forum/#!topic/capistrano/egnmyhYw93k
capistranoでssh越しのサーバーに設置する場合 - odeの開発メモ日記
ただ、上記に書いてある通り、踏み台サーバを経由する場合に ポート番号・鍵ファイル・パスフレーズの指定を、 踏み台サーバとデプロイ先サーバでそれぞれ設定できないようなので、 複雑な設定が必要な場合は無理ぽ。
Capistrano 3 系であれば、
Capistrano 3で多段sshしたい | BLOG.QuelLENcode
ssh_options で複雑な設定ができるようなので、無理すれば deploy.rb で解決できそう。
ただし、上記記事でも ~/.ssh/config 利用のが簡単だって結論。
Ubuntu12.04 で RMagick Imagemagick をインストールする
微妙な環境だけど、作業メモ。
- bundler (1.7.11)
- rails (3.2.19)
- rmagick (2.13.3) など。
Gemfile に下記のように指定。
... gem 'rmagick', '2.13.3' ...
bundle install
実行した結果、下記のようなエラー
Gem::Installer::ExtensionBuildError: ERROR: Failed to build gem native extension. /usr/share/ruby-rvm/rubies/ruby-1.9.3-p484/bin/ruby extconf.rb checking for Ruby version >= 1.8.5... yes checking for gcc... yes checking for Magick-config... no Can't install RMagick 0.0.0. Can't find Magick-config in /usr/share/ruby-rvm/gems/ruby-1.9.3-p484/bin:/usr/share/ruby-rvm/gems/ruby-1.9.3-p484@global/bin:/usr/share/ruby-rvm/rubies/ruby-1.9.3-p484/bin:/usr/share/ruby-rvm/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games *** extconf.rb failed *** Could not create Makefile due to some reason, probably lack of necessary libraries and/or headers. Check the mkmf.log file for more details. You may need configuration options. Provided configuration options: --with-opt-dir --without-opt-dir --with-opt-include --without-opt-include=${opt-dir}/include --with-opt-lib --without-opt-lib=${opt-dir}/lib --with-make-prog --without-make-prog --srcdir=. --curdir --ruby=/usr/share/ruby-rvm/rubies/ruby-1.9.3-p484/bin/ruby Gem files will remain installed in /tmp/bundler20150427-16923-1xvc14n/rmagick-2.13.3/gems/rmagick-2.13.3 for inspection. Results logged to /tmp/bundler20150427-16923-1xvc14n/rmagick-2.13.3/gems/rmagick-2.13.3/ext/RMagick/gem_make.out An error occurred while installing rmagick (2.13.3), and Bundler cannot continue. Make sure that `gem install rmagick -v '2.13.3'` succeeds before bundling.
Imagemagick がインストールされていないってことで、インストール。
sudo aptitude install imagemagick libmagick++-dev
なかなか処理が始まらないと思ってたらタイムアウト。
そういえばこの環境ではプロキシ設定が必要だった!
sudo vim /etc/apt/apt.conf.d/00proxy # 下記追加 Acquire::http::Proxy "http://hoge:80"; Acquire::ftp::Proxy "http://hoge:80";
もう一度、実行
sudo aptitude install imagemagick libmagick++-dev
The following NEW packages will be installed: gir1.2-gtk-2.0{a} libcairo2-dev{a} libfontconfig1-dev{a} libfreetype6-dev{a} libgtk-3-0{a} libgtk-3-bin{a} libgtk-3-common{a} libgtk2.0-0{a} libgtk2.0-bin{a} libgtk2.0-common{a} libgtk2.0-dev{a} libjasper-dev{a} libmagick++-dev libmagickcore-dev{a} libmagickwand-dev{a} libpango1.0-dev{a} librsvg2-bin{a} librsvg2-common{a} librsvg2-dev{a} libtiff4-dev{a} libtiffxx0c2{a} libwmf-dev{a} libxcomposite-dev{a} libxcursor-dev{a} libxdamage-dev{a} libxfixes-dev{a} libxft-dev{a} libxi-dev{a} libxrandr-dev{a} libxrandr2{a} libxrender-dev{a} 0 packages upgraded, 31 newly installed, 0 to remove and 82 not upgraded. Need to get 1,939 kB/15.4 MB of archives. After unpacking 59.1 MB will be used. Do you want to continue? [Y/n/?] Y Err http://old-releases.archive.ubuntu.com/ubuntu/ precise-updates/main libxrandr2 amd64 2:1.3.2-2ubuntu0.2 404 Not Found Err http://old-releases.archive.ubuntu.com/ubuntu/ precise-updates/main libtiffxx0c2 amd64 3.9.5-2ubuntu1.6 404 Not Found Err http://old-releases.archive.ubuntu.com/ubuntu/ precise-updates/main libfreetype6-dev amd64 2.4.8-1ubuntu2.1 404 Not Found Err http://old-releases.archive.ubuntu.com/ubuntu/ precise-updates/main libxrender-dev amd64 1:0.9.6-2ubuntu0.1 404 Not Found Err http://old-releases.archive.ubuntu.com/ubuntu/ precise-updates/main libxfixes-dev amd64 1:5.0-4ubuntu4.3 404 Not Found Err http://old-releases.archive.ubuntu.com/ubuntu/ precise-updates/main libxi-dev amd64 2:1.7.1.901-1ubuntu1~precise2 404 Not Found Err http://old-releases.archive.ubuntu.com/ubuntu/ precise-updates/main libxrandr-dev amd64 2:1.3.2-2ubuntu0.2 404 Not Found Err http://old-releases.archive.ubuntu.com/ubuntu/ precise-updates/main libjasper-dev amd64 1.900.1-13ubuntu0.1 404 Not Found Err http://old-releases.archive.ubuntu.com/ubuntu/ precise-updates/main libtiff4-dev amd64 3.9.5-2ubuntu1.6 404 Not Found 0% [Working]E: Failed to fetch http://old-releases.archive.ubuntu.com/ubuntu/pool/main/libx/libxrandr/libxrandr2_1.3.2-2ubuntu0.2_amd64.deb: 404 Not Found E: Failed to fetch http://old-releases.archive.ubuntu.com/ubuntu/pool/main/libx/libxrandr/libxrandr2_1.3.2-2ubuntu0.2_amd64.deb: 404 Not Found
よくわからんけど、404 が出まくっている。
一度、ログアウトして、再ログインすると
convert
が使えるようになっていたため、
bundle install
してみるもエラー。
なので、一度、imagemagick のみを削除して、ソースから入れ直す。
sudo aptitude remove imagemagick sudo su - cd /usr/local/src wget ftp://ftp.kddlabs.co.jp/graphics/ImageMagick/releases/ImageMagick-6.8.9-10.tar.gz tar zxfv ImageMagick-6.8.9-10.tar.gz cd ImageMagick-6.8.9-10 ./configure make && make check
結果
============================================================================ Testsuite summary for ImageMagick 6.8.9 ============================================================================ # TOTAL: 76 # PASS: 76 # SKIP: 0 # XFAIL: 0 # FAIL: 0 # XPASS: 0 # ERROR: 0
テストが通ったので、インストール。
make install
インストールも完了し、
bundle install
で無事インストール完了。
4. Rubyによるデザインパターン【Factory Method】
4回目のデザインパターンは Factory Method(ファクトリーメソッド) パターンの記事を書いてみる。
Template Method と関連性のあるパターン。
実装については簡単だけど、メリットが理解しづらい。
いろいろなサイトを参考にして、3つのメリットを理解したので記事の中で書いていく。
おなじみ、下記ソウルを忘れないこと。
参考:programming - Rubyによるデザインパターン5原則 - Qiita
調査
Factory Method パターン - Wikipedia
Factory Method パターンは、他のクラスのコンストラクタをサブクラスで上書き可能な自分のメソッドに置き換えることで、 アプリケーションに特化したオブジェクトの生成をサブクラスに追い出し、クラスの再利用性を高めることを目的とする。
Wikiの図の説明だが、 まず Creator という抽象クラスの定義について、 anOperation メソッドと factoryMethod メソッドが定義されているが、 anOperation メソッド内で factoryMethod が呼ばれるものとする。
factoryMethod メソッドは Product クラス型のインスタンスを生成して返却する。
ConcreteCreator クラスは Creator クラスを継承して定義。
factoryMethod をオーバーライドし、Product クラスを継承した ConcreteProduct クラスのインスタンスを生成して返却する。
概念
博士 # Creator 魔物を呼び出す # anOperation 魔物を作る # factoryMethod 魔物が標的を選ぶ 魔物が標的を襲う 魔物を作る # factoryMethod(オーバライドされる前提 魔物 # Product 標的を選ぶ 標的を襲う ビバルディ < 博士 # ConcreteCreator 魔物を作る # factoryMethod のオーバーライド 魔人プウを生成 魔人プウ < 魔物 # ConcreteProduct 標的を選ぶ 標的を襲う 標的をガムにする ドクターゲボ < 博士 魔物を作る 人造怪人を生成 人造怪人 < 魔物 標的を選ぶ 標的を襲う 標的の脂肪を吸引する 魔王 人を襲わせる ビバルディを生成.魔物を呼び出させる ドクターゲボを生成.魔物を呼び出させる
ただ、言葉にしてみただけ感・・・
博士 = Creator 魔物 = Product ビバルディ = ドクターゲボ = ConcreteCreator 魔人プウ = 人造怪人 = ConrcreteProduct 魔王 = 博士を利用するクラス
上記のような割当となる。
魔王視点で考えるならば、魔物を意識せずに標的を襲わせることができる。
つまり、いちいち魔王が魔物の操作をしなくて済むし、博士に魔物を呼び出せと命令するだけでいい。
これが Factory Method の1つ目のメリットだと考える。(Template Method のメリットとも言えるw)
博士の魔物を呼び出すメソッドが Template Method になっていて、
魔物を作るメソッドが Factory Method になっている。
以下、魔物を作るメソッドに該当するものをファクトリーメソッドと呼ぶ。
博士クラスの魔物を呼び出すメソッドが「魔物を生成して標的を襲うまで」のアルゴリズムを持っているので、 博士クラスを継承するビバルディ・ドクターゲボはこの Template Method の恩恵にあずかることができる。
実装
class Dr def call_monster monster = create_monster monster.select_target monster.attack_to_target end def create_monster;end end class Monster attr_accessor :target def select_target;end def attack_to_target;end end class DrGebo < Dr def create_monster MysteryMan.new end end class MysteryMan < Monster def select_target search_by_radar end def attack_to_target p "attack to #{@target}" end private def search_by_radar @target = "ririkun" end end class Vivaldi < Dr def create_monster Puu.new end end class Puu < Monster def select_target search_by_smell end def attack_to_target p "attack to #{@target}" end private def search_by_smell @target = "Mr. Zatan" end end class Maou def self.attack DrGebo.new.call_monster Vivaldi.new.call_monster end end Maou.attack # 実行結果 # "attack to ririkun" # "attack to Mr. Zatan"
実装を通して見えてくる2つ目のメリットは、
create_monster メソッド(ファクトリーメソッド)がサブクラスへ切り出されているおかげで、
開発が進めやすいというところ。
違う言い方をするなら、結合度が低くなり独立性が高まる。
Dr のサブクラス担当者はこのインスタンスをモックに置き換えて実装しておくことで、 Maou クラス(利用側)担当者の実装を滞らせることはなくなるし、 Monster のサブクラスの実装を分離して進められる。
複数人で共通ブランチを弄る場合、モック置き換えが活きる。
開発中のバグがある実装をコミットしても、モックのおかげで誰の実装にも影響を与えなくて済む。
テストを書いて実装が終わったなら、モックを本来のインスタンス生成に置き換えれば、これまた誰の実装に影響もなく済む。
そして、わかりづらい3つ目のメリットは、
Dr, Monster クラスの担当者がサブクラスを全く気にせずに実装を進められる。
つまり、フレームワーク側を作る側にメリットがある。
- 俺日記 : Factory Methodパターンの本当のメリットってなんだろう…。
- What is the difference between Factory and Strategy patterns? - Stack Overflow
- Difference between Factory Method and Strategy design patterns | Deadschool
↑のサイトを何十回も読み直し・・・
Factory Method パターンの親クラスにあたる実装をフレームワークとして捉えた場合、 サブクラスの具象名や実装を全く気にしないで実装を進めることができる。
またフレームワークを提供するに辺り、 サブクラス実装者にインスタンスの生成を委ねられることが、 フレームワークとしてのあり方でもある。
でもわざわざ Factory Method にしなくても、利用側でインスタンス生成して渡せばいいのでは感もある。 つまり、Strategy パターンを適用した場合どうなるのか。
class Dr def initialize(monster) @monster = monster end def attack @monster.attack # monster に委譲 end end class Monster def attack; end; # インターフェースの統一 end class MysteryMan < Monster attr_accessor :target def attack # インターフェースの実装 search_by_radar attack_to_target end def search_by_radar @target = "ririkun" end def attack_to_target p "attack to #{@target}" end end class Maou def self.attack Dr.new(MysteryMan.new).attack end end Maou.attack # 実行結果 # "attack to ririkun"
実装のキモが MysteryMan (委譲先)に寄り過ぎてフレームワークとしてあまり価値のない状態になった。
Strategy パターンはやはり、アルゴリズムの選択に意味があるのであって、実装にルールを作るフレームワークの要件に合っていない。
Template Method パターンならどうなのか。
# 生成に関する Dr は必要なくなるので実装しない class Monster def attack select_target attack_to_target end def select_target; end def attack_to_target; end end class MysteryMan < Monster def select_target ...
まず、Template Method は継承なので結合度が高い。
Factory Method だと、インスタンスを生成して、それに対して処理を委譲するので結合度は低くなる。
Template Method だと実装が増えるにつれて結合度が増していき、管理しづらいコードになっていくと予想される。
(コード量が多くないなら気にならないが。)
では、Template Method パターン内で処理を委譲するインスタンス生成をさせたらいいのでは。
class MysteryMan < Monster def select_target hoge = Hoge.new ...
処理を Hoge インスタンスに委譲できたとしても、 インスタンス生成がベタ書きになるので結合度は高く、 変更に弱いし、並列開発しづらい状態になる。
ファクトリーメソッド の改造
長いけど、さらに補足。
wiki には下記のようなことが書いてあった。
factoryMethodは、デフォルトの動作を含んだ具象メソッドである場合もある。パラメータを取り、それによって生成するクラスを変えることもある。
これはどういうことなのかというと、
class Vivaldi < Dr def create_monster(name) if name == "puu" Puu.new else Ruu.new end end end
生成するインスタンスの種類が増えているが、 ファクトリーメソッドによって生成するインスタンスをサブクラスが選んでいる。
利用する側はインスタンス生成に関する分岐を意識しなくて済むことがメリットになる。
ruby っぽく書くなら下記。
class Vivaldi < Dr def create_monster(name) Object.const_get(name.capitalize).new rescue nil end end
まとめ
サブクラスにファクトリーメソッドをオーバーライドさせてインスタンス生成させときゃいいんだろって、 言葉の上でもソース上でも簡単そうな Factory Method パターン。
実際はどういう角度でこのパターンを見るかによって、 メリットや使いどころが大きく変わるので理解が難しい。
- 利用する側(ConcreteCreator を呼び出す側)は気軽に使える
- ファクトリーメソッドによって生成されるインスタンスをモックに置き換えることで、みんなの並列開発が捗る
- フレームワーク開発のためのパターンとも言える
ただ単にインスタンス生成に無理矢理このパターンを使っても全くメリットがないし、 むしろ、ソースが読みづらくなるだけのデメリットしかない。
使いどころ
個人的な使いどころの見解としては、
- 軽いフレームワーク作り = Template Method
- 軽くないフレームワーク作り = Factory Method(+ Template Method)
- インスタンス生成すべき種類が多い = Factory Method
- インスタンス生成過程が複雑 = Builder
インスタンス生成すべき種類が多い場合には、 ConcreteCreator + ConcreteProduct を増やすのではなく、 ConcreteCreator のファクトリーメソッド内に分岐を作り、 ConcreteProduct を増やして インスタンスを作り分ける実装をするといい。
おさらい
もっと単純にまとめよう。
Factory Method は下記のとき使う。
次回
次回は Abstract Factory やな。