4-4. 継承とComposition
継承とComposition
オブジェクト指向プログラミング(OOP)に慣れている開発者は、継承やComposition(例:Componentシステム)のエンティティ構築を活用したいと考えるかもしれません。ここでは、継承とCompositionベースのエンティティ構築手法について示します。なお、Roblox実装ではここまでの実装機能を必要としないケースも多いので、発展的なTOPICとして捉えてください。
継承を用いた実装例
基底クラス:BaseCharacter
基本のキャラクターを構成する基底クラス「BaseCharacter」クラスを定義します。今回はキャラクター基本機能としてAnimation再生が可能なクラスにしています。
派生クラス:Warrior
派生クラスでは、基底クラスの機能を拡張またはオーバーライドします。Warriorではattackやtalkなどの機能拡張したキャラクターとします。
Warriorクラスのテーブルは、BaseCharacterをメタテーブルとして設定済みとなるため、Warriorクラスに対象のメソッドが見つからない場合、BaseCharacter側のメソッドを探しにいくようになります。またインスタンス生成時には、のテーブルをベースに作成するのでBaseCharacterのプロパティも引き継ぎます。
これらにより、Warriorクラスで実装しているメソッド等が優先されて利用されながら、ない場合はBaseCharacterのメソッドも使える、というポリモーフィズムを実現した継承実装ができています。
継承の利用に関して
継承は、特にRoblox開発者コミュニティでは実際にはあまり推奨されない手法です。Luaにはクラス機能がネイティブではなく、継承関係の管理や呼び出しがやや複雑でミスが発生しやすいなどの理由があります。代わりに、単一のクラスを利用するか、より柔軟なCompositionアプローチが推奨されます。とはいえ有用な場面もあると思いますので、状況を見極めて活用しましょう。
Compositionを用いた実装例
Compositionは、複数のコンポーネントを組み合わせて、より複雑なエンティティを構築する方法です。UnityでのComponentシステムがその代表例といえます。以下でComponentを組み合わせたエンティティ作成例を示します。
各機能をComponentとして実装
バトル系の機能や、会話の機能をそれぞれ機能別のComponentとして実装します。
エンティティ:Warrior
Warriorに必要なComponentを付与して構成します。
Warriorクラス内でWarrior(戦士)を構成するのに必要な機能(Component)を付与して構成しており、追加で機能が必要になった場合もComponentを作成して付与して拡張できます。Componentの場合、別なクラス(例:)が必要になった場合も必要な機能を同様に付与して効率的に構成していくことができます。これにより、機能を効率的に再利用可能とし、また機能単位で実装が分かれているため管理もしやすくなります。ただし、実装としては粒度が細かくなり実装の煩雑さが少し増します。
比較と活用
継承はコードの階層構造を作成しポリモーフィズムを実現できます。ただしクラス機能を持たないLuaにおいては少々使いにくいのは事実です。また階層が深くなると複雑さや、管理の煩雑さ増し柔軟性も低下する場合があります。
Compositionではエンティティの柔軟な構築を可能にします。ただし、実装が分割されすぎるなど、実装規模に対しては無駄に煩雑な実装になる場合もあります。
これらは設計方針に依存することであり、完全な正解などはありませんが、基本的にRoblox実装では、単一のクラスで済ませるか、Compositionでの再利用性高めることが、有効なアプローチと考えられるでしょう。