はじめに
筆者は新卒2年目のWebエンジニアです。
GASのライブラリを作成することになったため、ライブラリを設計する際の注意点を調べました。Gopher Academy Blogの記事を参考にしています。
信頼性があること
当然ですが、ライブラリはその機能が確実に実行される必要があります。
何を提供するかが一言で説明できる
ライブラリが行う機能・使うリソースは一つだけであれば、信頼性は高くなります。
例えば複数のスプレッドシートのIDを読み込んで、シート間であらゆるデータを扱えるようにするライブラリは便利そうですが、保守が難しく多くの場合要件に対して過剰な性能を持っています。このようなライブラリは避けるべきです。
また機能を細分化しすぎるのも気をつけなければなりません。
冒頭で紹介した記事では反面教師としてleft-padというライブラリが紹介されています。文字をパディングするのみの機能しか持たないライブラリです。最小限の機能だったためあらゆる箇所で利用されてしまい、これが非公開になった時、大混乱に陥ったのです。 (参考)
違う操作であっても同じ理由を背景とするのであれば、一つにまとめるべきです。例えば、CRUDのそれぞれを切り離してライブラリ化するのは得策ではありません。
十分にテストされている
ライブラリが適切にテストされていれば、信頼してライブラリを使えます。
冒頭で紹介した記事では PBT(プロパティ・ベース・テスト) がおすすめされています。
詳しくは下記の記事で紹介されています。
ユーザーのリソースを管理しない
ライブラリが裏側で勝手にライブラリを読み込むと、それだけライブラリの責任が大きくなるため信頼性が下がります。ユーザー側でもリソースを任意に管理できるようにします。
例えば、以下のようなコードは外部から読み込むシートが分からないため、避けるべきです。
function appendRowToSheet(values)
{
const sheet = SpreadsheetApp
.openById('xxxx')
.getSheets()[0];
sheet.appendRow(values);
}
利用者がシートを指定できるよう、以下のようにするのが賢明です。
function appendRowToSheet(sheet, values)
{
sheet.appendRow(values);
}
使いやすいこと
使いやすくする方法はいくつかあります。
ドキュメントとサンプルが充実している
ReadMeなどのドキュメントが優れていると、そのライブラリは使いやすくなります。
上手な書き方は以下の記事が参考になります。
冒頭で紹介した記事には、サンプルコードの掲載がおすすめされています。
例外を不必要に投げない
不必要な例外を投げないようにした方が良いです。というのも、例外が投げられた後はユーザーが制御できないからです。
想定外の動きは可能な限りエラーとして返し、利用者に判断を委ねるのが賢明です。
依存が最小限である
ライブラリ内でほかのライブラリを利用することは最小限に止めるべきです。
冒頭で紹介した記事では、各ライブラリがどこかのサーバーに通信していないかを確認する必要があること、ソースコードの共有を主目的とするライブラリがライブラリに依存しているのは不適切であることが指摘されています。
またGASの場合は利用者が依存ライブラリも別途追加しなければならない手間が発生するのも依存を減らす理由の一つになります。
引数を渡さなくても動く
何も渡さなくてもある程度デフォルトで動く状態にすべきです。
設定を何も書かなくても動くのが理想であり、必要な人だけが設定を書くようにすれば利用者の負担も減ります。
逆に、引数を渡せばカスタマイズできるようにするのも大切です。
「使いやすいこと」の各項目は、驚き最小の原則にも通じます。
驚き最小の原則については、以下の記事が分かりやすいです。
再利用可能
様々な場面で再利用できるライブラリは優秀といえます。
引数はクラスに依存しない
可能な限り、関数の引数は具体的な型やクラスに依存しないようにします。必要な条件をみたしているのであれば、どんな値でも受け入れられるようにするべきです。
返り値は具体的に
返り値は分かりやすく具体的に返すようにします。
例えば、APIの中にrow-indexの項目を入れることで、APIの返り値がどのくらいの長さなのかを知ることができるようにします。
拡張できる
「引数を渡さなくても動く」の項でも触れたように、デフォルトでも動くもののある程度利用者がカスタマイズできる余地を残すと再利用しやすくなります。
function notifyError(message, handler)
{
if (handler) {
handler(message);
} else {
Logger.log(message);
}
}
//handlerとして自由に設定した方法でエラーを送信できる
Lib.notifyError('失敗しました',
(msg) => { MailApp.sendEmail('admin@example.com', 'エラー', msg); });
ほかのライブラリと組み合わせられる
ライブラリを一つの閉じた世界で完結させずに、利用者がほかのライブラリと併用した時のことも考えるのが大事です。
例えば返り値をライブラリ内でしか使われていないクラスにするのは避けるべきです。全く異なるライブラリも難なく受け入れられるように、仕組みを構築する必要があります。