ブログトップ 記事一覧 ログイン 無料ブログ開設

Strategic Choice

2014-07-29

[]美しさ:メソッドを使った整列

どういうこと?

コードのシルエットが美しくない場合、ヘルパメソッドを導入して、コードを整列します。

どうして?

コードの「シルエット」に違和感があると、読み間違いが発生します。「似ているコードは似ているように見せる」のが原則です。

コードのシルエットが美しくない例を見てみます。人事データベースがあり、以下の関数が提供されています。

// 「Doug Adams」のようなpartial_nameを「Mr. Douglas Adams」に変える。
// それができなければ、errorに説明文を入れる。
string ExpandFullName(DatabaseConnection dc, string partial_name, string* error);

この関数を、実例を使ってテストしています。

DatabaseConnection database connection;
string error;

assert(ExpandFullName(database_connection, "Doug Adams", &error)
	== "Mr. Douglas Adams");
assert(error == "");
assert(ExpandFullName(database_connection, "Jake Brown", &error)
	== "Mr. Jacob Brown III");
assert(error == "");
assert(ExpandFullName(database_connection, "No Such Guy", &error) == "");
assert(error == "no match found");
assert(ExpandFullName(database_connection, "John", &error) == "");
assert(error == "more than one result");

このテストは、見た目が美しくありません。長すぎて折り返されているところもあります。シルエットは不細工だし一貫性のあるパターンがありません。

これは改行の位置を変えなければいけないパターンです。でも、「assert(ExpandFullName(database connection...,」や「error」などの文字列が何度も登場して邪魔をしています。

ステートメントにヘルパメソッドをかぶせれば、シルエットが美しくなり、コードが改善できます。

どうすれば?

ヘルパメソッドを導入して、コードを整列させます。

上述例であれば、ヘルパメソッド「CheckFullName」を導入します。

CheckFullName("Doug Adams", "Mr. Douglas Adams", "");
CheckFullName("Jake Brown", "Mr. Jacob Brown III", "");
CheckFullName("No Such Guy", "", "no match found");
CheckFullName("John", "", "more than one result");

これなら、引数の異なる4つのテストがあることがよくわかります。

「面倒な仕事」はすべて「CheckFullName()」に隠されることになりました。「CheckFullName」関数の実装は以下の通りです。

void CheckFullName(	string partial_name,
			string expected_full_name,
			string expected_error) {
	// database_connection はクラスのメンバになっている。
	string error;
	string full_name = ExpandFullName(database_connection, partial_name, &error);
	assert(error == expected_error);
	assert(full_name == expected_full_name);
}

ちなみに、ここでの目標はコードの見た目を美しくすることでしたが、この変更によってうれしい副作用がもたらされています。

  • 重複を排除したことでコードが簡潔になった。
  • テストケースの大切な部分(名前やエラー文字列)が見やすくなった。
    • 以前は、「database_connection」や「error」などのトークンに囲まれていて、コードを見て「飲み込む」のが難しかった。
  • テストの追加が簡単になった。

この話の教訓は、コードの「見た目」をよくすれば、表面上の改善だけではなく、コードの構造も改善できるということです。

2014-07-28

[]美しさ:一貫性のある簡潔な改行位置

どういうこと?

コードの見た目に一貫性を持たせるため、適切な位置に改行を入れるようにします。

どうして?

コードの「シルエット」に違和感があると、読み間違いが発生します。「似ているコードは似ているように見せる」のが原則です。

たとえば、任意の速度のネットワークに接続したときに、プログラムがどのように動くかを評価するクラス「TcpConnectionSimulator」があるとします。このクラスのコンストラクタには、4つの仮引数があります。

  1. 接続速度 (Kbps)
  2. 平均遅延時間 (ms)
  3. 遅延時間 (ms)
  4. パケットロス率 (%)

ここで、TcpConnectionSimulatorのインスタンスが3つ必要だとします。

public class PerformanceTester {
	public static final wifi = new TcpConnectionSimulator (
		500, /* Kbps */
		80, /* millisecs latency */
		200, /* jitter */
		1 /* packet loss % */);

	public static final t3_fiber = 
		new TcpConnectionSimulator (
			450000, /* Kbps */
			10, /* millisecs latency */
			0, /* jitter */
			0 /* packet loss % */);

	public static final cell = new TcpConnectionSimulator (
		100, /* Kbps */
		400, /* millisecs latency */
		250, /* jitter */
		5 /* packet loss % */);
}

横幅80文字に合わせるために(これが会社のコーディング標準だとします)余計な改行が入っています。その結果、「t3_fiber」の見た目が、他と違って「残念」なことになっています。

コードの「シルエット」が変なので、自然と「t3_fiber」に目が向いてしまいます。一見、「t3_fiber」が、同じクラスのインスタンスに見えません。

どうすれば?

コードの見た目を一貫性のあるものにするため、適切な改行を入れるようにします。また、コメントも整列させます。

上述の例を改善すると、以下のようになります。

public class PerformanceTester {
	public static final wifi = 
		new TcpConnectionSimulator (
			500, /* Kbps */
			80, /* millisecs latency */
			200, /* jitter */
			1 /* packet loss % */);

	public static final t3_fiber = 
		new TcpConnectionSimulator (
			450000, /* Kbps */
			10, /* millisecs latency */
			0, /* jitter */
			0 /* packet loss % */);

	public static final cell = 
		new TcpConnectionSimulator (
			100, /* Kbps */
			400, /* millisecs latency */
			250, /* jitter */
			5 /* packet loss % */);
}

このコードには一貫性があり、楽に目を通すことができるようになります。

ただ、縦に長くなっているし、同じコメントが3回も繰り返されています。さらに簡潔に書くと、以下のようになります。

public class PerformanceTester {
	// TcpConnectionSimulator(throughput, latency, jitter, packet_loss)
	//                        [Kbps]      [Kbps]   [ms]    [percent]

	public static final wifi = 
		new TcpConnectionSimulator (500,        80,     200,       1);

	public static final t3_fiber = 
		new TcpConnectionSimulator (450000,     10,       0,       0);

	public static final cell = 
		new TcpConnectionSimulator (100,       400,     250,       5);
}

コメントを最上部に移動して、仮引数を一行で書くようにしました。数値の右隣からコメントなくなり、より簡潔な表組みに「データ」が並ぶようになりました。

2014-07-25

[]美しさ

どういうこと?

優れたソースコードには、「見た目の美しさ」が必要です。

どうして?

見た目が美しいコードのほうが、使いやすいのは明らかです。

考えてみれば、プログラミングの時間のほとんどは「コードを読む時間」です。さっと流し読みができれば、誰にとっても使いやすいコードだと言えます。

例えば、(極端ですが)以下のようなコードを使わなければならないとします。

class StatsKeeper {
public:
//doubleを記録するクラス
	void Add(double d); // と素早く統計を出すメソッド
private: int count;			/* それまでの         個数
*/ public:
	double Average();
private: double minimum;
list<double>
	past_items
		;double maximum;
};

これを理解するには時間がかかります。しかし、以下のキレイなバージョンなら、マシンには同じコードでも、ヒトには結果は異なります。

//doubleを記録するクラスと
//すばやく統計を出すメソッド
class StatsKeeper {
	public:
		void Add(double d);
		double Average();

	private:
		list<double> past_items;
		int count; //それまでの個数

		double minimum;
		double maximum;
};

どうすれば?

優れたソースコードは「目に優しい」ものでなければなりません。そのためには、コードを読みやすくするための「余白」「配置」「順序」に気を配る必要があります。

以下、目に優しいコードのための3大原則です。

  1. 読み手が慣れているパターンと一貫性のあるレイアウトを使う。
  2. 似ているコードは似ているように見せる。
  3. 関連するコードをまとめてブロックにする。

2014-07-24

[]ドッグフーディング

dogfooding

または

Eating your own dog food

どういうこと?

開発した製品(やサービス)を、自らが使用することです。

どうして?

ユーザの視点で見ることにより、「本来の改善点」に気づくことが出来ます。

ユーザが使うように使うと、開発内のテストでは見つからなかった不具合が見つかります。これを修正し、製品の安定性を向上させることが出来ます。

ユーザが使うように使うと、全く使わない機能や、逆に欲しい機能が見つかります。また、使い勝手があまり良くない機能に気づきます。これらの機能過不足を調整し、製品の魅力を向上させることが出来ます。

どうすれば?

できれば、リリース前に、自らが本当のユーザになって使用します。疑似ユーザではなく、本当に使ってみるのが望ましいやり方です。

リリース前の製品は品質が悪いので、コストのリスクはありますが、ユーザに見放されるリスクに比べたら問題になりません。

最終的に、安定性を確保してユーザにあきれられるリスクを回避し、機能を改善してユーザを魅了するリターンがあるので、やらない手はありません。

また、リリース後であっても、自らの製品は使い続けるべきです。ユーザに対して「便利です」といって製品を提供している以上、自らがそれを使用して証明する責任があるからです。

参考


2014-07-23

[]名前:誤解されない名前:複数の名前を検討する

どういうこと?

名前を決めるときには、複数の候補を検討します。

どうして?

名前は、たいていの場合、完璧なものが一発で見つかることはありません。いくつか候補を上げ、それぞれ長所(ないし短所)について話し合い、そのうえで決定するべきです。

どうすれば?

名前を決めるときには、複数の候補を検討します。

以下で、具体的な検討の例を示します。

高トラフイックのウェブサイトでは、ウェブサイトの変更によってビジネスがどのくらい改善できるかを調べる「実験」をすることが多いです。

これは、実験用の設定ファイルです。

experiment_id: 100
description: "フォントサイズを14ptに上げる"
traffic_fraction: 5%
....

設定ファイルには、属性と値のペアが15個ほど定義されています。同じような実験をするときには、設定ファイルの大部分をコピペしなくてはいけません。

experiment_id: 101
description: "フォントサイズを13ptに上げる"
//[以下 experiment_id: 100のときと同じ]
....

既存の設定ファイルを、他の実験でも使えるようにしたいところです*1。そうすれば、以下のように書けます。

experiment_id: 101
the_other_experiment_id_I_want_to_reuse: 100
//[以下変更が必要な情報だけ書き換える]
....

ここで考えなければいけないのは、「the_other_experiment_id_I_want_to_reuse(再利用したい実験のID)」の名前を何にするか?です。

以下の4つの名前を検討してみます。

  1. template
  2. reuse
  3. copy
  4. inherit

どの名前も適切に思えます。おそらく、新しい機能を追加したのが自分自身だからです。でも、この機能を知らない人が見たらどうなるかを想像しなければなりません。

そこで、それぞれの名前を検討していきます。他の誰かが誤解する可能性を考えてみます。

template
experiment_id: 101
template: 100
....

「template」にはいくつかの問題があります。

まず、「これはテンプレートだ」なのか「このテンプレートを使っている」なのか、わかりにくい点です。

次に、「テンプレート」という言葉は、抽象的なものに何かを「埋め込んで」、具体的なものにするために使うものです。テンプレートに使う実験のことを「本物」の実験ではない抽象的なものと誤解する人がいるかもしれません。

この状況で「template」を使うのは、意味があいまいすぎるようです。

reuse
experiment_id: 101
reuse: 100
....

「reuse」は悪くない言葉です。

しかし、文字だけを見ると「この実験は100回再利用できる」と誤解される可能性もあります。名前を「reuse_id」に変えたほうがいいかもしれません。ただ、それでも、「reuse_id:100」のことを「この実験の再利用idは100だ」と誤解するユーザがいるかもしれません。

copy
experiment_id: 101
copy: 100
....

「copy」はいい名前です。

しかし、「copy:100」だけでは、「この実験を100回コピーする」なのか「これは100回めのコピーである」なのか、わかりません。

「他の実験を参照している言葉」ということを明確にするには、「copy_experiment」という名前に変えるといいかもしれません。今までのところ、これが最善の名前です。

inherit
experiment_id: 101
inherit: 100
....

「inherit」という言葉はプログラマにはなじみがあります。

何かを継承するというのは、新たに変更を加えるという意味です。クラスを継承すれば、そのクラスのメソッドとメンバがすべて手に入り、それらを変更したり新しく追加したりできます。現実の世界で考えてみても、身内の財産を継承するというのは、その財産を自分で好きなように保有したり売却したりできるという意味です。

ただし、他の実験から継承していることは明確にしておいた方がよさそうです。名前は「inherit_from」や「inherit_from_experiment_id」に変更したほうがよいかもしれません。

結論

以上の検討結果から、最善の名前は「copy_experiment」と「inherit_from_experiment_id」ということになりました。これらは、何が起きるかを明確に表していて、誤解を生む可能性が低いからです。

*1:これは「プロトタイプ継承」パターンと呼ばれる手法です