はじめに
先日、後輩から「Interfaceを実装して似ている処理をまとめました!」とプルリクエストの依頼が来ました。
内容を見てみると、複数のクラスで使用されているメソッドをIntercaceとして実装しているようでした。
一見問題なく聞こえますが、よくよく見てみるとInterfaceの使い方を誤っているようでした。
今回はそんなInterfaceのお話をしていきます。
なお、Unityのスクリプトのお話です。
問題のコード
ではどんなコードだったのか例を書いてみます。
public interface IUpdate
{
public void Update();
}
public class A : MonoBehaviour, IUpdate
{
public void Update()
{
// Do Something
}
}
public class B : MonoBehaviour, IUpdate
{
public void Update()
{
// Do Something
}
}
public class Manager : MonoBehaviour
{
[SerializeField] A _a;
[SerializeField] B _b;
private void Update()
{
_a.Update();
_b.Update();
}
}
別にコンパイルエラーが出るわけでもないし、パフォーマンスに影響が出るわけでもなさそうです。
何がよくなかったのか
まず、MicrosoftのInterfaceの説明を見てみます。
読んでも正直よくわからないですよね。
重要なのは以下の文章です。
インターフェイスでは、実装型が演算子またはその他の静的メンバーを定義する必要があることを宣言できます。
言い換えると、あるInterfaceを実装したクラスは、Interfaceで定義したメソッドが必ずあるということになります。
要は、そのクラスを知らなくても、Interfaceさえ知っていればそこに実装されたメソッドがあることは分かるということです。
これは、メソッドを抽象化しクラスの結合度を下げることができるわけです。
※言葉の意味が分からない方はそれぞれググってみるとよいと思います。
先ほどのコードに戻ると、Interfaceで実装されたメソッドを使ってはいますが、
具体的なクラス(A
やB
)を知ってしまっているので、Interfaceの恩恵を受けられていません。
恩恵を受けているような書き方にするには以下になると思います。
public interface IUpdate
{
public void Update();
}
public class A : IUpdate
{
public void Update()
{
// Do Something
}
}
public class B :IUpdate
{
public void Update()
{
// Do Something
}
}
public class Manager : MonoBehaviour
{
IUpdate _updatable;
private void Start()
{
_updatable = new A();
}
private void Update()
{
_updatable.Update();
}
}
何が変わったのかといえば、Managerクラスで保持しているフィールドがInterfaceで定義されている点です。
これにより、Start()の中でIUpdateというInterfaceを定義したクラスであればどんなものでも、IUpdateで定義されたUpdate()というメソッドを呼び出すことができるわけです。
この例だと、Start()の中でA
というクラスをインスタンス化してますが、
別にB
というクラスでも問題なく動作します。
なぜなら、どちらのクラスもIUpdateというInterfaceを実装しているからです。
このように、Interfaceのメリットは具体的なクラスを知らなくても、
Interfaceさえ知っていればメソッドを呼び出せるというものです。
まとめ
Unityで使用するコードでこのようなことが起きたわけですが、
Unityってコンポーネント指向なところがあって、Unityが用意しているInterfaceを使うことはあっても、自作したInterfaceを活かせる場面は、システムのかなりコアな部分でないとなかったりします。
となると、経験の浅いプログラマはInterfaceを実装する機会も減り、
使い方が分からなくなってしまいがちです。
私の後輩もこのような状態になっていたのだと思います。
しかし、この後輩は似たような処理をまとめようと考えたり、
Interfaceを使ってみようと考えたわけです。
素晴らしいですよね。
もし、Interfaceがあまりよくわからないという方も、この後輩のようにチャレンジのつもりで使っていると、だんだんとそのメリットと使いどころが分かってくると思います。
ちなみに、Interfaceで定義したフィールドをSerializeFieldの属性にしても、Inspector上に表示されませんのでご注意を。(表示できるようにする拡張エディタが売っていたりはしますが。)
コメント