【良いコードを書くための Tips】ロジック編


# 概要

https://qiita.com/Yuki-TU/items/3426ff7a84fe03687b9e

の続きです。TypeScriptで記述しています。


---

# 目次

1. 導入
2. 命名規則
3. より綺麗にする
4. **ロジック**(<-ここ)


---

# 4. ロジック

## if/else ブロックの並び順

なるべく肯定を先に記述すると良い

```ts
// bad
function fee() {
  let result;
  if (!isChild()) {
    result = adultFee();
  } else {
    result = childFee();
  }
  return result;
}

// good
function fee() {
  let result;
  if (isChild()) {
    result = childFee();
  } else {
    result = adultFee();
  }
  return result;
}
```

---

## else 句は複雑になるためなるべく利用しない

- **ガード説**により else を回避
- **ガード節**とは else 句を使用せず早期リターンする手法

```ts
// bad
function fee() {
  let result;
  if (isChild()) {
    result = childFee();
  } else if (isSenior()) {
    result = seniorFee();
  } else {
    result = adultFee();
  }
}
// good
function fee() {
  if (isChild()) return childFee();
  if (isSenior()) return seniorFee();
  return adultFee();
}
```

---

## 複雑になるなら三項演算子は利用しない

ぱっと見理解できない書き方の三項演算子は利用しない

- 読みにくい
- デバッグもできない(ステップ実行できない)

```ts
// bad
return login === SUCCESS
  ? permission !== SUCCESS
    ? "permissyon error"
    : ""
  : "loging error";

// good
if (login === SUCCESS) {
  if (permission !== SUCCESS) {
    return "permission error";
  }
  return "";
} else {
  return "login error";
}
```

---

## ネストを浅くする

ガード節よりネストを回避

1. 失敗のケースを早期リターン

```ts
// bad
getError(){
    if(login === SUCCESS) {
        if(permission !== SUCCESS) {
            return 'permission error';
        }
        return '';
    } else {
        return 'login error';
    }
}

// good
getError() {
    if(login !== SUCCESS) return 'login error';
    if(permission !== SUCCESS) return 'permission error';
    return '';
}
```

---


## ループ内部のネスト削除

ループ内のネスト削除は continue を利用

1. 失敗ケースを continue

```ts
// bad
animals.forEach((animal: {name: string, age: string}) => {
    if(animal !== null) {
        animalAges.push(Number(animal.age))
        if (animal.name === 'gorila') {
            golilas.push(animal)
        }
    }
});

// good
animals.forEach((animal: {name: string, age: string}) => {
    if(animal === null) continue;
    animalAges.push(Number(animal.age))
    if (animal.name !== 'gorila) continue;
    golilas.push(animal);
});
```

---


## 説明変数・要約変数

```ts
// bad
if (
  request.user.id === documnet.ownId &&
  request.user.yearOfResistration >= 3
) {
  // ユーザはこの文書にアクセスできる
}

// good
const MIN_YEAR_OF_RESISTRATION = 3;
const ownDocument = request.user.id === documnet.ownId;
const canAccessDocument =
  ownDocument && request.user.yearOfResistration >= MIN_YEAR_OF_RESISTRATION;
if (canAccessDocument) {
  // ユーザはこの文書にアクセスできる
}
```

---

## ド・モルガンの法則を利用

Not を分配して or/and を反転するド・モルガンの法則を利用すると読みやすい

!(a || b || c) <=> !a && !b && !c

!(a && b && c) <=> !a || !b || !c

```ts
// bad
if (!(existsFile && isProtected)) {
  alert(
    "ファイルを読み込めませんでした。ファイルが存在するかアクセス権限があるか確認してください。"
  );
}

// good
if (!existsFile || isProtected) {
  alert(
    "ファイルを読み込めませんでした。ファイルが存在するかアクセス権限があるか確認してください。"
  );
}
```

---

## 短絡評価を適切に利用

javascript, python では最後に評価した値を返す

```ts
// if(isPlaying){ pause(); } のような挙動
isPlaying && pause();

// cityから評価していき値が真であれば代入
// 最後にデフォルト価を入れる
const birthPlace = city || prefecture || country || "Earth";
```

---

## 複雑なロジック

- 複雑なロジックだなと思ったら反対を考える
- ある範囲がもう片方の範囲と重なっているかどうかを判定する関数

```
1. 一方の範囲の終点が、ある範囲の間にある場合
  a <---->
  b    <---->
2. 一方の範囲の始点が、ある範囲の間にある場合
  a    <---->
  b <---->
3. 一方の始点と終点が他の範囲を囲んでいる場合
  a   <------>
  b     <-->
```

```ts
// bad 
// ロジックが複雑で読みにくい
function overLapsWith(range: Range, other: Range) {
    return (range.begin >= other.begin && range.begin < other.end>) ||
        (range.end > other.begin && range.end <= other.end>) ||
        (range.begin <= other.begin && range.end >= other.end>);
}
```

- 反対を考える

```
1. 一方の範囲の終点が、ある範囲の始点よりも前になる場合
  a   <--->
  b         <--->
2. 一方の範囲の始点が、ある範囲の終点よりも後にある場合
  a         <--->
  b <--->
```

```ts
// good
function overLapsWith(range: Range, other: Range) {
  if (other.end <= range.begin) return false;
  if (other.begin >= range.end) return false;
  return true;
}
```

---

## 4. まとめ
- else 句は**ガード節**を利用することで避ける
- 複雑な複雑な三興演算子は避け、if 文を利用
- ネストは**ガード節**により浅くする
- 簡潔な名前で式を説明
- 短絡評価は適切に利用
- 複雑なロジックは逆の発想を適用して簡潔にならないかを考える