良いコードを書くためのテクニック集
勉強会で作成した資料をまとめました.
例題の言語は、TypeScriptにて記載してますが、TypeScript以外でも利用可能なTipsです
目次
- 導入(<-ここ)
- 命名規則(<-ここ)
- より綺麗にする
- ロジック
- 大きなコード作成
- 最後に
1. 導入
人のコードを見るのって大変じゃないですか?
- コードレビュー
- 既存コードの改修
- 既存コードに機能追加
大半がコードを書くより見る時間の方が長い
原因
- 変数名,関数名が単純
- ex) tmp, information, value
- 何を表しているかのかがわからないため、コードを追わないとダメ
- 一つの関数メソッドが複数のタスク
- 関数名を抽象的にしていろいろの機能を盛り込む
- 不要なコード残っている(コメントアウト含め)
- これは必要なコードなのか?を考える時間が発生
- ロジックが複雑
- ex)
if(){if(){if()}}
- 何がなんだかわからん
- ex)
悪いコード(あまり直視しないでください)
/* メソッドが抽象的で様々なタスクを処理 */
public setSubtitle(index: number) { /* ネストが多すぎて動作がよくわからない */
if (this.currentSubtitle != index) { /* currentSubtitleとindexの違いは? */
if (this._settings.isIE && this.useSidecar() && this.useOverray()) {
this.setSubtitleForIE(index);
} else {
for (var i = 0; i < this.element.textTracks.length; i++) {
if (this._element.textTracks[i].label !== "dummy") {
this._element.textTracks[i].mode = 'hidden';
}
}
if (!!this._captionspan) {
this._captionspan.innerHTML = "";
}
if (!!this._areaDiv) {
this._areaDiv.classList.remove('visible');
this._areaDiv.classList.add('hidden');
}
if (index != -1) { /* -1とはどういう状態なのか? */
this.element.textTracks[index].mode = 'showing';
if (!!this._areaDiv) {
this._areaDiv.classList.remove('hidden');
this._areaDiv.classList.add('visible');
}
}
}
this.currentSubtitle = index;
}
}
改めて目的
なぜ学ぶのか?
他人が理解するのに時間がかからないコードを書くことは最も大事
ただ、良いコードを書きたいけど具体的にどうしたらいいの?って人が多いと思います
そんな人に今日からコード上で使えるテクニックを紹介します
2. 命名規則
クラス、関数、変数、定数、メソッド、プロパティすべてにおいて名前をつける
どのような名前をつけるとよいのかを考えよう
明確な単語を利用
読み込み
function getPage() {}
get
ではあまり明確な単語ではないのであまり利用すべきでない
より具体的に以下の用に着けると良い
// インターネットから取って来る
function fetchPage() {}
// ローカルキャッシュから読み出す
function loadPage() {}
// データの復元
function restorePage() {}
追加
setData
は意味が広いためできるだけ避ける
// dataを追加
function addData() {}
// データを末尾に追加
function appendData() {}
// データを先頭に追加
function pushData() {}
// データを登録
function resisterStrage() {}
書き込み
// 既存のページを保存
function savePage() {}
// ユーザ画面にページを出力
function outputPage() {}
// インターネット上へ送信
function sendPage() {}
削除
// データを削除(復元不可)
function deletePage() {}
// データを除去(復元可能)
function removePage() {}
// データを廃棄(復元可能)
funtion trashPage() {}
// データを初期化
function clearPage() {}
// データを破棄(再利用不可)
fucntion destroyPage() {}
// 先頭のデータを取り出す
function popPage() {}
検証
checkAccount()
は抽象的なので非推奨
// 対象のデータがルールを満たしているかを確認
function testAccount() {}
// 対象のデータが正しいか検証
function validateAccount() {}
// 対象のデータを比較する
function compareAccount() {}
// 対処のデータを照合
function verifyAccount() {}
探索
// 情報を検索(発見可能前提)
function findData() {}
// 情報を探索(発見するとは限らん)
function searchData() {}
// ある条件で抽出する
function extractData() {}
// ある条件で除外する
function excludeData() {}
更新
// 修正する
function modifiedData() {}
// 最新にする
function updateData() {}
// 適用する
function applyData() {}
// 変更する
function changeData() {}
真偽値
// bad
let loadFlag = false;
flag は用途を表すだけで、意味や状態を表現できないため使うのは避ける
errorflag
は、falseがエラーなのか、trueがエラーなのかわからない。
お決まりパターンがあるので従うと良い
// is + 形容詞
let isEmpty;
// is + 過去分詞
let isHidden;
// is + 主語 + 過去分詞
let isViewLoaded;
// has + 名詞(データやプロパティを持っているか)
let hasParent;
// can + 動詞(処理ができる)
let canLoad;
// 動詞
let existsGoogle; // Listにgoogleが存在する
let containsGoogle; // Listにgoogleが含まれている
// should + 動詞(命令を実行するべきか)
let shouldLoad;
そのほかの単語
参考 URL
汎用的な名前は使用しない
意味が広いため理解しにくい
// bad
let tmp;
let val;
let it;
汎用的な名前は使用しない
ただ、イテレータで使用するi
, j
, k
は OK
for (let i = 0; i < animals.length; i++) {
animalAges.push(Number(animals[i].age));
}
TypeScript だとforEach
を使うと良い
animals.forEach((animal: { name: string; age: string }) => {
animalAges.push(Number(animal.age));
});
具体的な名前にする
より具体的な名前がつけられるようにメソッド分割
例)
// bad
setSubTitle(){}
// ok
paarseSubTimeType(){} // 字幕の種類を解析
fetchSubTitleData(){} // 字幕情報取得
formatSubTitleData(){} // 取得した字幕情報を整形
outputSubTitle(){} // ユーザーに表示
名前に情報を詰める
単位をつける
const delayTime_ms = 10; // 遅延時間10ms
const imageSize_mb = 100; // 画像サイズ100MByte
名前の長さを決める
ガイドライン
- スコープが小さいものは短い名前で OK
- スコープが大きいものは情報を詰め込み長い名前にする
// nameだけでも意味が伝わる
if (user) {
const name = loadUserName(user);
outputUserName(name);
}
限界価
最大値、最小値は max, min をつける(包含関係)
const MIN_ITEMS_IN_CART = 1; // カートに入れれる最小は1アイテム以上
const MAX_ITEMS_IN_CART = 10; // カートに入れれる最大は10アイテム以下
if (shoppingCart.items > MAX_ITEMS_IN_CART) {
// 11アイテム以上はエラー
alert("カートにある商品数が多すぎます");
return;
}
if (shoppingCart.items < MIN_ITEMS_IN_CART) {
// 0アイテム以下はエラー
alert("1アイテム以上で選択してください");
return;
}
範囲
範囲は、start, last を使用(包含関係)
function outputConsole(start: number, last: number) {
for (let i = start; i <= last; i++) {
console.log(i); // 2, 3, 4
}
}
outputConsole(2, 4);
排他的範囲
排他的範囲 begin(以上), end(未満)を使用(排他的関係)
function outputConsole(begin: number, end: number) {
for (let i = begin; i < end; i++) {
console.log(i); // 2, 3
}
}
outputConsole(2, 4);
フラグは肯定形
肯定にすることで可読性アップ
// ok
let isPlaying;
// bad
let isNotPlaying;
2. まとめ
- より明確な英単語を使用する
- イテレータは、
i
,j
,k
で大丈夫だが、複数の場合はuse_i
等にして付加情報をつける - 具体的な名前にするため、メソッドを細分化
- 時間サイズ等には単位をつける
- スコープが大きいほど長くしてより具体的な名前をつける
- 限界値、範囲の名前を適切にする
- フラグは肯定にする