もくもくブログ

メモに近い。記事の正確性には期待をしないでください。

Follow me on GitHub

開発時にViewの実装で守っている事

背景

MVC/MVP等のソフトウェアアーキテクチャにViewなる概念があります。
アーキテクチャはしばしば解釈に幅がありますが、見た目に関する実装を通常は指します。
(ゲーム開発ではどこまでViewとみなすかで流儀があるとは思いますが)

今回はシンプルにUIに対するView実装に着目して、普段の開発で守っていることを記載したいと思います。
これは特定のフレームワーク上でのみ有効なものではありません。

守っている事

Viewでは単一の機能だけを提供する

文字通りです。
例えば以下のようなボタンを表現するためのViewがあるとして、以下のような記述は基本的には避けています。

public class ButtonView {
  // note: グレーアウトを制御
  public void SetGrayout(bool isGrayout){ 
   /* 何らかの実装 */
  
    // note: グレーアウトさせるなら選択不可能にする
    if(isGrayout){
     SetEnableClicked(false); 
    }
  }
  
  // note: 選択可能かどうかを制御 
  private void SetEnableClicked(bool enable){ /* 何らかの実装 */ }
}

このケースだと、「グレーアウトさせる場合は必ず選択不可能」な状態となっているため、汎用性に欠けます。
(動作が確定しているときや覚悟を決めた時は良いと思いますが、Viewのような異なる文脈で
使いまわされるようなクラスに対して、制御に縛りを設けるとだいたい不幸な事になるので避けています。)

なので下記のように、機能別に分けています。
このようにしておくと、異なる文脈でも柔軟に対応ができます。
そして、Viewを協調させて一つの文脈に対応する挙動はViewControllerとでも名付けて、このクラスに任せています。

public class ButtonView {
  // note: グレーアウトを制御
  public void SetGrayout(bool isGrayout){ /* 何らかの実装 */ }
  // note: クリック可能かどうかを制御 
  public void SetEnableClicked(bool enable){ /* 何らかの実装 */ }
}

文脈に対応するViewの制御はViewControllerを使う

先の話の続きになりますが、Viewを機能別に分けたことでView制御するクラスが必要になります。
それはViewControllerクラスで行われます。これは以下のような形となります。

public class HogeViewController {
  private ButtonView _buttonView;
  
  public void SetGrayOut(bool isGrayout){
    if(isGrayout){
      _buttonView.SetGrayOut(true);
      _buttonView.SetEnableClicked(false);
    }
    /* 何らかの実装 */
  }
}

これらのViewControllerを場面ごとに組み合わせていくことで、実装を行っています。

Viewの内部構造を外部に露出させない

これはオブジェクト指向プログラミングの基本的な話ですが、内部構造は露出させないようにしています。
例えば先のButtonViewがUnityのMonoBehaviourの場合に、transformやgameObjectを外部で触らないようにしています。
(といっても、個人開発でやっていることは意識的に全て関数でラップさせているだけですが。) このようにしておくと、内部の実装に変更があっても利用者はそれを気にせずに利用し続けることができます。

まじめにチーム開発で運用する場合は毎回interfaceを切って、どう頑張っても内部構造を触らせないようにすることもできます。 個人開発では面倒なだけなので切っていません。

終わりに

以上が守っている事です。
書いておいてなんですが、これらはViewに限った話ではないですね。
それはそれとして守っておくと平和になりがちなので、とりあえず真似をしてみることをおすすめします。