2022.02.28
STAFF BLOG
スタッフブログ
TECHNICAL
テクログ

どうも!ポケモンゲットしたい、わいです!
ポケモンゲットするためにはただマサラタウンで突っ立っているだけではダメで、自らが草むらに入っていく必要があります。
学びを得るというのも同じで、やはり狩りをするつもりで自らが草むらに入っていく必要があると思います。
今回は多摩川の河川敷で野生の ソフトウェアデザイン 2022年2月号 を捕まえました。
引数地獄の関数
// 別の場所に定義されている
// function makePlayer(url, autoplay=false, loop=false, muted=false) {}
const player = makePlayer('video.mp4', true, true, true);
player.play();
上記のような関数呼び出しのコードには以下のような問題点があります。
- 第一引数は動画のURLっぽいことが想像できるが、他の引数は関数定義を見ないとなにを指すのか全くわからない
- 3つの
boolean
型の引数の順番を間違えやすい - 引数を増やしたときの自由度が低く、古いコードが壊れやすい
引数を増やしたときの自由度が低い
上記問題点の中でもこちらはイメージしにくいと思うので、一例をあげます。
たとえば、上記関数に動画のサムネイルも引数として必要になった場合、ほとんどの動画に対してサムネイルを設定するはずなので、引数の位置は url
の次の第二引数にしたいです。
しかし、そうすると既存の makePlayer
関数の呼び出し箇所をすべて変更する必要が出てきます。これは影響範囲が大きすぎます。
仕方なく最後の引数として追加するか、ほぼ同じメソッドを作成するしかありません。
そして最後の引数として追加すると、他の引数でデフォルト値を使いたい場合は下記のようになってしまします。
const player = makePlayer('video.mp4', undefined, undefined, undefined, 'thumbnail.jpg');
Options Objectパターン
こうした問題を解決するのがOptions Objectというデザインパターンです。このパターンは、位置が重要になる引数を複数使う代わりに、引数を1つのオブジェクトにまとめる手法です。
このパターンで makePlayer
の呼び出しを書き直したものがこちらです。
const player = makePlayer({
url: 'video.mp4',
autoplay: true,
loop: true,
muted: true,
});
引数をオブジェクトにすることで、それぞれの設定がプロパティ名で表現され、設定がなにを指すのかがわかりやすくなります。また、引数の位置も問題なくなります。
関数宣言では、分割代入引数を用いることでどのような設定値を受け取ることができるかが明確になります。
// シンプルな関数定義 => 引数がなにを指すのかまだわからない
function makePlayer(options) {
// options.url で引数にアクセス
}
// 分割代入引数を用いた場合
function makePlayer({ url, autoplay=false, loop=false, muted=false }) {
// url で引数にアクセス
// デフォルト値が設定できる
}
また、引数の順番の制約がなくなったので、サムネイル用の引数も好きな位置に追加することが可能になりました。
function makePlayer({
url,
thumbnail='default.jpg', // 既存の関数呼び出しを気にせず、追加
autoplay=false,
loop=false,
muted=false,
}) {}
// 不要な引数は省略できる
const player = makePlayer({ url: 'video.mp4', thumbnail: 'thumbnail.jpg' });
TypeScriptで型を定義すると、引数をより明確にすることができます。
type MakePlayerOptions = {
url: string;
thumbnail?: string;
autoplay?: boolean;
loop?: boolean;
muted?: boolean;
}
function makePlayer({
url,
thumbnail='default.jpg',
autoplay=false,
loop=false,
muted=false,
}: MakePlayerOptions) {}
さいごに
Options Objectパターン、いかかでしたか。かなりシンプルな発想なので、名前は知らなくても同じことやっていたというのがありそうだなと思いました。
PHPを書いていて、既存の関数に引数を追加するってことは今まで何度もやってきて、そのたびに頭を抱えていたはずなのに、私自身この発想は初めてでした。
このことを自身で考察してみると、PHPの場合はオブジェクトではなく連想配列を使うことになり、分割代入引数が使えない?からかなと思いました。なんとなく連想配列を引数に渡すのは気持ち悪い気がします。(慣れてないだけという可能性が大きいですw)
ちなみに、PHP8.0からは名前付き引数が実装され、今回考えた問題はすべて解決されているようです。
このような感じでまたポケモンゲットしたら報告したいと思います!
以上、わいでした。
健闘を祈る!!
※ ソフトウェアデザイン 2022年2月号 は社内図書で借りました