t-hom’s diary

主にVBAネタを扱っているブログ…とも言えなくなってきたこの頃。

DualShock2KeyCommandのGitリポジトリPublic化と最近のリファクタリング

今日はPS4コントローラーをマクロキーボードとして使うプロジェクト「DualShock2KeyCommand」のリポジトリをPrivateからPublicに変更した。

github.com

これまでPrivateにしていたのはゴリゴリに私用に特殊な設定をハードコードしてるのでPublicにしても別に誰も見ないだろうということだったんだけど、エゴサでブログを参考にしたという話を見たのでコードも公開することにした。
※ブログでは前からコード全文載せてるので利便性が上がったくらいだと思うけど。

ついでに最近のリファクタリングについて

東方モードと称して次のようなキーバインドを登録していたんだけど、いかんせん縦長で全体の閲覧性が悪い。

void mode_touhou(){
  if (PS4.getButtonPress(CROSS)) {
    Keyboard.press('z');
  } else {
    Keyboard.release('z');
  }

  if (PS4.getButtonPress(UP)) {
    Keyboard.press(KEY_UP_ARROW);
  } else {
    Keyboard.release(KEY_UP_ARROW);
  }
  if (PS4.getButtonPress(RIGHT)) {
    Keyboard.press(KEY_RIGHT_ARROW);
  } else {
    Keyboard.release(KEY_RIGHT_ARROW);
  }
  if (PS4.getButtonPress(DOWN)) {
    Keyboard.press(KEY_DOWN_ARROW);
  } else {
    Keyboard.release(KEY_DOWN_ARROW);
  }
  
  if (PS4.getButtonPress(LEFT)) {
    Keyboard.press(KEY_LEFT_ARROW);
  } else {
    Keyboard.release(KEY_LEFT_ARROW);
  }

  if (PS4.getButtonPress(R1)) {
    Keyboard.press(KEY_LEFT_SHIFT);
  } else {
    Keyboard.release(KEY_LEFT_SHIFT);
  }
  
  if (PS4.getButtonClick(L1)) {
    Keyboard.press('x');
    delay(40);
    Keyboard.releaseAll();
  }

  if (PS4.getButtonClick(TRIANGLE)) {
  }
  
  if (PS4.getButtonClick(SQUARE)) {
    Keyboard.press('c');
    delay(40);
    Keyboard.releaseAll();
  }
  
  if (PS4.getButtonClick(OPTIONS)) {
    Keyboard.press(KEY_ESC);
    delay(40);
    Keyboard.releaseAll();
  }
}

例えば一番上のif文はコントローラーの×ボタンが押されていたらzを押して、押されていなければzを離すという処理をストレートに記述しているんだけど、まだまだマシン寄りの記述なので具体的すぎてノイズが多い。もう少し抽象度をあげて人間に優しいコードを目指した結果、こうなった。

void press_sync(ButtonEnum pad, char key) {
    if (PS4.getButtonPress(pad)) {
      Keyboard.press(key);
    } else {
      Keyboard.release(key);
    }
}

void click_sync(ButtonEnum pad, char key) {
  if (PS4.getButtonClick(pad)) {
    Keyboard.press(key);
    delay(40);
    Keyboard.release(key);
  }
}

void mode_touhou(){
  press_sync(CROSS, 'z');
  press_sync(UP, KEY_UP_ARROW);
  press_sync(RIGHT, KEY_RIGHT_ARROW);
  press_sync(DOWN, KEY_DOWN_ARROW);
  press_sync(LEFT, KEY_LEFT_ARROW);
  press_sync(R1, KEY_LEFT_SHIFT);
  click_sync(L1,'x');
  click_sync(SQUARE,'c');
  click_sync(OPTIONS,KEY_ESC);
}

ボタンとキーを同期させるためのpress_syncとclick_syncという関数を作って、メインコードでは「×ボタンとzボタンを同期させろ」という、より人間目線でのやりたいことに近い書き方に変更。

ちなみにこれによって共通処理が一個所にまとまったが、これは副次的な効果にすぎない。共通化それ自体を目的としたコーディングは依存地獄を生みやすいので十分に注意が必要。

今後のリファクタリングの方向性

昨日ヨドバシで次の書籍を購入したので、Arduinoでもオブジェクト指向を取り入れてメンテナンス性の高いコーディングをしたいと思う。

具体的にはPS4コントローラーを継承して動作モードとLEDカラー設定をオブジェクト側に持たせたり、マウスを継承して小数点以下の移動管理をメインコードからマウスオブジェクト側に寄せたりといったことを考えている。

リファクタリングで気を付けたいこと

関数とかクラスを覚えたばかりで陥りがちなのが、メインコードのシンプル化が目的化して複雑さをほとんどクラスや関数に追い出した結果、関数やクラスが肥大化してメンテナンス性が低下するという事象。

コードには「余計な複雑さ」と「必要な複雑さ」があるので、「上手くやれば複雑さはすべて消し去ることが出来る」というのは誤解で、「必要な複雑さはどこかには残るのでバランスよく分散させて薄める」のが正解じゃないかと思う。

当ブログは、amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、 Amazonアソシエイト・プログラムの参加者です。