ブログ 約5分

OGREエンジン:OOP原則のケーススタディ

Share this article
OGREエンジン:OOP原則のケーススタディ

OGRE(Object-Oriented Graphics Rendering Engine)は、シーン指向の柔軟な3Dレンダリングエンジンで、C++で書かれており、開発者がハードウェアアクセラレーションによる3Dグラフィックスを利用したアプリケーションを、より簡単かつ直感的に作成できるように設計されています。このクラスライブラリは、Direct3DやOpenGLなどの基盤となるシステムライブラリを使用する詳細を抽象化し、ワールドオブジェクトやその他の高レベルクラスに基づくインターフェースを提供します。

CppDependで解析して、その設計上の利点を発見してみましょう。



OGRE3Dのアーキテクチャ

依存関係グラフは、OGRE3Dプロジェクト間の関係を示しています。



アーキテクチャはプラグイン指向であり、カーネルプロジェクト「OgreMain」に変更を加えることなくOgre3Dを拡張するのに非常に便利です。このアーキテクチャをサポートするために、OgreMainプロジェクトはプラグインと通信するために必要なクラスを提供しています。

カーネルプロジェクトがプラグインとどのように通信するかを見てみましょう。次のクエリでRenderSystem_GLプラグインが使用するOgreMainクラスを検索します。

SELECT TYPES WHERE IsDirectlyUsedBy "RenderSystem_GL"


プラグインは、いくつかのユーティリティクラスとモデルエンティティを使用し、いくつかの抽象クラスをオーバーライドして、Ogre3Dエコシステムに統合されます。

以下の結果をフィルタリングして、RenderSystem_GLプラグインが使用する抽象クラスのみを検索してみましょう。

SELECT TYPES WHERE IsDirectlyUsedBy "RenderSystem_GL" AND IsAbstract



プラグインは、シーン、テクスチャ、レンダリング動作などをオーバーライドできます。Ogre3Dは、レンダリングを簡単にカスタマイズできる興味深いAPIを提供しており、ニーズに合わせて適応させるのが非常に容易です。

継承とポリモーフィズム

オブジェクト指向アプローチを使用すると、ポリモーフィズムを最大限に活用しようとして継承を使いすぎることがあります。

これはOgre3Dフレームワークのカーネルを表すOgreMainで特に顕著で、多くのクラスがプラグインプロジェクトによってオーバーライドされるように設計されています。





しかし、多重継承は複雑さを増大させるため、慎重に使用する必要があります。多くの基底クラスを持つクラスを検索してみましょう。





複数のクラスから派生しているクラスはごくわずかです。

抽象度

プラグイン指向のアーキテクチャでは、ホストはより柔軟で拡張可能にするために多くの抽象クラスを持つ必要があります。

OgreMainの抽象クラスを検索してみましょう。





名前空間のレイヤリング

CppDependはDSMグラフを提供しており、この行列を三角化して、依存関係の循環を強調する赤い境界に焦点を当てることができます。



Ogre、Ogre::EmitterCommands、Ogre::OverlayElementCommandsの間には依存関係の循環が存在します。この依存関係を持つことは必ずしも問題ではありませんが、この種の依存関係を避けることは疎結合を強制します。この興味深い 記事 はレイヤリングの利点を説明しています。

Ogreと他の2つの名前空間間の依存関係の起源を検索してみましょう。





Ogre名前空間は、他の名前空間のすべてのCmdクラスを使用しており、それらのすべてのクラスはOgre::ParticleEmitterによってstaticフィールドとして使用されています。ParticleEmitterはそれらをCmdParam辞書に追加します。

依存関係の循環を避けるために別の方法で実装することが可能かもしれませんが、これは大きな問題ではありません。

型の凝集度

単一責任の原則は、クラスは変更する理由を1つだけ持つべきであると述べています。そのようなクラスは凝集性が高いと言われます。高いLCOM値は一般に、凝集性の低いクラスを示します。いくつかのLCOMメトリクスがあります。LCOMは[0-1]の範囲の値を取ります。LCOMHS(HSはHenderson-Sellersの略)は[0-2]の範囲の値を取ります。LCOMHSメトリクスは、凝集性のない型を検出するのにより効率的であるとよく考えられていることに注意してください。1を超えるLCOMHS値は警戒すべきと考えるべきです。





凝集性が低いと見なされるクラスはごくわずかです。

使用されているデザインパターン

シングルトン

クラスが1つのインスタンスのみを持つことを保証するには、シングルトンパターンを使用するのが最善の方法です。

シングルトンであるクラスを検索してみましょう。

SELECT TYPES WHERE DeriveFrom "Ogre.Singleton"





ご覧のとおり、ほぼすべてのマネージャークラスがシングルトンです。一般に、マネージャーはシングルトンの良い候補です。そして、Singletonから派生していないマネージャークラスを検索できます。





Singletonから派生していないマネージャーはごくわずかです。それらのクラスは何度もインスタンス化できるため、これは正常です。

ファクトリ

ファクトリパターンは、オブジェクトの作成を抽象化するのに非常に便利です。低結合と高凝集を強制します。この 記事で説明されています。



しかし、ファクトリを持っているだけでは、インスタンスがそれを通じて作成されることは保証されません。クラスは依然として直接インスタンス化できるからです。したがって、インスタンス化にファクトリが使用されることを保証するルールを定義する必要があります。

例えば、Entityクラスについて、それをインスタンス化する各クラスを発見するために次のルールを定義できます。





ご覧のとおり、EntityFactoryのみがEntityクラスをインスタンス化しています。

マネージャー

マネージャークラスはサブシステムへのアクセスを提供し、プロジェクトをモジュール化するのに非常に便利です。Ogre3Dには多くのマネージャーがあり、それぞれが異なるサブシステムを表しています。

SELECT TYPES WHERE NameLike "Manager$"





ファサード

ファサードは、サブシステムを使いやすくする高レベルのインターフェースを定義します。エファレント結合メトリクスを使用して、プロジェクト内のファサードを検出できます。特定の型のエファレント結合は、それが直接依存している型の数です。TypeCe > 50の型は、あまりにも多くの他の型に依存しています。それらは複雑で、複数の責務を持っています。リファクタリングの良い候補です。

ただし、ファサードでは高いTypeCeが正常な場合があります。

高いTypeCeを持つクラスを検索してみましょう。





これらのクラスのすべてがファサードとは限らず、それぞれの高いTypeCeには正当な説明があるかもしれません。一部はおそらくリファクタリングできるでしょうが、確実に言えるほどOgre3Dをよく知りません。

最も広く使用されているファサードはRootクラスです。それがどのサブシステムを使用しているか見てみましょう。



ご覧のとおり、Rootクラスはほぼすべてのマネージャークラスを使用しています。

次のCQLクエリで、Rootを通じてアクセスできないマネージャーを検索することもできます。

SELECT TYPES WHERE !IsDirectlyUsedBy "Ogre.Root" AND NameLike "Manager$" AND !IsAbstract





オブザーバー

オブザーバーは、サブジェクトと呼ばれるオブジェクトが、オブザーバーと呼ばれる依存オブジェクトのリストを保持し、通常はそれらのメソッドの1つを呼び出すことで、状態の変化を自動的に通知するパターンです。主にイベント処理システムの実装に使用されます。

Ogre3Dは、Listenerクラスを使用してオブザーバーパターンを実装しています。

OgreMainプロジェクトのListenerクラスを検索してみましょう。





結論

Ogre3Dは、非常にクリーンで、適切に設計され、十分に文書化されたフレームワークです。使用されているデザインパターンの目的を簡単に理解でき、そのモジュール性は機能をより迅速に学ぶのに役立ちます。

Share this article