どうも。つじけ(tsujikenzo)です。このシリーズでは、2021年9月から始まりました「ノンプロ研GAS中級講座6期」について、全7回でお届けします。最終回の7回目を、5回に渡ってお送りしています。いつ終わるのか。。
前回のおさらい
前回は、「内部設計・コーディング・テスト」をお届けしました。
今回は、4回目で「クラス設計のコツ」です。
最近、個人的に、「こうやって書けば、すこし業務アプリケーション開発が楽になるかもしれないなぁ」と、思う書き方があります。
今日はそんな、「オブジェクト指向(っぽいもの)」という手法を取り入れながら、プログラミングしていきます。
今日のアジェンダ
- 引数にオブジェクトを取る
- インスタンスの役割
引数にオブジェクトを取る
クラス化のさいに、「処理の共通化をさせよう」という想いが強くなればばるほど、自然と引数は多くなります。
なんにでも対応できるように、引数を配列でもたせたりしてしまうこともあるでしょう。
/** GmailDraftクラス */
class GmailDraft {
/** メールの下書きを作成するためのコンストラクタ
* @constructor
* @param{Array} string
*/
constructor(personArray) {
const [name, toAddress, ccAddress, subject, body] = personArray;
this.name = name;
this.toAddress = toAddress;
this.ccAddress = ccAddress;
this.subject = subject;
this.body = body;
}
}
オブジェクト指向プログラミングの特徴は、オブジェクト同士が作用しあうという点です。 出典:[GAS][DDD]1章オブジェクト指向とはなにか
たとえば、GmailDraftが生成するインスタンスは、1件のGmailメッセージになるのが自然でしょう。
toAddressやccAddressは、インスタンスによって条件分岐したいところです。
営業部に所属するスタッフが、日報を提出する先は、営業部の管理職ですし、製造部なら、製造部の管理職に送信したいです。
これまでの手続き型プログラミングでは、この条件分岐を、if文で書いていました。
/** GmailDraftクラス */
class GmailDraft {
/** メールの下書きを作成するためのコンストラクタ
* @constructor
* @param{Array} string
*/
constructor(personArray) {
const [name, team, ccAddress, subject, body] = personArray;
this.name = name;
this.team = team;
this.toAddress = team === '営業部' ? 'eigyoMG@gmail.com' : 'seizoMG@gmail.com';
this.ccAddress = ccAddress;
this.subject = subject;
this.body = body;
}
}
しかし、if文は、コードの変化に弱く、可読性を下げる原因のひとつです。
できるだけクラスでは、if文は書きたくありません。
インスタンスの役割
Aさんが営業部に所属しているなら、その状態は、Staffクラスのインスタンスが持っているはずです。
これは、コロコロ変わるものではありません。
インスタンスの状態を、プロパティにもつか、メソッドにもつかは、ときとばあいによりますが、プロパティはあまり増やさない方がいいです。
ここではあくまで例として、「所属」をプロパティに持たせてみました。
/** Staff情報を取り扱うクラス */
class Staff {
/** コンストラクタ */
constructor(staffName) {
this.staffName = staffName;
this.team = this.getTeam();
}
/** スタッフシートのRecordsから、所属チームを返すメソッド
* @return{string} 所属名
*/
getTeam() {
const records = this.getStaffSheetRecords();
const filter = records.filter(record => record['名前'] === this.staffName)[0];
const team = filter['所属'];
return team;
}
/** 日報提出先のメールアドレスを返すメソッド
* @return{string} メールアドレス
*/
getTeamManagerAddress() {
const records = this.getStaffSheetRecords();
const filter = records.filter(record => record['名前'] === this.staffName)[0];
const mail = filter['管理者アドレス'];
return mail;
}
/** スタッフシートのRecordsを返すメソッド
* @return{Array} 連想配列形式のrecords
*/
getStaffSheetRecords() { }
}
/** TEST関数 */
function testStaff() {
//インスタンス生成
const staffName = '辻健蔵';
const s = new Staff(staffName);
//所属を確認
console.log(s.getTeam); //営業部
//日報のメールアドレスを確認するメソッド
console.log(s.getTeamManagerAddress()); //eigyoMG@gmail.com
}
なので、このように、GmailDraftクラスに、Staffクラスのインスタンスを渡すと、if文が消えます。
テスト関数のインスタンスの生成と、GmailDraftクラスのコンストラクタに注目してください。
/** GmailDraftクラス */
class GmailDraft {
/** メールの下書きを作成するためのコンストラクタ
* @constructor
* @param{Object} Staffクラスのインスタンス
*/
constructor(obj) {
this.obj = obj;
this.name = obj.staffName;
this.team = obj.team;
this.recipient = obj.getTeamManagerAddress();
}
}
function testGmailDraft() {
//Staffクラスのインスタンスを生成
const staffName = '辻健蔵';
const tsujike = new Staff(staffName);
//GmailDraftクラスのインスタンスを生成
const g = new GmailDraft(tsujike);
console.log(g.name); //辻健蔵
console.log(g.team); //営業部
console.log(g.recipient); //tsujike@gmail.com
}
もしクラス内で、if文を書かなければならないような状況になったら、コンストラクタや、インスタンス生成時の引数を見直してみましょう。
PDF、Gmail、LINE、Slackなどのクラスは、生成したいコンテンツを(できるだけオブジェクトで)引数に取るとよいでしょう。
まとめ
以上で、「クラス設計のコツ」をお送りしました。
いままで、コンストラクタに渡す引数は、文字列型や配列が多かったかもしれません。
しかしこのように、コンストラクタの引数にオブジェクトを渡すと、一気に新しい世界が待ち受けています。
次回は、 最終回で 「シートクラス」 をお届けします。
このシリーズの目次
[GAS]オブジェクト指向によるGAS開発のススメ