こんにちは。コミュニケーションアプリ「LINE」のモバイルクライアントを開発している石川です。
この記事は、毎週木曜の定期連載 "Weekly Report" 共有の第 39 回です。 LINEヤフー社内には、高い開発生産性を維持するための Review Committee という活動があります。ここで集まった知見を、Weekly Report と称して毎週社内に共有しており、その一部を本ブログ上でも公開しています。(Weekly Report の詳細については、過去の記事一覧を参照してください)
作業の前に連絡を
以下の FooMediaPlayer
は、メディアを再生するためのクラスです。メインとなる関数は play
で、その中の initMedia
によってメディアがロードされ、playMedia
で実際の再生処理を行います。また、再生状態は、playingState
によって公開されます。
class FooMediaPlayer {
private var playingMediaMetaData: MediaMetaData? = null
private var playingMedia: Media? = null
var playingState: PlayingState = PlayingState.None
private set
fun play(mediaMetaData: MediaMetaData) {
playingMediaMetaData = mediaMetaData
initMedia()
playMedia()
}
private fun initMedia() {
val mediaPath = playingMediaMetaData?.filePath
if (mediaPath != null && !isValidMediaFile(mediaPath)) {
playingMedia = null
return
}
...
playingMedia = ...
}
private fun playMedia() {
val media = playingMedia
if (media == null) {
playingState = PlayingState.None
return
}
...
playingState = PlayingState(media, ...)
...
}
}
このコードに、何か改善すべき点はありますか?
「連絡」できる依存関係
initMedia
と playMedia
には、「先に initMedia
を呼ばなければならない」という制約があります。しかし、呼び出し順を変えてもコンパイルは通ってしまい、実行時にエラーが発生することになります。このように 呼び出し順に制約がある場合は、引数などを使って、制約を明示的にする と良いでしょう。別の言い方をすると、正しい順序が強制されるような設計にできると良いです。
以下の実装では、playMedia
の引数に fetchMedia
の戻り値を要求しているため、fetchMedia
と playMedia
の呼び出し順が間違いにくいようになっています。
class FooMediaPlayer {
var playingState: PlayingState = PlayingState.None
private set
fun play(mediaMetaData: MediaMetaData) {
val media = fetchMedia(mediaMetaData)
playingState = playMedia(media)
}
private fun fetchMedia(metaData: MediaMetaData): Media? {
if (!isValidMediaFile(metaData.filePath)) {
return null
}
val media = ...
...
return media
}
private fun playMedia(media: Media?): PlayingState {
if (media == null) {
return PlayingState.None
}
...
return PlayingState(media, ...)
}
}
凝集度と結合度の観点から
順序を明示するために引数を使うことは、凝集度 (strength/cohesion) をより強くし、結合度 (coupling) をより弱くすることも意味します。
凝集度: 改善前の play
は、順序が決ま っている複数の動作で構成されているため、手続き的強度 (procedural strength) の一種になっています。一方で改善後は、戻り値と引数で情報を受け渡しする関係になっているので、連絡的強度 (communicational strength) という、より強い凝集度になっています。
結合度: 改善前の initMedia
と playMedia
では、インスタンスプロパティで情報を共有しているので、クラスのスコープで見たとき 共通結合 (common coupling) になっていると言えます。一方で、改善後のコードは、構造化された値を引数として受け渡ししているので、スタンプ結合 (stamp coupling) という、より弱い結合度になっています。結合度については、 https://speakerdeck.com/munetoshi/code-readability-session-6-ver-2-ja も併せてご参照ください。
一言まとめ
動作の順序を明確にするために、引数と戻り値を効果的に使う。
キーワード: implicit dependency
, function flow
, strength