オブジェクト指向の2つの世界

オブジェクト指向は使われ方で2つの世界に分けて考えられ、2つの世界を混ぜないことが重要だと思っているのだが、その考えについてちょっとアウトプットしてみる。

## 1. 意味指向オブジェクト指向の世界
後に「最初のオブジェクト指向言語」と呼ばれることになるSimulaは、群体シミュレーションのために作られた言語である。シミュレーションのためには、少しづつ異なる要素を大量に生成しなくてはならない。

そのために、現在でもお馴染みのクラス・サブクラス・継承などの仕組みが開発された。これにより、ある要素を派生させて別の要素を作ると言うことが簡単にできるし、要素間の関係の「意味」が言語機能によって陽に書ける(クラスBirdはクラスAnimalの一種である、と言うように)。

この使われ方を「意味指向オブジェクト指向」と呼んでみる。学校で習うような教科書的なオブジェクト指向、すなわちAnimalクラスを継承してDogクラスやCatクラスを作るようなオブジェクト指向は、「意味オブジェクト指向」に属する。

## 2. 構造指向オブジェクト指向の世界

オブジェクト指向言語は、GUIシステムを書くのに好都合な言語である、という側面がある。

例えば、GUIシステムで「画面内のどのボタンがクリックされたか」を検出することを考える。これを実現するには、まず画面のどこがクリックされたかを検出し、次に各要素の位置と大きさからどの要素がクリックされたかを検出するだろう。

そのとき、下記のように位置と大きさが格納されているとしたら、ボタンが追加されたとき`else if`を追加する必要があり、メンテナンス性が悪い。

```ts
const button1Height = 10;
const button1Width = 30;
const button1X = 20;
const button1Y = 200;
const button2Height = 10;
const button2Width = 30;
const button2X = 70;
const button2Y = 300;

// クリック検出
if(button1X <= clickX && clickX <= button1X + button1Width && button1Y <= clickY && clickY <= button1Y + button1Height){
    // button1がクリックされたときの処理
} else if (button2X <= clickX && clickX <= button2X + button2Width && button2Y <= clickY && clickY <= button2Y + button2Height) {
    // button2がクリックされたときの処理
} else {
    // クリックされたボタンはない
}

```

下記のようにクラスを使い、多態性を活用して実装すると、ボタンの追加が簡単で、メンテナンス性が良くなる。

```ts

class Widget {
  public x:number;
  public y:number;
  public width:number;
  public height:number;
  public action:()=>void {
  }
}

// ボタン1クラス
class Button1 extends Widget{
  public x:number = 10;
  public y:number = 20;
  public width:number = 200;
  public height:number = 200;
  public action:() => {
    console.log("button1");
  };
}

// ボタン2クラス
class Button1 extends Widget{
  public x:number = 100;
  public y:number = 220;
  public width:number = 200;
  public height:number = 200;
  public action:() => {
    console.log("button1");
  };
}

// ボタンの登録
let buttons: Widget[] = [
    new Button1,
    new Button2,
];

// クリック検出
for (let i = 0;i < buttons.length;i++){
    if(buttons[i].x <= clickX && clickX <= buttons[i].x + buttons[i].width && buttons[i].y <= clickY && clickY <= buttons[i].y + buttons[i].height ){
        buttons[i].action();
    }
}

```

このように、オブジェクト指向が実現する構造を利用するとGUIの実装は簡単でメンテナンス性も良くなる。もちろんオブジェクト指向の構造を使わず、構造体や関数ポインタを利用しても似たようなことはできる(し、そのような実装になっているGUIシステムもある)が、オブジェクト指向言語を使ってしまった方がわかりやすいだろう。

しかし、このオブジェクト指向になにか「意味」があるわけではない。ここではWidgetクラスを継承してButton1を作っているが、Button1は「意味」的にWidgetクラスの一種であるわけではない。「構造」的にButton1クラスにはWidgetクラスと同じメソッドとメンバが実装されている必要があるので、継承という「構造」が使われているというだけである（そのため、例えば関数型言語を使う方が好都合だということであれば、置き換えても全然問題がない）

このようにオブジェクト指向言語の「構造」を利用したプログラミングを、「構造指向オブジェクト指向」と呼んでみる。

## 二つの世界を区別して扱う

この二つを混ぜないようにすることが重要だと思う。

例えば、iOSプログラミングでUIViewControllerのサブクラスを作るのはよい。フレームワークの設計指針に則ったことだからだ。しかし、「アプリの他の部分もオブジェクト指向っぽく書くべきなのかな？フレームワークや言語がオブジェクト指向だし」という考えは別に持たなくていい。

なぜなら、GUIシステムにおいてオブジェクト指向言語が使われているのは、拡張可能なGUIを作るのに好都合だという「構造志向」な理由が主だからだ。

もちろん、自分なりの考えがあってオブジェクト指向を使うのはよい。しかし考えなしに、なんとなくフレームワークに合わせるためにオブジェクト指向を使う必要はない。自分のアプリ専用の基底ボタンクラスとかは、自分で必要だと思わない限り作らなくてよい。

意味志向でオブジェクト指向が使われるケースとしては、クリーンアーキテクチャやドメイン駆動設計があると思う。この場合、ドメイン部分は意味志向寄りの考えをした方が良く、外界に近い部分は構造志向で考えるほうがよさそうである。

ともかく、オブジェクト指向を使う理由は意味を表現するためなのか、構造を活用したいのか、その区別をはっきりさせて作っていくと、無駄なところでオブジェクト指向の機能を使ってしまうこともなくなり、わかりやすくなると思う。

以上