2014年5月29日木曜日

Google GuiceのBest Practicesを訳してみた - 直接の依存性のみ注入せよ

直接の依存性のみ注入せよ
他のオブジェクトを取得するために注入することを避けよ。例えば、Accountを取得するためにCustomerを注入してはいけない。
public class ShowBudgets {
    private final Account account;

    @Inject
    ShowBudgets(Customer customer) {
        account = customer.getPurchasingAccount();
    }

代わりに、依存するクラスを直接注入せよ。これはテストを簡単にする。テストはテスト自身とCustomerにだけ注意すればよい。@Providesを付けたメソッドを使い、CustomerからAccountをバインドする。
public class CustomerModule extends AbstractModule {
    @Override public void configure() {
        ...
    }

    @Provides
    Account providePurchasingAccount(Customer customer) {
        return customer.getPurchasingAccount();
    }

依存性を直接注入することで、コードはシンプルになる。
public class ShowBudgets {
    private final Account account;

    @Inject
    ShowBudgets(Account account) {
        this.account = account;
    }

2014年5月26日月曜日

Google GuiceのBest Practicesを訳してみた - 可変性を最小化せよ

可変性を最小化せよ
可能であればいつでも、不変なオブジェクトを生成するようにコンストラクターインジェクションを使用すること。不変なオブジェクトはシンプルで、共有でき、再利用できる。このパターンにしたがって注入可能なクラスを定義する。
public class RealPaymentService implements PaymentService {

    private final PaymentQueue paymentQueue;
    private final Notifier notifier;

    @Inject
    RealPaymentService(
        PaymentQueye paymentQueue,
        Notifier notifier) {
        this.paymentQueue = paymentQueue;
        this.notifier = notifier;
    }

    ...

このクラスの全てのフィールドはfinalであり、@Injectが付けられたコンストラクタで初期化されている。Effective Javaでは不変性の他の利点について述べられている。

メソッドインジェクションとフィールドインジェクション
コンストラクターインジェクションにはいくつかの制限がある。

  • 注入されるコンストラクタはオプションにならない。
  • オブジェクトがGuiceによって生成されない限り、使用されない。これは一部のフレームワークとの関係を断つ。
  • サブクラスはsuper()を全ての依存するクラスとともに呼ばなければならない。これはコンストラクターインジェクションを扱いにくくする。親クラスに変更があったときは特に。
Guiceによって生成されないオブジェクトを初期化するなら、メソッドインジェクションがもっとも便利だ。AssistedInjectやMultibinderのような拡張は、バインドされたオブジェクトを初期化するために、メソッドインジェクションを使用する。

フィールドインジェクションは、コンパクトに記述できる。故に、例を示すときやスライドで頻繁に目にする。それはカプセル化もできていないし、テストもしにくい。finalなフィールドへ注入してはいけない。注入された値が全てのスレッドで参照できることを、JVMは保証していない。

2014年5月19日月曜日

Lispでバケツソート

日経ソフトウェアの2014年6月号で、バケツソートが紹介されていました。
興味深かったので、Lispで実装してみました。

ソース
(defun backet-sort (v backet-count)
  "バケツソート"
  (let ((backet (array-to-backet v backet-count)))
    (backet-to-array backet (length v))))

(defun array-to-backet (arr backet-count)
  "配列からバケツを構成する"
  (let ((backet (make-array backet-count :initial-element 0)))
    (dotimes (i (length arr) backet)
      (let ((n (aref arr i)))
        (setf (aref backet n) (1+ (aref backet n)))))))

(defun backet-to-array (backet array-length)
  "バケツからソート済みの配列を構成する"
  (let ((arr (make-array array-length)))
    (loop with arr-index = 0
          for i below (length backet)
          when (< 0 (aref backet i))
          do (loop repeat (aref backet i)
                   do (setf (aref arr arr-index) i)
                      (setq arr-index (1+ arr-index))))
    arr))


(defun main ()
  "動作確認のための関数"
  (let ((l (loop with state = (make-random-state t)
                 repeat 10
                 collect (random 10 state))))
    (print (sort (make-array (length l) :initial-contents l) #'<))
    (print (backet-sort (make-array (length l) :initial-contents l) 10))))

(main)

なんとなく汚い感じがするのは、きっと自分の腕が未熟だからでしょう。

2014年5月12日月曜日

JavaScriptでは"false"はtrueとして扱われる

タイトルだけでは「そんなバカな」と思われるかもしれませんが、要はtruthy/falsyの話です。

JavaScriptでのTruthy/Falsy
仕様は、ECMAScript Language Specification - 9.2 ToBooleanにあります。これによれば、falseとして扱われるのは次の6つです。

  1. undefined
  2. null
  3. false
  4. 0
  5. NaN
  6. "" (空文字列)
Boolean関数を使って確認してみました。


"false"がtrueとして扱われることが確認できました。

問題になるケース
問題になるケースとしては、サーバーサイドの言語からJavaScriptへ、type="hidden"のinputのvalueへfalseをセットする場合が考えられます。サーバーでセットされた値を使用して、次のようなif文を書くと問題が発生します。

// <input id="someElement" type="hidden" value="false" />のとき
if (document.getElementById('someElement').value) {
    // このブロックが実行される
    ...
}

回避するには、明示的に"true"と比較するのがいいでしょう。

// <input id="someElement" type="hidden" value="false" />のとき
if ("true" === document.getElementById('someElement').value) {
    // このブロックは実行されない
    ...
}

JavaScriptの型は独特のゆるさがあるので、ついついチェックがあまくなりがちです。しかし、今自分がどんな型を扱おうとしているのか把握していないと、思わぬところでハマってしまいます。という記事でした。

おまけ
「Boolean型に変換するAPIはないのか」と思って調べてみましたが、次のような結果に。


参考
ECMAScript Language Specification

2014年5月5日月曜日

unittest.mockの簡単な紹介

単体テストに欠かせないツールのひとつに、モックがあります。
Pythonにも3.3からモックモジュールが追加されました。
unittest.mockといいます。今回はこのモジュールの簡単な使い方を紹介します。

モックが必要になる場面として、システム日付の取得があります。
例えば、当日が閏日かどうか判定する関数を考えてみましょう。
なにも考えずに関数を実装すると、次のようになると思います。
def is_leap():
    d = datetime.date.today()
    if 2 == d.month and 29 == d.day:
        print(str(d) + ' is LEAP')
        return True
    else:
        print(str(d) + ' is NOT LEAP')
        return False

このままではOSの時計を設定しないと試験ができないので、
日付をパラメータとして渡すようにします。
def is_leap(d):
    if 2 == d.month and 29 == d.day:
        print(str(d) + ' is LEAP')
        return True
    else:
        print(str(d) + ' is NOT LEAP')
        return False

日付を注入できるようにしたことで、テストしやすくなりました。
例えば、このようにします。
assert True == is_leap(datetime.date(2012, 2, 29))
assert False == is_leap(datetime.date(2012, 3, 1))

unittest.mockを使うと、次のような感じになります。
d = unittest.mock.MagicMock()
d.month = 2
d.day = 29
assert True == is_leap(d)

モックを使わないほうが単純です。こういう場合はdateオブジェクトを使ったほうがいいでしょう。
さて、現実には全てのコードを自由に変更できるとは限りません。
たとえそんな場合でも、unittest.mock.patchを呼ぶことで、対応できる場合があります。
is_leap関数を修正せずに次のようにします。
with unittest.mock.patch('datetime.date') as dt:
    dt.today().month = 2
    dt.today().day = 29
    assert True == is_leap()

datetime.date.today()メソッドが返すオブジェクトをモックオブジェクトと置き換え、
属性を設定することでテストをパスしています。
確かにこの方法でも試験はできますが、日付をパラメータ化するほうがベターだと思います。

他にも、unittest.mockにはメソッドの呼び出しを記録する機能もあります。
詳しいことはオフィシャルのドキュメントを参照してください。

参考: