リーダブルコード - The Art of Readable Code まとめ
良いコードを書くのは得意だという自負があったのでなんとなく読むのをスルーしていたが、改めて読んでみると勉強になる部分がとても多かった。特に「最短時間で理解できるコードが読みやすいコードだ」という一文にはとても感銘を受けた。あとは自分の中でも答えが出ていなかったものに対して、明確な回答が用意されていてよかった。
以下、自分が重要だと思ったことを少しずつまとめていく。
1章
- コードは理解しやすくなければいけない
- コードは他の人が最短時間で理解できるように書かなければいけない
第I部 表面上の改善
2章 名前に情報を詰め込む
- 明確な単語を選ぶ(get → fetch / download、stop → pause / kill)
- 抽象的な名前よりも具体的な名前を使う(--run_locally → —use_local_database)
- 名前に情報を追加する(id → hex_id)
- 値の単位(start → start_ms)
- その他の重要な属性を追加する(plaintext_password、unescaped_comment、html_utf8)
- スコープが小さければ短い名前でもいい
// コードを理解するのに必要な情報がすぐそばにあるからだ。 - 不要な単語を投げ捨てる(ConvertToString() → ToString())
- 名前のフォーマットで情報を伝える(コーディング規約での命名規約とか)
3章 誤解されない名前
- filter() → select() / exclude()
- Clip(text, length) → remove(text, length) / truncate(text, length)
- 限界値を含めるときはminとmaxを使う
- 範囲を指定するときはfirstとlastを使う
- 包含/排他的範囲にはbeginとendを使う
- ユーザの期待に合わせる(getMean() → computeMean()、size() → countSize())
4章 美しさ
- なぜ美しさが大切なのか?
→ 見た目が美しいコードのほうが使いやすいのは明らかだ。考えてみれば、プログラミングの時間のほとんどはコードを読む時間なのだッ! - 一貫性のある簡潔な改行位置
- (ヘルパー)メソッドを使った整列
// コードの「見た目をよく」すれば、表面上の改善だけではなく、コードの改善もできる - 縦の線をまっすぐにする
- 整列すべきなのか?
// ぼくたちの経験では、プログラマが心配するほどの手間にはならない
// もし手間になるようだったら、そのときは止めればいい - 宣言をブロックにまとめる
// 論理的なグループに分けてあげるといいだろう - コードを「段落」に分割する
- 似ている考えをグループにまとめて、他の考えと分ける
- 視覚的な「踏み石」を提供できる
- 段落単位で移動できるようになる
- 個人的な好みと一貫性
→ 一貫性のあるスタイルは「正しい」スタイルよりも大切だ。
5章 コメントすべきことを知る
- コメントの目的は、書き手の意図を読み手に知らせることである。
- コードからすぐにわかることをコメントに書かない。
- 例:Accoutクラスの定義
- ひどい名前はコメントをつけずに名前を変える
- 優れたコード > ひどいコード + 優れたコメント
- 自分の考えを記録する
- コードが期待ない理由をコメントに書いてもいい
- コードの欠陥にコメントをつける
- その欠陥を文書化することを恥ずかしがってはいけない
- 定数にコメントをつける
- なぜその値を持っているのかという「背景」が存在する場合が多い
- 頭のなかで考えていたことを記録するのが大切
- 読み手の立場になって考える
6章 コメントは正確で簡潔に
- コメントは領域に対する情報の比率が高くなければいけない
- コメントを簡潔にしておく
- // CategoryType -> (score, weight)
typedef hash_map<int, pair<float, float> > ScoreMap;
- // CategoryType -> (score, weight)
- 曖昧な代名詞を避ける(それ)
- 歯切れの悪い文章を磨く(によって優先度を変える → の優先度を高くする)
- 関数の動作を正確に記述する
- // このファイルに含まれる改行文字(’\n’)を数える。
int CountLines(string filename) { … }
- // このファイルに含まれる改行文字(’\n’)を数える。
- 入出力のコーナーケースに実例を使う
- // 実例:Strip(“abba/a/ba”, “ab”)は”/a/“を返す
String Strip(String src, String chars) { … }
- // 実例:Strip(“abba/a/ba”, “ab”)は”/a/“を返す
- コードの意図を書く(listを逆順にイテレートする → 値段の高い順に表示する)
- 情報密度の高い言葉を使う(キャッシュ、正規化)
OCMockのサンプルコード
APIリファレンスっぽいものが見つからなかったので、とりあえずテストコードを書いてみた。 YusukeHosonuma/OCMockSample · GitHub
気が向いたら追加していく予定。
// // OCMockSampleTests.m // OCMockSampleTests // // Created by Yusuke on 6/23/15. // Copyright (c) 2015 Yusuke. All rights reserved. // #import <UIKit/UIKit.h> #import <XCTest/XCTest.h> #import <OCMock/OCMock.h> #import "Foo.h" @interface OCMockSampleTests : XCTestCase @property (nonatomic, assign) BOOL handleNotificationMockCalled; @property (nonatomic, assign) BOOL andDoBlockCalled; @end @implementation OCMockSampleTests - (void)setUp { [super setUp]; self.handleNotificationMockCalled = NO; self.andDoBlockCalled = NO; } - (void)tearDown { [super tearDown]; } /// --- create - (void)test_makeForClass { // クラス定義からMockを生成する id mock = [OCMockObject mockForClass:[NSString class]]; NSInteger returnValue = 100; [[[mock stub] andReturnValue:[NSValue valueWithBytes:&returnValue objCType:@encode(NSInteger)]] length]; XCTAssertEqual([mock length], 100); } - (void)test_makeForClassWithMacro { // マクロ版 id mock = OCMClassMock([NSString class]); OCMStub([mock length]).andReturn(100); // マクロを使うと参照型かプリミティブ型か区別しなくても良さそう XCTAssertEqual([mock length], 100); } - (void)test_partialMockObject { // 生成ずみのオブジェクトからMockを生成する Foo *foo = [[Foo alloc] init]; id mock = [OCMockObject partialMockForObject:foo]; [[[mock stub] andReturn:@"foo!"] bar]; XCTAssertEqualObjects(@"foo!", [mock bar]); } - (void)test_partialMockObjectWithMacro { // マクロ版 Foo *foo = [[Foo alloc] init]; id mock = OCMPartialMock(foo); OCMStub([mock bar]).andReturn(@"foo!"); XCTAssertEqualObjects(@"foo!", [mock bar]); } // --- andXxx - (void)test_andReturn { // andReturn - 任意の値(id型)を返す id mock = [OCMockObject mockForClass:[NSString class]]; [[[mock stub] andReturn:@"foo!"] stringByAppendingString:[OCMArg any]]; // 対象のメソッドが引数を取るときは[OCMArg any]を与える [[[mock stub] andReturn:@"bar!"] stringByAppendingPathExtension:OCMOCK_ANY]; // マクロで書くならOCMOCK_ANY XCTAssertEqualObjects(@"foo!", [mock stringByAppendingString:@"dummy"]); XCTAssertEqualObjects(@"bar!", [mock stringByAppendingPathExtension:@"dummy"]); } - (void)test_andReturnValue { // andReturnValue - andReturnのプリミティブ値バージョン id mock = OCMClassMock([NSString class]); NSInteger returnValue = 100; [[[mock stub] andReturnValue:[NSValue valueWithBytes:&returnValue objCType:@encode(NSInteger)]] length]; XCTAssertEqual(100, [mock length]); } - (void)test_andThrow { // andThrow - 例外をスローするようにする id mock = OCMClassMock([NSString class]); id exception = [NSException exceptionWithName:@"dummy" reason:@"" userInfo:nil]; [[[mock stub] andThrow:exception] length]; XCTAssertThrows([mock length]); } - (void)test_andPost { // andPost - Notificationをpostするようにする id mock = OCMClassMock([NSString class]); id notification = [NSNotification notificationWithName:@"mock" object:self]; [[[mock stub] andPost:notification] length]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotificationMock:) name:@"mock" object:nil]; [mock length]; XCTAssertTrue(self.handleNotificationMockCalled); } - (void)handleNotificationMock:(NSNotification *)notification { self.handleNotificationMockCalled = YES; } - (void)test_andCall { // andCall - 代わりのメソッドを呼び出すようにする Foo *foo = [[Foo alloc] init]; id mockStr = OCMPartialMock(foo); [[[mockStr stub] andCall:@selector(mockBar) onObject:self] bar]; // SELでかわりに呼び出すメソッドを指定する XCTAssertEqualObjects(@"mockBar!", [mockStr bar]); } - (NSString *)mockBar { return @"mockBar!"; } - (void)test_andDo { // andDo - 特定のBlocksを呼び出すようにする id mock = OCMClassMock([NSString class]); [[[mock stub] andDo:^(NSInvocation *invocation) { self.andDoBlockCalled = YES; }] length]; [mock length]; XCTAssertTrue(self.andDoBlockCalled); } - (void)test_andForwardToRealObject { // andForwardToRealObject - Mockが保持している実際のオブジェクトへ呼び出しを転送するようにする Foo *foo = [[Foo alloc] init]; id mock = OCMPartialMock(foo); // PartialMockでないとNG [[[mock stub] andForwardToRealObject] bar]; XCTAssertEqualObjects(@"bar!", [mock bar]); } // --- expect / verify - (void)test_expect_verify { // expect - そのメソッドが呼び出されることを期待し、 // verify - で検証する Foo *foo = [[Foo alloc] init]; id mock = OCMPartialMock(foo); [[mock expect] hoge]; // hogeメソッドが呼び出されていること(を期待) [mock foo]; [mock verify]; // 検証 } - (void)test_expect_verifyWithDelay { // verifyWithDelay - n秒後に検証する Foo *foo = [[Foo alloc] init]; id mock = OCMPartialMock(foo); [[mock expect] hoge]; [mock fooDelay]; [mock verifyWithDelay:0.2]; // 0.2秒後に検証 } @end
SwiftでFizzBuzzと世界のナベアツ
最初はネタで書こうと思ったのだけれど、わりとSwiftの書き方の勉強になった。
gist.github.com
最終的にXCTestとXcode beta7からのUIテストも書いたサンプルをGitにあげた。github.com
Java8でFizzBuzz
StreamAPIとかJava8の機能を使ったらどうかけるだろう、と思って書いてみた。
gist.github.com
無駄に無限数列を定義してるけれど、こういう書き方が出来るようになっとことを考えるとJavaも変わったよな、と思う。
とりあえずStreamAPIバンザイ!
Node.jsメモ
ドットインストールで勉強してみたので簡単にメモっておく。 dotinstall.com
基本的なこと
- シングルスレッドモデル(イベントループ)
- JavaScriptで動く
- コールバックを用いた非同期処理
モジュール?の読み込み
var http = require('http'), settings = require('./setting.js'); // 自分で作ったものはパスを指定
モジュールのインストール
npm(node package manager)を使う
$ sudo npm install xxx
JS(サーバ)の実行
$ node server.js
Hello, world
// bind request event. server.on('request', function(req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); // response-header res.write('Hello, world'); // response-body res.end(); // finish response } // start listen server.listen(1337, 'localhost');
テンプレートエンジン
ejs(Embedded JavaScript templates)とかが使える。
<html> <h1><%= title %></h1> <!-- escape --> <p><%- content %></p> <!-- non-escape --> <p><%= n %> views</p> </html>
クエリ処理
querystringというパッケージが便利。
if (req.method === 'POST') { req.data = ""; // init req.on('readable', function() { // while-reading req.data += req.read(); }); req.on('end', function() { // read-finish. console.log(req.data); var query = qs.parse(req.data); // parse }); }