メソッドに切り出すタイミング2

リーダブルコードの10.7の139ページで、ユーザ情報を暗号化してURLに含めるコードが紹介されています。このコードを小さく分けてみましょう。

# リーダブルコード p139 user_info = { "username": "...", "password": "..." } user_str = json.dumps(user_info) cipher = Cipher("aes_128_cbc", key=PRIVATE_KEY, init_vector=INIT_VECTOR, op=ENCODE) encrypted_bytes = cipher.update(user_str) encrypted_bytes += cipher.final() # 現在の128ビットブロックをフラッシュする url = "http://example.com/?user_info=" + base64.urlsafe_b64encode(encrypted_bytes)

ここで取り組んでいるのは「ユーザ情報を暗号化してURLに含める」だけど、コードの大部分は「PythonのオブジェクトをURLセーフな文字列」にするものになっている。こうした下位問題は抽出しておこう。

リーダブルコード p139

ということで、改善後のコードも紹介されています。

# リーダブルコード p139 def url_safe_encrypt(obj): obj_str = json.dumps(obj) cipher = Cipher("aes_128_cbc", key=PRIVATE_KEY, init_vector=INIT_VECTOR, op=ENCODE) encrypted_bytes = cipher.update(user_str) encrypted_bytes += cipher.final() # 現在の128ビットブロックをフラッシュする return base64.urlsafe_b64encode(encrypted_bytes) user_info = { "username": "...", "password": "..." } url = "http://example.com/?user_info=" + url_safe_encrypt(user_info)

続いて、やりすぎのコードを紹介しています。

# リーダブルコード p140 user_info = { "username": "...", "password": "..." } url = "http://example.com/?user_info=" + url_safe_encrypt(user_info) def url_safe_encrypt(obj): obj_str = json.dumps(obj) return url_safe_encrypt_str(obj_str) def url_safe_encrypt_str(data): encrypted_bytes = encrypt(data) return base64.urlsafe_b64encode(encrypted_bytes) def encrypt(data): cipher = make_cipher() encrypted_bytes = cipher.update(data) encrypted_bytes += cipher.final() # 現在の128ビットブロックをフラッシュする return encrypted_bytes def make_cipher(): return Cipher("aes_128_cbc", key=PRIVATE_KEY, init_vector=INIT_VECTOR, op=ENCODE)

 小さな関数を作りすぎると、逆に読みにくくなってしまう。あちこちに飛び回る実行パスを追いかけることになるからだ。

 プロジェクトの他の部分から再利用できるのであれば、小さな関数を追加するのも意味のあることかもしれない。でも、それまでは必要ない。

リーダブルコード p140

小さく分けることと「必要になるまで作るな(YAGNI You ain't gonna need it)」は反しませんし、ぼくは、これぐらい小さく分けてもいいと思います。

そうは言っても、やりすぎの指摘を受け止めて、少し控えめのバージョンを考えてみましょう。

url_safe_encryptは、3つの処理
(1)オブジェクトの文字列化
(2)暗号化
(3)Base64エンコード
に分けることができます。

そこで、encryptだけを分けたバージョンが、次のコードです。

# Ninton aoki作成 user_info = { "username": "...", "password": "..." } url = "http://example.com/?user_info=" + url_safe_encrypt(user_info) def url_safe_encrypt(obj): obj_str = json.dumps(obj) encrypted_bytes = encrypt(obj_str) return base64.urlsafe_b64encode(encrypted_bytes) def encrypt(data): cipher = Cipher("aes_128_cbc", key=PRIVATE_KEY, init_vector=INIT_VECTOR, op=ENCODE) encrypted_bytes = cipher.update(data) encrypted_bytes += cipher.final() # 現在の128ビットブロックをフラッシュする return encrypted_bytes

encrypt内のCiper行がゴチャゴチャしている感じがします。やりすぎバージョンで make_cipher を分けていたのは悪くない気がします。

make_cipherを分けたほうがいい理由としては、暗号化だけでなく復号化も同じCipherを使いたいかもしれないことです。テストケースを作って検証するとき、元データと復号化したデータを比較検証したいかもしれません。

そこで、やりすぎバージョンのmake_cipherに、op引数をつけたバージョンが、次のコードです。

# Ninton aoki作成 user_info = { "username": "...", "password": "..." } url = "http://example.com/?user_info=" + url_safe_encrypt(user_info) def url_safe_encrypt(obj): obj_str = json.dumps(obj) encrypted_bytes = encrypt(obj_str) return base64.urlsafe_b64encode(encrypted_bytes) def encrypt(data): cipher = make_cipher(ENCODE) encrypted_bytes = cipher.update(data) encrypted_bytes += cipher.final() # 現在の128ビットブロックをフラッシュする return encrypted_bytes def make_cipher(op): return Cipher("aes_128_cbc", key=PRIVATE_KEY, init_vector=INIT_VECTOR, op=op)
タイトルとURLをコピーしました