未熟学生エンジニアのブログ

TetsuFeの個人開発ブログ

TetsuFeはテツエフイー と読みます。FlutterやWeb周り全般、チーム開発について語るブログ

良いコードを書くために普段気をつけているパターン集

はじめに

この記事では、自分が普段コードを書く際に気をつけていることの中から、「良いコードを書くためのパターン」を説明していきます。今日から早速使えるようなものに絞ったつもりです。

ガード節による早期return

ガード節は、主にエラーなどの異常を受け取る可能性がある場合、 正常系の処理の手前で例外を発生させたり、returnしたりする節のこと です。一般に例外的な場合にはコードブロックが小さくなるので、先に例外を発生させたりreturnすることでその後の箇所でそれらを意識しなくて良くなり、よりコードがよみやすくなります。

StreamBuilder(stream: stream, builder: (context, snapshot){

  // ガード節
  if(snapshot.hasError){
    return Text("エラーです");
  }

  // 正常系
  if(!snapshot.hasData){
    rerurn CircularProgressIndicator(); // データが流れてくるまではインジケータを表示
  }else{
    return Text("正常に ${snapshot.data} を受け取りました");
  }
});

また、if elseとの使い分けとしては、ifもelseも正常な条件の場合はif elseを使い、例外的な条件を含む場合はガード節を使います

宣言的に書く

以下のようなListがあるとします。

List<int> collection = new List<int> { 1, 2, 3, 4, 5 };

このcollectionから、奇数だけを取り出した新しいListを作りたいとします。

命令的に書く場合、以下のようになります

List<int> results = new List<int>();
foreach(var num in collection)
{
    if (num % 2 != 0)
          results.add(num);
}

宣言的に書く場合、以下のようになります

var results = collection.where( num => num % 2 != 0);

慣れの問題もありますが、命令的に書くよりも宣言的に書いた方が実行結果がわかりやすいです。命令的に書くと、命令を追わないと結果がわからない場合が多いです。 命令的に書く場合、「命令の結果こうなる」ということになりますが、宣言的に書く場合、「こうなるようにしてほしい」という感じになります。 「結論から話した方がわかりやすい」という話と近いような、近くないような…?

クラス

できるだけprivateフィールド・メソッドを使う

クラス内からしか使われないフィールド・メソッドはprivateで宣言し、クラス外から使えないようにしましょう。これをすることで間違ってクラス外から使ってしまうことを防ぐことができますし、 クラス外から使われる可能性を考える必要がなくなり、コードがより読みやすく・デバッグしやすくなります。 以下は、広告配信のAdmobを使う際に、AndroidiOSでidを使い分けて初期化するコードです。

注:Dartでは、_(アンダースコア)をつけることで private を表します

import 'dart:io';

import 'package:firebase_admob/firebase_admob.dart';

class AdMobService{
  void init() {
    FirebaseAdMob.instance.initialize(appId: _appId());
  }

  String _appId() {
    if (Platform.isAndroid) {
      return 'ca-app-pub-yyyy';
    } else if (Platform.isIOS) {
      return 'ca-app-pub-xxxx';
    } else {
      return '';
    }
  }
}

接合部をつくる

接合部とは、「レガシーコード改善ガイド」に出てくる言葉で、ある処理の中身を変更せずに処理の振る舞いを変えられる部分のことです。例を見てもらった方がわかりやすいと思います。

接合部なし

void saveArticle(Article article){
  final api = ArticleApi();
  api.save(article);
}

接合部あり

void saveArticle(Article article, Api api){
  api.save(article);
}
saveArticle(article, ArticleApi);
// こんな感じでも使える
// saveArticle(article, testApi);

あるいは、

class Repository{
  Repository({@required this.api});
  final Api api;
 
  void saveArticle(Article article){
    api.save(article);
  }
}
final repository = Repository(api: TestApi());
repository.saveArticle(articlei);

これの嬉しい点は、 処理の中身を書き換えなくとも、引数やDIコンテナなどで渡す値を変えることで振る舞いを変えられることです。これにより、モックオブジェクトを挿入しやすくなったり、別の種類の振る舞いに切り替えたりしやすくなります。 また、処理の中身を書き換えなくて良いのでより安全です。

computed propertyの活用

クラス内に関係する処理が集中し、同じコードが色々なところに散らばることを防ぐことができます。いわゆる Don't repeat yourself に従ったコードになるわけですね。

class Item{
  int price;

  String priceWithUnit(){
    return '$price円';
  }
}

その他

できるだけreadonlyを使う

readonlyを活用することで、意図せぬタイミングで変数の値が書き換えられることを防ぐことができます。要は変更されるべきでない変数には定数 or 不変宣言をすべきということですね。

定数を直打ちせず、変数を使って名前をつける

final productionApiUrl = "https://production.com/api";
final developmentApiUrl = "http://localhost:3000/api";

定数を直打ちせず、名前をつけていると意味や用途がわかりやすくなります。

また、変数として定義しておくことで、他の場所でも同じ定数を使いたい時にタイプミスを気にせず安全に使いまわせるので便利です。

とはいえ、全ての定数を変数にしようとすると大変なので、

  • 値だけでは意味がわかりにくいもの
  • 複数の箇所で使い回すもの

を変数にすると良いと思います。

空白行で段落を作る

空白行でコードに段落を作ることで、意味の境目をわかりやすくして読みやすくなります。

Linter・Formatterを使う

Linterによる静的チェック、Formatterによる自動フォーマットでコードをより確実に綺麗な状態に保ちます。

終わりに

その他、SOLID原則やアーキテクチャも気にしていたりしますが、ここでは典型的なパターンのみを列挙してみました。 まだまだ勉強中なので、もっといろんな点に注意してコードが書けるようになっていきたいですね。

参考