リーダブルコードの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)
Code language: PHP (php)
ここで取り組んでいるのは「ユーザ情報を暗号化して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)
Code language: PHP (php)
続いて、やりすぎのコードを紹介しています。
# リーダブルコード 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)
Code language: PHP (php)
小さな関数を作りすぎると、逆に読みにくくなってしまう。あちこちに飛び回る実行パスを追いかけることになるからだ。
プロジェクトの他の部分から再利用できるのであれば、小さな関数を追加するのも意味のあることかもしれない。でも、それまでは必要ない。
リーダブルコード 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
Code language: PHP (php)
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)
Code language: PHP (php)