Unityでインディゲームを作る!

Unityでのゲーム制作を目指し、それに関わる話題についてのブログ

C#におけるインターフェースは、横の繋がりを作るための変換アダプター [ interfaceとは ]

 C#におけるinterface : インターフェースについてです。継承やポリモーフィズムはclass間の縦の繋がりを作り出したり、それを利用する機能であるのに対して、interfaceは横の繋がりを作り出すための機能と言えると思います。

 interfaceを使うことで、異なるclass間に共通のメソッドを持たせたり、あるいは同一のオブジェクトとして、まとめ上げることも出来るようになります。

  また、C#では多重継承が禁止されていますが、interfaceに関しては複数のinterfaceを装着することが出来るようです。

 

classの復習

 classとはオブジェクト指向のプログラミングにおいて、単純なタイプでは定義しきれない複雑、かつ規模の大きいデータタイプを定義するためのものです。

 また、あるclassから内容を引き継いだclassを更に定義することが出来ます。これが『継承』と呼ばれる機能で、内容を重複させることなく、コード量を減らすことが出来ます。

 元となったclassは親クラス、あるいはbase classと呼ばれ、継承したクラスは子クラス、またはsub classとなり、親子関係、上下関係といったヒエラルキーが発生します。

 

 コードの量が減り、管理しやすくなる、という利点の他に関係のあるclassを抽象的に扱うことが出来るようになります。

 抽象的に扱う、とは、細かい差異を気にせずに物事を本質的に捉える、ということであり、プログラミングにおいては、縦の繋がりのある各classを同一のclassとしてザックリ捉えることでコードをより単純にするという機能です。

 ポリモーフィズム(Polymorphizm)とは、class間の縦の繋がりを利用し、同一のメソッドを持たせつつも、クラス毎に異なった実装をするという機能です。

 

ゆるい横の繋がり

  classはプログラムにおけるコードを機能や概念ごとに分けてまとめることで管理しやすくするためのモノですが、class間の繋がりは疎であるのが良い、とされます。

 継承は縦の関係を作り出すものですが、class間の関係が複雑化してしまう恐れがあり、上手く使わないといけない機能です。

 

 概念も違うし、文脈も異なるclass同士を繋げるのは更に危険だということになります。各classの独立性を保ちながらも、横の繋がりを作り出せるというのがinterfaceの利点です。

 

人(ヒト)と自動車の共通点

 突然ですが、我々人間と自動車の共通点はあるでしょうか。かたや生物、かたや機械で工業製品・・・まったく別々の存在です。

 これが人間と犬ならば、『動物(Animal)』という括りでまとめることが出来るかもしれませんし、更に細かい『哺乳類(Mammal)』というclassでまとめることも出来るかもしれません。

 しかし、ヒトと自動車ではそうはいかず、この世における存在、オブジェクト(Object)として扱うぐらいしかないのでは?と思います。でも、そうなるとObjectの定義が猥雑になってしまうかもしれないし・・・。

 

 なので、ヒトclassと自動車classを同一のモノとして上手く扱うためには、外部アダプターが必要になります。

 

interfaceに中身は無い

 日常生活にある、様々なアダプターは連結のためのアダプターが多いですが、それ自体にはほとんど機能がない、中身がない装着具です。

 interfaceも同様にその中身はinterfaceとしての名前とメソッド名だけです。メソッド内の実装をinterface内部で行うことは出来ません。interfaceが用意するのは、あくまでガワ、表面上のコトだけです。

f:id:miur-us:20190508160341p:plain

interfaceの中身は未定義のメソッドだけ

 頭文字を大文字のI(アイ)にするのは慣例ですが、interfaceだと分かるようにするためにも、この慣例に従った方がいいと思います。

 今回、ヒトと自動車を扱うためのinterfaceを利用するテストプログラムを書きましたが、Icontrollerというinterfaceを用意しました。

 PrintNameは名前などを表示するためのもので、Moveは目的地に移動せよ、というメソッドです。 

 

ヒトclassと自動車class

 最低限のclass設計ではありますが、ヒトclassと自動車classをそれぞれ用意しました。なんらか名前を持っている、という様な部分は共通していますが、例えば、ガソリンの搭載量、残量というのは自動車ならでは変数ですし、どちらにせよ、文脈的な繋がりがあるべきではない、まったく独立したclassであるべきの存在です。

f:id:miur-us:20190508161313p:plain

Human class Icontrollerを装着済み

 メンバー変数としては名前だけでprivateメソッドとして、WalkTo(歩いていく)というメソッドがあります。PrintNameとMoveはinterface由来のメソッド。interfaceを付けると、そのinterfaceにあるメソッドを実装することを強制されます。

 interfaceをそのclassに装着するには、class名の後にコロン(:)を打って、そこに表記するので、継承したclassと並ぶ場合があり、ややこしいっちゃあややこしいです。その場合、コンマで区切る必要があります。

f:id:miur-us:20190508161726p:plain

自動車(Car)class

 Car classにはメンバー変数として、名前だけでなく、重量とガソリン量があります。人間classにも体重はあっても良かったですが、class同士の差異を強調するために省いた、ということで。

 自動車は運転するモノなので、人間の『歩いて行く』に対して、DriveToという移動のためのメソッドを定義しています。

 

ヒトと自動車を同等にあつかう

 本プログラムでのMainメソッドでは、まずIcontrollerのリストを生成してから、Human classとCar classのインスタンスをそれぞれ一つずつ作って、リストに追加します。そして、foreach文で各々のPrintName, Moveを実行する、という内容です。

f:id:miur-us:20190508163626p:plain

Main文。interfaceによってヒトと自動車をリスト化できる

 List<>は本来、同タイプのデータしかリスト化できませんが、Icontrollerタイプとすることで、リスト化することができます。

 ここではHumanタイプとして、johnというインスタンスを、IcontrollerタイプとしてblueBirdというCar classのインスタンス を生成しています。どちらもList<Icontroller>に追加出来ています。

 

 GetTypeメソッドでTypeを確認できます。元のclass名だけでいいので、GetType().Nameがいいです。

f:id:miur-us:20190508163313p:plain

出力結果。異なるクラスをまとめて同様の処理が出来ている

 というわけで、名前などの情報を表示する、駅まで移動させる(残りのガソリンも計算して表示する)という単純なプログラムではありますが、異なるclassをまとめてイテレーションする、ということがinterfaceによって行うことが出来ました。

 

まとめ

 interfaceはメソッド、しかもその名前しか定義できないし、何に使うのかもさっぱり分からない機能だな、と思っていました。

 しかし、最近になって、『継承』などの機能と相対する機能であるということに気付き、その有用性を少し理解できた気がします。

 

 使いこなせるようになるのは、もうしばらく掛かりそうですが、便利な機能なので、正しく使えるようになりたいです。