概要
関数は便利な代わりに使い方を間違えると、ただ可読性が下がりバグの原因になる代物だったりします。
関数のメリット・デメリット
メリット
- 処理を共通化できる
- 関数レベルのテストが可能になる
- 可読性が向上する場合がある
デメリット
- 可読性が低下する場合がある
可読性を左右するポイント
可読性を左右するポイントは1つだけです。
- 呼び出し元が関数内の実装を知らずに扱えるかどうか
例を出すなら、誰しもオープンソースライブラリの関数を使っていると思いますが、
仮にその関数の実装まで把握する必要があったら、可読性が低いと感じると思います。
実装を知らずに扱える関数の特徴
- 副作用がない
- 関数名が機能を表している(動詞 or 動詞+名詞)
- 適切なクラスに配置してある
- 明確な引数
1. 副作用がない
薬の副作用と同じで、関数の主作用以外の作用のことです。
副作用の例
public List<Input> OrderByName(List<Input> inputList)
{
inputList = inputList
.OrderBy(input => input.Name)
.ToList();
return inputList;
}
この関数は順番を並び替えて応答する主作用のほかに、呼び出し元でも引数の値が変わってしまう副作用を持っています。
よくある副作用は例に出したような、引数の値が変わってしまうことですが、
関数名が主作用を表していて、それ以外は副作用にあたります。
2. 関数名が機能を表している(動詞 or 動詞+名詞)
関数とは動作のことで、動詞から始まらなければ不明確になります。
そしてどんな動作をするのかを使い手が予測できる明確な名前を付けます。
関数名の例
class UserService
{
public UserRepository _userRepository;
public UserCacheRepository _userCacheRepository;
public void AddUser(User user)
{
_userRepository.Add(user);
_userCacheRepository.AddUser(user);
}
}
上記がユーザー追加の例だとわかるのは、関数名が適切に表現されているためです。
関数名はクラス名と密接にかかわってくるため、今回のuserRepositoryのような責務が明確なクラスはAddUserでもAddでもユーザーを追加することがわかります。
3. 適切なクラスに配置してある
先ほどの関数名の例は関数が適切なクラスに配置してありましたが、
不適切なクラスに配置した場合は、動作が不明確になります。
悪い例、不適切なクラスに配置してある
class UserService
{
public DeviceRepository _deviceRepository;
public DeviceCacheRepository _deviceCacheRepository;
public void AddUser(User user)
{
_deviceRepository.Add(user);
_deviceCacheRepository.AddUser(user);
}
}
4. 明確な引数
一部しか使わないのに、オーバーな引数を設定するのは、引数の設定にコストがかかり、関数の動作の勘違いに繋がります。
明確な引数の例
class UserService
{
public UserRepository _userRepository;
public UserCacheRepository _userCacheRepository;
public void AddUser(User user)
{
_userRepository.Add(user);
_userCacheRepository.Add(new UserCacheData()
{
UserId = user.Id,
UserName = user.Name
});
}
}
UserクラスをそのままUserCacheRepositoryのAddに渡してしまうと、User情報全てがキャッシュされると読めてしまうため。
仮にNameだけがキャッシュ対象の場合は引数で明確にすることが可読性を上げてくれます。
まとめ
以上がオブジェクト指向プログラミングで関数の作り方でした。
読み手を意識した関数は自然とテストしやすい関数になると思います。