[GAS]クラスを使ったテクニック

今回はGoogle Apps Script(GAS)におけるクラスを使用した、シートに関するデータと処理を一箇所にまとめるテクニックを紹介したいと思います。

まず以下のようなシートがあったとします。

各データは在庫コード、品名、在庫数といったデータを持っています。

例えばある処理の中で指定の在庫コードの商品の在庫数を現在からn個加算(入庫処理)させたい場合などは、通常はその処理の中で直接シートに対する処理を書く事が多いでしょう。

例えば以下のような感じで実装したとします。

const someProcessing_1 = () => { // なんらかの処理を行う関数
  // なんらかの処理・・・

  // 入庫処理開始
  const targetCode = 'I-0015' // 例えば在庫コードが「I-0015」の在庫数を加算したかったとする
  const additionalStock = 20 // 20個在庫を加算する

  const inventorySheet = SpreadsheetApp.getActive().getSheetByName('在庫マスタ')
  const inventoryValues = inventorySheet.getDataRange().getValues().slice(1)

  // 在庫コードが「I-0015」のレコードを取得する
  const foundItem = inventoryValues.find(([code])=>code === targetCode)

  // 現在の在庫数を取得
  const currentStock = foundItem[2]
  const updatedStock = currentStock + additionalStock // 加算後の在庫数

  // シートの在庫数を更新する
  const targetRow = inventoryValues.findIndex(([code])=>code === targetCode) + 1
  inventorySheet.getRange(targetRow, 3).setValues(updatedStock) // 在庫数を書き換える
  // 入庫処理終了

  // なんらかの処理・・・
}

この方法だと例えば他の処理でも入庫処理を行いたい場合、その処理の中でも同じ実装を再度書かないといけないため、ロジックに重複が生じます。
なので通常は入庫処理に関する実装部分を以下のように別関数に切り出して定義します。

const addStock = (targetCode, addition) => { // 入庫処理を行う関数
  const inventorySheet = SpreadsheetApp.getActive().getSheetByName('在庫マスタ')
  const inventoryValues = inventorySheet.getDataRange().getValues().slice(1)

  // 在庫コードが「I-0015」のレコードを取得する
  const foundItem = inventoryValues.find(([code])=>code === targetCode)

  // 現在の在庫数を取得
  const currentStock = foundItem[2]
  const updatedStock = currentStock + addition // 加算後の在庫数

  // シートの在庫数を更新する
  const targetRow = inventoryValues.findIndex(([code])=>code === targetCode) + 1
  inventorySheet.getRange(targetRow, 3).setValues(updatedStock) // 在庫数を書き換える
}

const someProcessing_2 = () => { 
  // なんらかの処理・・・

  addStock('I-0015', 10) // 入庫処理の関数呼び出し

  // なんらかの処理・・・
}

const someProcessing_3 = () => {
  // なんらかの処理・・・

  addStock('I-0001', 15) // 入庫処理の関数呼び出し

  // なんらかの処理・・・
}

共通部分を関数に切り出せばこのように重複をなくすことが出来ました。
別の箇所で入庫処理を行いたい場合は、addStock関数を呼び出すだけで良く、かなりコードの共通化が進みました。

クラスを使用する

しかし、さらに在庫を出庫する処理や新商品を追加したい場合など、在庫マスタに関する処理が増えてきた場合を想像してみましょう。

入庫処理、出庫処理、商品登録処理はいずれも在庫マスタに関する処理なので一箇所にまとめておきたいと言うニーズが出てくると思います。

さらに今後同じスプレッドシートファイル内に備品管理や別のリソースを管理するシートが追加された場合にどの関数がどのシートに対応しているかが管理が煩雑になる可能性があります。

さらに関数単位でシートオブジェクトを生成しているなど重複した処理を書いているのでそれらも共通化したいところです。

そういった時に便利なのが「クラス(Class)」になります。
上記の実装と出庫処理、商品登録処理も追加した場合の実装がこちらになります。

// 在庫マスタシートに関連したデータと処理をまとめたクラス
class InventoryMaster{
  constructor(){ // コンストラクタ内でシート内で共通する処理の設定を行う
    this.sheet = SpreadsheetApp.getActive().getSheetByName('在庫マスタ')
    this.values = this.sheet.getDataRange().getValues().slice(1)
  }

  // 入庫を行う処理(メソッド)
  addStock(targetCode, addition){
    const foundItem = this.values.find(([code])=>code === targetCode)

    // 現在の在庫数を取得
    const currentStock = foundItem[2]
    const updatedStock = currentStock + addition // 加算後の在庫数

    // シートの在庫数を更新する
    const targetRow = this.values.findIndex(([code])=>code === targetCode) + 1
    this.sheet.getRange(targetRow, 3).setValues(updatedStock) // 在庫数を書き換える
  }

  // 出庫を行うメソッド
  deliverStock(code, stock){
    // 実装は省略・・・
  }

  // 商品を新規登録するメソッド
  register(code, name, stock){
    // 実装は省略・・・
  }
}

// クラスを使用する
const someProcessing_3 = () => { // なんらかの処理を行う関数
  // なんらかの処理・・・

  // InventoryMasterクラスをインスタンス化
  const inventoryMaster = new InventoryMaster()

  // 入庫処理
  inventoryMaster.addStock('I-0015', 10) // addStockメソッドを呼び出して在庫を加算する

  // 出庫処理
  inventoryMaster.deliverStock('I-0001', 20) // addStockメソッドを呼び出して在庫を加算する

  // 出庫処理
  inventoryMaster.register('I-0032', '商品AF', 20) // registerメソッドを呼び出して新商品を追加する

  // なんらかの処理・・・
}

クラス定義の中にある「constructor(コンストラクタ)」メソッドがありますが、ここでインスタンス化の時に行ってほしい処理を実装します。

この場合、クラス内でシートオブジェクトは共通で使いまわしたいのでここで定義しておく事で、例えば「this.sheet」と指定すれば、クラス内のどこからでもシートオブジェクトを取得する事が可能になります。

このようにクラスを使用する事で同一シート内の様々な処理を一つのクラスに閉じ込める事が出来、さらに共通部分の重複も削除する事ができるためコードの見通しが良くなりました。

大量のシートがある場合は実装コストにオーバーヘッドが発生しますが、関数だけではなくクラスという選択肢もあると何かと便利なのでぜひ知らない方は覚えておくと手札が増えて良いんじゃないかと思います。

それではまたごきげんよう。